\n \u003Cinput type=\"text\" name=\"superpower\" />\n \u003Cbutton type=\"submit\" is=\"ajax-submit\" data-target=\"hero-container\">\n add new item\n \u003C/button>\n\u003C/form>\n\n\u003Cdiv id=\"hero-container\">\u003C/div>\n","html",[471,15043,15044,15071,15098,15119,15150,15155,15164,15173,15177],{"__ignoreMap":469},[474,15045,15046,15049,15052,15055,15057,15060,15063,15065,15068],{"class":476,"line":477},[474,15047,15048],{"class":503},"\u003C",[474,15050,14954],{"class":15051},"s9eBZ",[474,15053,15054],{"class":480}," action",[474,15056,811],{"class":503},[474,15058,15059],{"class":484},"\"/heroes\"",[474,15061,15062],{"class":480}," method",[474,15064,811],{"class":503},[474,15066,15067],{"class":484},"\"post\"",[474,15069,15070],{"class":503},">\n",[474,15072,15073,15076,15079,15082,15084,15087,15090,15092,15095],{"class":476,"line":507},[474,15074,15075],{"class":503}," \u003C",[474,15077,15078],{"class":15051},"input",[474,15080,15081],{"class":480}," type",[474,15083,811],{"class":503},[474,15085,15086],{"class":484},"\"text\"",[474,15088,15089],{"class":480}," name",[474,15091,811],{"class":503},[474,15093,15094],{"class":484},"\"hero\"",[474,15096,15097],{"class":503}," />\n",[474,15099,15100,15102,15104,15106,15108,15110,15112,15114,15117],{"class":476,"line":547},[474,15101,15075],{"class":503},[474,15103,15078],{"class":15051},[474,15105,15081],{"class":480},[474,15107,811],{"class":503},[474,15109,15086],{"class":484},[474,15111,15089],{"class":480},[474,15113,811],{"class":503},[474,15115,15116],{"class":484},"\"superpower\"",[474,15118,15097],{"class":503},[474,15120,15121,15123,15125,15127,15129,15132,15135,15137,15140,15143,15145,15148],{"class":476,"line":584},[474,15122,15075],{"class":503},[474,15124,15031],{"class":15051},[474,15126,15081],{"class":480},[474,15128,811],{"class":503},[474,15130,15131],{"class":484},"\"submit\"",[474,15133,15134],{"class":480}," is",[474,15136,811],{"class":503},[474,15138,15139],{"class":484},"\"ajax-submit\"",[474,15141,15142],{"class":480}," data-target",[474,15144,811],{"class":503},[474,15146,15147],{"class":484},"\"hero-container\"",[474,15149,15070],{"class":503},[474,15151,15152],{"class":476,"line":607},[474,15153,15154],{"class":503}," add new item\n",[474,15156,15157,15160,15162],{"class":476,"line":642},[474,15158,15159],{"class":503}," \u003C/",[474,15161,15031],{"class":15051},[474,15163,15070],{"class":503},[474,15165,15166,15169,15171],{"class":476,"line":663},[474,15167,15168],{"class":503},"\u003C/",[474,15170,14954],{"class":15051},[474,15172,15070],{"class":503},[474,15174,15175],{"class":476,"line":694},[474,15176,917],{"emptyLinePlaceholder":916},[474,15178,15179,15181,15183,15186,15188,15190,15193,15195],{"class":476,"line":700},[474,15180,15048],{"class":503},[474,15182,15027],{"class":15051},[474,15184,15185],{"class":480}," id",[474,15187,811],{"class":503},[474,15189,15147],{"class":484},[474,15191,15192],{"class":503},">\u003C/",[474,15194,15027],{"class":15051},[474,15196,15070],{"class":503},[464,15198,15202],{"className":15199,"code":15200,"language":15201,"meta":469,"style":469},"language-javascript shiki shiki-themes github-light github-dark","class AjaxSubmitButton extends HTMLButtonElement {\n connectedCallback() {\n this.addEventListener(\"click\", async (event) => {\n event.preventDefault();\n let html = await ajaxFormSubmit();\n document.getElementById(this.dataset.target).innerHTML = html;\n });\n }\n}\n\ncustomElements.define(\"ajax-submit\", AjaxSubmitButton, {\n extends: \"button\",\n});\n","javascript",[471,15203,15204,15220,15228,15261,15272,15290,15310,15315,15320,15324,15328,15343,15353],{"__ignoreMap":469},[474,15205,15206,15209,15212,15215,15218],{"class":476,"line":477},[474,15207,15208],{"class":810},"class",[474,15210,15211],{"class":480}," AjaxSubmitButton",[474,15213,15214],{"class":810}," extends",[474,15216,15217],{"class":480}," HTMLButtonElement",[474,15219,14027],{"class":503},[474,15221,15222,15225],{"class":476,"line":507},[474,15223,15224],{"class":480}," connectedCallback",[474,15226,15227],{"class":503},"() {\n",[474,15229,15230,15233,15235,15238,15240,15243,15245,15248,15251,15254,15256,15259],{"class":476,"line":547},[474,15231,15232],{"class":510}," this",[474,15234,1402],{"class":503},[474,15236,15237],{"class":480},"addEventListener",[474,15239,1483],{"class":503},[474,15241,15242],{"class":484},"\"click\"",[474,15244,520],{"class":503},[474,15246,15247],{"class":810},"async",[474,15249,15250],{"class":503}," (",[474,15252,15253],{"class":14037},"event",[474,15255,2077],{"class":503},[474,15257,15258],{"class":810},"=>",[474,15260,14027],{"class":503},[474,15262,15263,15266,15269],{"class":476,"line":584},[474,15264,15265],{"class":503}," event.",[474,15267,15268],{"class":480},"preventDefault",[474,15270,15271],{"class":503},"();\n",[474,15273,15274,15277,15280,15282,15285,15288],{"class":476,"line":607},[474,15275,15276],{"class":810}," let",[474,15278,15279],{"class":503}," html ",[474,15281,811],{"class":810},[474,15283,15284],{"class":810}," await",[474,15286,15287],{"class":480}," ajaxFormSubmit",[474,15289,15271],{"class":503},[474,15291,15292,15295,15298,15300,15302,15305,15307],{"class":476,"line":642},[474,15293,15294],{"class":503}," document.",[474,15296,15297],{"class":480},"getElementById",[474,15299,1483],{"class":503},[474,15301,8394],{"class":510},[474,15303,15304],{"class":503},".dataset.target).innerHTML ",[474,15306,811],{"class":810},[474,15308,15309],{"class":503}," html;\n",[474,15311,15312],{"class":476,"line":663},[474,15313,15314],{"class":503}," });\n",[474,15316,15317],{"class":476,"line":694},[474,15318,15319],{"class":503}," }\n",[474,15321,15322],{"class":476,"line":700},[474,15323,703],{"class":503},[474,15325,15326],{"class":476,"line":913},[474,15327,917],{"emptyLinePlaceholder":916},[474,15329,15330,15333,15336,15338,15340],{"class":476,"line":920},[474,15331,15332],{"class":503},"customElements.",[474,15334,15335],{"class":480},"define",[474,15337,1483],{"class":503},[474,15339,15139],{"class":484},[474,15341,15342],{"class":503},", AjaxSubmitButton, {\n",[474,15344,15345,15348,15351],{"class":476,"line":926},[474,15346,15347],{"class":503}," extends: ",[474,15349,15350],{"class":484},"\"button\"",[474,15352,4715],{"class":503},[474,15354,15355],{"class":476,"line":932},[474,15356,15357],{"class":503},"});\n",[439,15359,15360,15361,15364],{},"Der Vorteil des Custom-Elements gegenüber jQuery (oder sonstiger eigener Implementierung) ist, dass der Browser sich\ndarum kümmert, unser JavaScript mit dem HTML zu verbinden und eine Instanz des ",[471,15362,15363],{},"AjaxSubmitButtons"," zu erstellen.",[439,15366,15367,15368,15371,15372,15377],{},"Kommen zur Laufzeit weitere ",[471,15369,15370],{},"submit"," Buttons mit diesem Attribut hinzu, werden sie vom Browser automatisch mit unserem\ngewünschten Verhalten erweitert. Ist das JavaScript aus irgendwelchen Gründen nicht verfügbar funktioniert weiterhin das\ngute alte HTML Formular mit komplettem Seitenreload. Wir verbessern unsere Anwendung mit jedem weiteren\nTechnologie-Layer, der zur Verfügung\nsteht, ",[1002,15373,15376],{"href":15374,"rel":15375},"https://developer.mozilla.org/de/docs/Glossary/Progressive_Enhancement",[1006],"Progressive Enhancement"," genannt. HTML\nbeschreibt den Inhalt, CSS macht es bunt, und zu guter Letzt verbessern wir die Benutzererfahrung mit JavaScript.",[439,15379,15380],{},"Das Backend wird mit diesem Ansatz auch nicht komplizierter oder gar komplexer. Im Controller müssen wir erkennen, dass\nes sich um einen Ajax Request handelt. In dem Fall wird ein HTML Fragment gerendert, andernfalls wird die komplette\nSeite gerendert:",[464,15382,15384],{"className":709,"code":15383,"language":711,"meta":469,"style":469},"@PostMapping(value = \"/heroes\")\npublic String addSuperhero(\n @RequestParam String hero,\n @RequestParam String superpower,\n @RequestHeader(name = \"X-Requested-With\", defaultValue = \"\") String requestedWith,\n Model model\n ) {\n\n model.addAttribute(\"hero\", hero);\n model.addAttribute(\"superpower\", superpower);\n\n if (\"ajax\".equals(requestedWith)) {\n return \"fragments/hero-fragment :: hero-fragment\";\n }\n\n return \"full-page-including-the-hero-fragment\";\n}\n\n",[471,15385,15386,15391,15396,15401,15406,15411,15416,15421,15425,15430,15435,15439,15444,15449,15453,15457,15462],{"__ignoreMap":469},[474,15387,15388],{"class":476,"line":477},[474,15389,15390],{},"@PostMapping(value = \"/heroes\")\n",[474,15392,15393],{"class":476,"line":507},[474,15394,15395],{},"public String addSuperhero(\n",[474,15397,15398],{"class":476,"line":547},[474,15399,15400],{}," @RequestParam String hero,\n",[474,15402,15403],{"class":476,"line":584},[474,15404,15405],{}," @RequestParam String superpower,\n",[474,15407,15408],{"class":476,"line":607},[474,15409,15410],{}," @RequestHeader(name = \"X-Requested-With\", defaultValue = \"\") String requestedWith,\n",[474,15412,15413],{"class":476,"line":642},[474,15414,15415],{}," Model model\n",[474,15417,15418],{"class":476,"line":663},[474,15419,15420],{}," ) {\n",[474,15422,15423],{"class":476,"line":694},[474,15424,917],{"emptyLinePlaceholder":916},[474,15426,15427],{"class":476,"line":700},[474,15428,15429],{}," model.addAttribute(\"hero\", hero);\n",[474,15431,15432],{"class":476,"line":913},[474,15433,15434],{}," model.addAttribute(\"superpower\", superpower);\n",[474,15436,15437],{"class":476,"line":920},[474,15438,917],{"emptyLinePlaceholder":916},[474,15440,15441],{"class":476,"line":926},[474,15442,15443],{}," if (\"ajax\".equals(requestedWith)) {\n",[474,15445,15446],{"class":476,"line":932},[474,15447,15448],{}," return \"fragments/hero-fragment :: hero-fragment\";\n",[474,15450,15451],{"class":476,"line":938},[474,15452,1276],{},[474,15454,15455],{"class":476,"line":944},[474,15456,917],{"emptyLinePlaceholder":916},[474,15458,15459],{"class":476,"line":950},[474,15460,15461],{}," return \"full-page-including-the-hero-fragment\";\n",[474,15463,15464],{"class":476,"line":956},[474,15465,703],{},[3938,15467,15469],{"id":15468},"auf-dem-weg-zur-single-page-application","Auf dem Weg zur Single Page Application",[439,15471,15472],{},"Mittlerweile haben wir die ajax-submit Komponente in unserer Anwendung an sehr vielen Stellen im Einsatz. Bisher hat\nsie sich als robust und einfach bewährt. Jeder im Team hat verstanden wie man es einsetzt und wie es funktioniert. Wir\nbrauchen keine Runtime a la ReactJS, wir müssen Hooks nicht verstehen, wir benötigen kein komplexes Build Setup. Wir\nmüssen nicht überlegen wie wir vue Komponenten integrieren und wir müssen uns keine Gedanken bzgl Client Side only\nFunktionalität machen.",[439,15474,15475],{},"Kurz gesagt: Wir bauen unser Frontend ohne modernes JavaScript Framework und sind (trotzdem) glücklich.",[439,15477,15478],{},"Zugegeben, die Progressive Enhancement Denkweise ist herausfordernd. Daran zu denken und zu überlegen, wie Anforderungen\nohne JavaScript gelöst werden können, hat unsere Industrie vielleicht verlernt? In der Vergangenheit haben mich\nKollegen, die überwiegend im Backend unterwegs sind gefragt, wie man denn heutzutage ein Frontend baut. Nehme man da\nAngular oder React? Über Anforderungen war man sich da aber noch nicht bewusst… Es war zumindest nicht die Einleitung\nzur Technologie Frage.",[1065,15480,15482],{"id":15481},"herausforderungen-die-kommen-könnten","Herausforderungen die kommen (könnten)",[994,15484,15485],{},[997,15486,15487],{},[448,15488,15489],{},"Progressive Enhancement Denkweise",[439,15491,15492],{},"Sobald unsere Anwendung einen gewissen Charakter moderner Single Page Applications erreicht hat, wird das natürlich der\nDefault werden (im Geiste). Hier gilt es weiterhin an die Basis zu denken und nicht sofort an JavaScript only Lösungen.",[994,15494,15495],{},[997,15496,15497],{},[448,15498,15499],{},"History Handling",[439,15501,15502,15503,15508],{},"Vor und Zurück nach bestimmten Aktionen ist bisher keine Anforderung. Sollte diese Anforderung kommen ist das aber auch\nkein Hexenwerk. Nach JavaScript Aktionen die URL mit\nder ",[1002,15504,15507],{"href":15505,"rel":15506},"https://developer.mozilla.org/en-US/docs/Web/API/History",[1006],"history API"," ändern ist im Bereich des Möglichen 😉",[994,15510,15511],{},[997,15512,15513],{},[448,15514,15515],{},"State",[439,15517,15518,15519,15524],{},"Nach bestimmten Aktionen den State an mehreren Stellen im Browser aktualisieren. Hier muss denke ich abewägt werden, ob\nBibiliotheken eine Daseinsberechtigung bekommen, oder\nob ",[1002,15520,15523],{"href":15521,"rel":15522},"https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent",[1006],"Custom Events"," ausreichen.",[1024,15526,15527],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":469,"searchDepth":507,"depth":507,"links":15529},[15530,15531,15532,15533],{"id":14888,"depth":507,"text":14889},{"id":14931,"depth":507,"text":14932},{"id":15004,"depth":507,"text":14995},{"id":15468,"depth":507,"text":15469,"children":15534},[15535],{"id":15481,"depth":547,"text":15482},[1030,1031],"2020-06-23T11:20:03","Mit einigen Jahren JavaScript und Reactjs Erfahrung durfte ich Ende letzten Jahres (November 2019) Teil eines neuen\\nTeams und eines neuen Projektes werden. Das Projekt ist ein Traumprojekt jeden Entwicklers. Ein grüne Wiese Projekt mit\\n“freier” Technologiewahl. “Frei” in Form von man darf sich die Zeit für eine Risikoanalyse nehmen und moderne Tools und\\nFrameworks evaluieren.","https://synyx.de/blog/frameworkless-frontend-und-trotzdem-gluecklich/",{},"/blog/frameworkless-frontend-und-trotzdem-gluecklich",{"title":14827,"description":14836},"frameworkless-frontend-und-trotzdem-gluecklich","blog/frameworkless-frontend-und-trotzdem-gluecklich",[15546,15201,15547],"development","progressive-enhancement","Mit einigen Jahren JavaScript und Reactjs Erfahrung durfte ich Ende letzten Jahres (November 2019) Teil eines neuen Teams und eines neuen Projektes werden. Das Projekt ist ein Traumprojekt jeden Entwicklers. Ein grüne Wiese Projekt mit 'freier' Technologiewahl. 'Frei' in Form von man darf sich die Zeit für eine Risikoanalyse nehmen und moderne Tools und Frameworks evaluieren.","JGt7bZJ_gokf64kVf7XrDHMaEJ2JdAYkb3HTHT7LZEw",{"id":15551,"title":15552,"author":15553,"body":15554,"category":15634,"date":15635,"description":15636,"extension":1034,"link":14746,"meta":15637,"navigation":916,"path":15638,"seo":15639,"slug":15558,"stem":15641,"tags":15642,"teaser":15645,"__hash__":15646},"blog/blog/bewerbung-und-schnuppertag-in-zeiten-von-social-distancing.md","Bewerbung und Schnuppertag in Zeiten von Social Distancing",[303,91],{"type":432,"value":15555,"toc":15632},[15556,15559,15567,15570,15573,15584,15587,15590,15593,15596,15599,15604,15609,15612,15615,15618,15621,15624,15627],[435,15557,15552],{"id":15558},"bewerbung-und-schnuppertag-in-zeiten-von-social-distancing",[439,15560,15561,15562,15566],{},"Grundsätzlich ändert sich am Ablaufprozedere wenig, Du sendest Deine Bewerbung per Mail\nan ",[1002,15563,15565],{"href":15564},"mailto:jobs@synyx.de","jobs@synyx.de",". Bei der Durchsicht achten wir mehr auf Deine Projekte, sowie Deine technischen\nund methodischen Skills, als auf Zeugnisse. Deshalb ist eine Projektliste sehr hilfreich, in der Du uns Arbeitsweise,\nDauer und eingesetzte Technologien skizzierst.",[439,15568,15569],{},"Im Team wird Deine Bewerbung besprochen und wenn wir eine passende Stelle für Dich haben, erhältst Du eine Einladung zum\nBewerbungsgespräch per Video-Call.",[439,15571,15572],{},"So weit so gut. Sei ganz Du selbst, das ist die erste Gelegenheit uns persönlich kennenzulernen. Wir möchten mehr über\nDich und Deine Erfahrungen, Deine weiteren Ziele erfahren. Das ist auch die Chance für Dich, Deine Fragen loszuwerden.",[439,15574,15575,15576,8764,15580,15583],{},"Da wir derzeit im Homeoffice arbeiten fällt leider der Gang durch unser Büro aus, aber lass Dir versichert sein, es ist\nalles da, was das Herz begehrt. Dazu kannst Du Dich auch gerne ",[1002,15577,8234],{"href":15578,"rel":15579},"https://synyx.de/dein-arbeitsplatz/",[1006],[1002,15581,8234],{"href":8256,"rel":15582},[1006]," informieren.",[439,15585,15586],{},"Wenn wir auf einer Wellenlänge sind, laden wir Dich zu einem virtuellen Schnuppertag ein. Warum wir das tun? Wir sind\nkeine Freunde von Hire-and-Fire, deshalb ist uns wichtig, dass unsere Zusammenarbeit langfristig funktioniert. Für\nDich bietet der Schnuppertag die Chance, Dir einen ganzheitlichen Eindruck von Deinen potenziellen neuen Kollegen und\nunsere Arbeitsweise hier bei synyx zu machen.",[439,15588,15589],{},"Um Dir das im Rahmen der Gegebenheiten zu ermöglichen, haben wir uns ein Konzept für einen gelungenen remote\nSchnuppertag überlegt.",[439,15591,15592],{},"Bevor es überhaupt losgeht, werden wir gemeinsam vorab einen Techniktest durchführen. So können wir sicher sein, dass\nuns keine technischen Probleme den Spaß verderben.",[439,15594,15595],{},"Von der Begrüßung bis zur Verabschiedung wird ein Mentor an Deiner Seite sein und Dich virtuell begleiten, Dir Antworten\nliefern und dafür sorgen, dass Du dich wohlfühlst.",[439,15597,15598],{},"Damit Du möglichst viele verschiedene Teams und die unterschiedlichen Themenfelder kennenlernst, ist der Tag in mehrere\nZeitblöcke unterteilt. Wie kann das aussehen?",[439,15600,15601],{},[2205,15602],{"alt":469,"src":15603},"https://media.synyx.de/uploads/2020/06/Tagesablauf1-768x706.jpg",[439,15605,15606],{},[2205,15607],{"alt":469,"src":15608},"https://media.synyx.de/uploads/2020/03/White.jpg",[439,15610,15611],{},"Du darfst bei uns direkt in reale Aufgaben rein schnuppern. Wie an anderen Tagen auch, kann es zu einem Supportfall oder\nanderen Anfragen kommen, da erlebst Du die Teams dann gleich richtig in Action.",[439,15613,15614],{},"Wir wissen, wie anstrengend ein Schnuppertag schon in Reallife sein kann und wollen Dir natürlich genug Möglichkeiten\nzur Erholung geben, deshalb sind regelmäßige Pausen sehr wichtig. Außerdem wirst Du in unserer virtuellen Kaffee-Ecke\nauch andere Kollegen treffen und Dich austauschen können.",[439,15616,15617],{},"Am Ende des Tages führt Dein Mentor oder einer unserer Gründer noch ein Abschlussgespräch mit Dir und entlässt Dich in\nden Feierabend.",[439,15619,15620],{},"Unser Bewerbungsprozess geht an dieser Stelle wie gehabt weiter, jeder der Kollegen, der die Gelegenheit hatte, Dich an\nDeinem Schnuppertag kennenzulernen ist angehalten, seinen Eindruck zu schildern.",[439,15622,15623],{},"So können wir für unsere Seite differenziert beurteilen wie gut Du zu synyx passt.",[439,15625,15626],{},"Du bist selbstverständlich auch angehalten, Dir Gedanken zu machen und uns gerne Dein Feedback zum Schnuppertag zu\ngeben. Im besten Fall bist Du genauso begeistert von uns, wie wir von Dir 😉",[439,15628,15629,15630],{},"Hast Du Lust bekommen, uns kennenzulernen? Dann schreib uns an ",[1002,15631,15565],{"href":15564},{"title":469,"searchDepth":507,"depth":507,"links":15633},[],[1031],"2020-06-03T16:18:53","Grundsätzlich ändert sich am Ablaufprozedere wenig, Du sendest Deine Bewerbung per Mail\\nan jobs@synyx.de. Bei der Durchsicht achten wir mehr auf Deine Projekte, sowie Deine technischen\\nund methodischen Skills, als auf Zeugnisse. Deshalb ist eine Projektliste sehr hilfreich, in der Du uns Arbeitsweise,\\nDauer und eingesetzte Technologien skizzierst.",{},"/blog/bewerbung-und-schnuppertag-in-zeiten-von-social-distancing",{"title":15552,"description":15640},"Grundsätzlich ändert sich am Ablaufprozedere wenig, Du sendest Deine Bewerbung per Mail\nan jobs@synyx.de. Bei der Durchsicht achten wir mehr auf Deine Projekte, sowie Deine technischen\nund methodischen Skills, als auf Zeugnisse. Deshalb ist eine Projektliste sehr hilfreich, in der Du uns Arbeitsweise,\nDauer und eingesetzte Technologien skizzierst.","blog/bewerbung-und-schnuppertag-in-zeiten-von-social-distancing",[15643,12105,9458,15644],"bewerbungsprozess","social-distancing","Du möchtest Dich bei synyx bewerben und fragst Dich, wie der Bewerbungsprozess abläuft, während Social Distancing angesagt ist? Dann ist dieser Artikel für Dich.","cPbKPgaeMLDIWkklhYyJNT-fc5SkYHijjjIkIA1o_xs",{"id":15648,"title":15649,"author":15650,"body":15651,"category":15724,"date":15725,"description":15726,"extension":1034,"link":11876,"meta":15727,"navigation":916,"path":15728,"seo":15729,"slug":15655,"stem":15730,"tags":15731,"teaser":15733,"__hash__":15734},"blog/blog/welt-passwort-tag.md","Welt-Passwort-Tag",[94],{"type":432,"value":15652,"toc":15719},[15653,15656,15659,15662,15665,15668,15672,15675,15678,15681,15684,15688,15691,15694,15697,15700,15703,15707,15710,15713,15716],[435,15654,15649],{"id":15655},"welt-passwort-tag",[439,15657,15658],{},"Passwörter sind überall. Sie schützen alle möglichen Aspekte unseres täglichen Lebens. Trotzdem denken wir viel zu wenig\nüber sie nach. Wir wollen uns den World Password Day zum Anlass nehmen, uns Passwörter, ihre Sicherheit und die Theorie\ndahinter etwas genauer anzuschauen.",[439,15660,15661],{},"Fangen wir mit einem Gedankenexperiment an, um uns klar zu machen, wie wichtig Passwörter und der sichere Umgang mit\nihnen ist. Stellen wir uns mal vor alle unsere Passwörter wären plötzlich nicht mehr geheim. Das Onlinebanking –\nungeschützt. Social Media Accounts – in Sekunden übernommen. Jede geschriebene Nachricht, jedes geschickte Bild –\nplötzlich öffentlich. Und das ist erst die Spitze des Eisbergs. Mittlerweile gibt es kaum noch etwas, das sich nicht\nonline verwalten lässt. Strom, Gas, Versicherungen, Arzttermine, Handyverträge, Fitness-Tracking – alles online und oft\nnur geschützt durch ein Passwort. Für die meisten von uns wäre es ein absolutes Desaster, wenn Unbekannte plötzlich\nZugriff auf all das hätten.",[439,15663,15664],{},"Die erste Frage, die den meisten beim Thema Passwörter durch den Kopf geht, lautet daher meist “Sind meine Passwörter\neigentlich sicher?”. Die Antwort darauf: “Kommt drauf an”. Das erste und wichtigste Kriterium für ein sicheres Passwort\nhaben wir gerade gesehen: Es muss geheim sein. Ein Passwort kann noch so lang und komplex sein, wenn es nicht geheim\nbleibt, ist es unsicher. Doch was erst mal trivial klingt, wird erstaunlich oft zum Problem.",[439,15666,15667],{},"Und damit ist nicht der unscheinbare Klebezettel am Monitor als “Merkhilfe” gemeint. Tatsächlich ist den meisten\nmittlerweile klar, dass sie Passwörter nicht rumerzählen oder so aufschreiben sollten, dass andere sie finden können.\nDoch ihr seid nicht die Einzigen, die eure Passwörter kennen – die Seiten, bei denen ihr euch damit anmelden wollt,\nmüssen sie ebenfalls kennen.Wenn diese Seiten nicht sorgsam mit euren Passwörtern umgehen, können sie schnell in die\nfalschen Hände gelangen. Problematisch wird das ganze vor allem dann, wenn dasselbe Passwort auch für andere Dienste\nverwendet wurde. Bei sogenannten Credential Stuffing Angriffen probieren große Mengen von Zugangsdaten, die von\ngehackten Diensten geleakt wurden, auf anderen Plattformen aus. Und das oft mit Erfolg, denn das Wiederverwenden von\nPasswörtern ist leider eine verbreitete Praxis. Wenn ihr euch also nicht sicher seid, ob ein Dienst sicher mit euren\nPasswörtern hantiert – Spoiler: Sicher könnt ihr da nie sein – solltet ihr für jeden Dienst ein eigenes Passwort\nverwenden.",[3938,15669,15671],{"id":15670},"doch-wie-sollte-dieses-passwort-aussehen","Doch wie sollte dieses Passwort aussehen?",[439,15673,15674],{},"Wer in den letzten zehn Jahren nicht gerade unter einem Stein geschlafen hat, dürfte diese Regeln schon mal gehört\nhaben: Ein Passwort muss auf jeden Fall lang sein. Und es soll Groß- und Kleinbuchstaben enthalten. Und Zahlen. Und am\nbesten auch noch Sonderzeichen. Ein Passwort, welches nach diesen Regeln erstellt wurde, sollte von den meisten\nWebseiten akzeptiert werden. Aber ist es deswegen auch sicher? Tatsächlich sind diese etablierten Regeln für die\nZusammensetzung sicherer Passwörter definitiv nicht über alle Zweifel erhaben. Nehmen wir mal das Passwort\n“Zwiebelkuchen1!”. Es dürfte mit 15 Zeichen definitiv zu den längeren Passwörtern zählen, enthält Groß- und\nKleinbuchstaben, Zahlen und Sonderzeichen, erfüllt also alle üblichen Kriterien. Aber ist “Zwiebelkuchen1!” deswegen\nschon sicher?",[439,15676,15677],{},"Um diese Fragen zu beantworten, lohnt es sich, einen Blick darauf zu werfen, wie Angreifer vorgehen, um Passwörter zu\nknacken. Haben wir uns die Regel der Geheimhaltung zu Herzen genommen, für jeden Dienst ein eigenes Passwort verwendet\nund dieses geheim gehalten, bleibt einem Angreifer nichts anderes übrig, als zu raten. Die einfachste Methode ist dabei,\nalle möglichen Passwörter auszuprobieren – der sogenannte Brute Force Angriff. Und genau hier kommen unsere etablierten\nKriterien ins Spiel. Die Kriterien zielen darauf ab, die Menge der möglichen Passwörter möglichst groß zu machen, damit\nder Angreifer möglichst lang braucht, um sie durchzuprobieren. Nehmen wir als Beispiel mal ein Zahlenschloss. Ein\neinfaches Zahlenschloss hat drei Ziffern mit jeweils 10 möglichen Zeichen (die Zahlen von 0 bis 9) und damit 10 *\n10 * 10 = 1.000 mögliche Kombinationen. Braucht ein Angreifer pro Kombination nur eine Sekunde, kann er in nicht mal\n17 Minuten alle möglichen Kombinationen durchprobieren und das Schloss öffnen.",[439,15679,15680],{},"Nehmen wir stattdessen nun ein Zahlenschloss mit vier Ziffern, gibt es 10 * 10 * 10 * 10 = 10.000 verschiedene\nMöglichkeiten und der Angreifer braucht schon fast drei Stunden. Die Länge des Passworts spielt also eine wichtige\nRolle. Noch wichtiger als die Länge ist allerdings die Zusammensetzung. Hätte das Schloss statt drei Ziffern nämlich\ndrei Buchstaben ergeben sich bei einem Alphabet von 26 Zeichen bereits 26 * 26 * 26 = 17.576 mögliche Kombinationen.\nNehmen wir Zahlen, Groß- und Kleinschreibung, sowie die üblichsten Sonderzeichen dazu erhalten wir ein Alphabet von 94\nmöglichen Zeichen und damit 94 * 94 * 94 = 830.584 mögliche Kombinationen. Dafür bräuchte unser Angreifer schon über\nneun Tage. Unser Passwort “Zwiebelkuchen1!” ist sogar 15 Zeichen lang. Um alle Passwörter mit 15 Zeichen bestehend aus\nBuchstaben, Zahlen und Sonderzeichen durchzuprobieren bräuchte ein Angreifer, selbst wenn er 1.000.000 Passwörter in\nder Sekunde ausprobieren kann, immer n hoch 12.534.620.711.557.642 Jahre. Das Passwort ist also sicher – oder?",[439,15682,15683],{},"Was den Brute Force Angriff angeht, ist so ein Passwort definitiv als sicher anzusehen. Leider weiß auch unser\nAngreifer, dass er mit einfachem Durchprobieren wenig Erfolg haben wird. Oft ist das aber auch gar nicht nötig, denn\nviele Passwörter sind alles andere als zufällig gewählt. Schaut man sich die Liste der häufigsten Passwörter an, stellt\nman fest, dass es sich oft um normale Wörter handelt. Ein häufiges Vorgehen ist daher das Durchprobieren von Wörtern aus\ndem Wörterbuch oder von Listen häufiger Passwörter. Ist “Zwiebelkuchen1!” also sicher, weil es nicht im Wörterbuch\nvorkommt? Nicht wirklich. Zwar unterscheidet es sich in Zahlen und Sonderzeichen von einem Wort aus dem Wörterbuch, doch\ndiese sind nicht zufällig gewählt. Stellen wir uns mal vor, jemand hat sein Lieblingspasswort “Zwiebelkuchen” in eine\nPasswortmaske eingegeben und die Maske lehnt das Passwort ab, weil es weder Zahlen noch Sonderzeichen enthält. Was ist\nnun wahrscheinlicher: Dass die Person ein komplett neues Passwort erstellt, oder dass sie so lange Zeichen anhängt, bis\ndie Passwortmaske zufrieden ist? Tatsächlich ist das Anhängen kurzer Zahlen-Sonderzeichen Kombinationen an einfache\nWörter ein häufiges Vorgehen. Und das macht es auch für den Angreifer berechenbar. Denn alle Wörter aus dem Wörterbuch\nmit einer beliebigen Kombination aus einer Zahl und einem Sonderzeichen angehängt durchzuprobieren ist immer noch um ein\nVielfaches schneller als ein Brute Force Angriff. Und mit einem solchen Angriff dürfe “Zwiebelkuchen1!” leider schnell\ngefunden sein.",[3938,15685,15687],{"id":15686},"was-muss-ein-passwort-also-noch-erfüllen-um-sicher-zu-sein","Was muss ein Passwort also noch erfüllen, um sicher zu sein?",[439,15689,15690],{},"Die Kryptographie liefert dazu folgende Antwort: Um sicher zu sein, muss ein Passwort von echtem Zufall ununterscheidbar\nsein. Das Passwort “z$f6^Kj8” ist mit acht Zeichen deutlich kürzer als “Zwiebelkuchen1!”, trotzdem ist es definitiv das\nsicherere Passwort. Der Grund ist, dass es für den Angreifer keine Möglichkeit gibt, schneller auf dieses Passwort zu\nkommen als durch einfaches Ausprobieren. Und einfaches Ausprobieren ist, wenn die Kriterien für Länge und\nZusammensetzung berücksichtigt wurden, extrem ineffizient. Das Passwort hat aber einen entscheidenden Nachteil: Es ist\nalles andere als leicht zu merken. Besonders wenn man für jeden Dienst ein eigenes Passwort vergibt, können echt\nzufällige Passwörter sehr anstrengend sein.",[439,15692,15693],{},"Für dieses Problem existieren verschiedene Lösungen, von denen die meisten aber wieder neue Probleme mit sich bringen.\nEin Lösungsansatz nutzt aus, dass Menschen leichter in der Lage sind, sich Fantasieworte zu merken, wenn sie sie\naussprechen können. Das Problem daran: In Passwörtern, die sich einfach aussprechen lassen, kommen Buchstaben und\nBuchstabenfolgen mit einer ähnlichen Häufigkeit vor wie in der natürlichen Sprache. Diese Eigenschaft kann ein Angreifer\nnutzen, um alle Passwörter auszuschließen, die von dieser Häufigkeit abweichen. Das Resultat ist ein Angriff, der auf\nleicht aussprechbaren Passwörtern deutlich effizienter ist als der einfache Brute Force. Eine andere häufige Empfehlung\nsind Passphrasen, also Passwörter, die aus mehreren echten Wörtern zusammengesetzt sind, welche sich einfach merken\nlassen. Diese Passwörter enthalten zwar keine Zahlen oder Sonderzeichen, gleichen das allerdings durch eine deutlich\nhöhere Länge aus. Neben dem gleichen Problem wie bei leicht aussprechbaren Passwörtern können Angreifer hier einen\nweiteren Effekt nutzen. Man hat nämlich festgestellt, dass Menschen dazu neigen, bei Passphrasen grammatikalisch\nkorrekte Sätze zu formen. Auch dadurch lassen sich wieder etliche Kombinationen ausschließen und ein\nGeschwindigkeitsvorteil gegenüber dem Brute Force erzielen. Zusammengefasst lässt sich sagen: Alles, was Passwörter\nweniger zufällig macht, macht sie angreifbarer.",[439,15695,15696],{},"Es bleibt also dabei: Das sicherste Passwort ist individuell pro Dienst möglichst lang und komplex und echt zufällig –\nund damit leider schwer zu merken. Und genau das ist der Grund, warum immer wieder schwache Passwörter verwendet werden.\nDenn obwohl sich die meisten sehr wohl bewusst sind, wie man sichere Passwörter erstellt, ist es ihnen einfach zu\nanstrengend. Doch es gibt eine Lösung, die in den letzten Jahren immer mehr an Popularität gewinnt: Passwortmanager.\nPasswortmanager ermöglichen es, sich nur noch ein einziges Passwort merken zu müssen und damit ganz bequem und guten\nGewissens allen Regeln für sicheres Passwortmanagement folgen zu können.",[439,15698,15699],{},"Wir haben bei synyx auch unsere Erfahrungen damit sammeln können und das Ergebnis ist klar – wer einmal auf einen\nPasswortmanager gewechselt ist, will nicht mehr davon weg.",[439,15701,15702],{},"Daher ist unsere Empfehlung zum World Password Day: Macht euch das Leben und Gewissen leichter und wechselt auf einen\nPasswortmanager!",[3938,15704,15706],{"id":15705},"update-zum-ändere-dein-passwort-tag-2021-wie-wichtig-ist-es-eigentlich-passwörter-regelmäßig-zu-ändern","Update zum Ändere-Dein-Passwort-Tag 2021: Wie wichtig ist es eigentlich, Passwörter regelmäßig zu ändern?",[439,15708,15709],{},"Um diese Frage zu beantworten, sollte man sich erst mal überlegen, wovor das Ändern des Passworts eigentlich schützt.\nDas Ändern des Passwortes hilft nur dann, wenn jemand euer altes Passwort kannte. Wenn ihr zum Beispiel Aktionen in\neinem eurer Accounts seht, die nicht von euch kommen, wäre das ein klares Indiz dafür, dass jemand euer Passwort in die\nFinger bekommen hat. In solchen Fällen ist und bleibt es immer der sinnvollste erste Schritt, das eigene Passwort zu\nändern. Was aber, wenn ihr nicht gehackt wurdet?",[439,15711,15712],{},"Begeben wir uns mal in die Perspektive des Angreifers, der in euer Konto eindringen will. Der Angreifer kennt euer\nPasswort nicht und kann nur versuchen, euer Passwort",[439,15714,15715],{},"(möglichst effizient) zu raten. Alle Wege, die am Passwort vorbei führen, können wir hier erst mal ignorieren, denn die\nexistieren genau so, wenn ihr euer Passwort nicht ändert. Ist das Passwort bereits ein sicheres Passwort, ändert sich\nfür den Angreifer nichts. Er muss nach wie vor versuchen, euer Passwort rauszufinden, nur dass es nun ein anderes ist.\nNur wenn ihr zufällig genau den Moment trefft, in dem der Angreifer gerade Passwörter durchprobiert, kann es sein, dass\nseine Suche ohne Ergebnis bleibt und er von vorne beginnen muss. Um aus diesem Effekt aber einen Sicherheitsmehrwert zu\nziehen, müsstet ihr euer Passwort viel öfter als nur einmal im Jahr ändern. Tatsächlich existieren Systeme, die genau so\nvorgehen und deren Passwörter teilweise nur wenige Sekunden lang gültig sind – für Menschen ist so was natürlich schwer\nmöglich. Einen echten Mehrwert bringt das provisorische Ändern der privaten Passwörter also nicht.",[439,15717,15718],{},"Das gilt allerdings nur für Passwörter, die bereits sicher sind – also solche, die nur für einen Dienst verwendet und\nlang komplex und echt zufällig gewählt wurden. Erfüllt euer Passwort diese Kriterien nicht, könnt ihr eure Sicherheit\nsteigern, indem ihr es durch ein sicheres Passwort ersetzt. Nutzt also diesen Anlass, um mal zu überprüfen, ob\ntatsächlich auch alle eure Passwörter wirklich sichere Passwörter sind.",{"title":469,"searchDepth":507,"depth":507,"links":15720},[15721,15722,15723],{"id":15670,"depth":507,"text":15671},{"id":15686,"depth":507,"text":15687},{"id":15705,"depth":507,"text":15706},[1031],"2020-05-07T20:08:39","Passwörter sind überall. Sie schützen alle möglichen Aspekte unseres täglichen Lebens. Trotzdem denken wir viel zu wenig\\nüber sie nach. Wir wollen uns den World Password Day zum Anlass nehmen, uns Passwörter, ihre Sicherheit und die Theorie\\ndahinter etwas genauer anzuschauen.",{},"/blog/welt-passwort-tag",{"title":15649,"description":15658},"blog/welt-passwort-tag",[11907,15732],"password","Passwörter sind überall. Sie schützen alle möglichen Aspekte unseres täglichen Lebens. Trotzdem denken wir viel zu wenig über sie nach. Wir wollen uns den World Password Day zum Anlass nehmen uns Passwörter, ihre Sicherheit und die Theorie dahinter etwas genauer anzuschauen.","O11OSc3CtpDG7MZY5l0DTyLzxhSJg75H2ZTDUsdfjI4",{"id":15736,"title":15737,"author":15738,"body":15739,"category":15760,"date":15761,"description":15762,"extension":1034,"link":15763,"meta":15764,"navigation":916,"path":15765,"seo":15766,"slug":15743,"stem":15767,"tags":15768,"teaser":15769,"__hash__":15770},"blog/blog/synyx-als-partner-der-cyberwehr-bw.md","synyx als Partner der Cyberwehr BW",[94],{"type":432,"value":15740,"toc":15758},[15741,15744,15747,15750,15753],[435,15742,15737],{"id":15743},"synyx-als-partner-der-cyberwehr-bw",[439,15745,15746],{},"Wer bei Cyberwehr an Feuerwehr für “Cyber” denkt, liegt damit gar nicht so falsch. Wenn es brennt, wissen alle sofort,\nwo sie Hilfe bekommen – bei der 112. Doch was ist, wenn es nicht im wortwörtlichen Sinne brennt, sondern die\nFirmenwebseite gekapert, Server übernommen oder ganze Rechnerlandschaften lahmgelegt wurden und nun der Betrieb steht?\nWorauf große Firmen mit eigener IT-Sicherheitsabteilung vorbereitet sind, trifft Mittelständler und Kleinunternehmen\nbesonders nicht-technischer Branchen oft hart. Der Lieblingsbäcker oder die Autowerkstatt um die Ecke mag zwar keine\neigene IT-Abteilung geschweige denn IT-Sicherheitsexpert*innen beschäftigen, auf Internet und eine funktionierende IT\nkann allerdings kaum noch ein Betrieb verzichten.",[439,15748,15749],{},"Hier kommt das Cyberwehr Projekt ins Spiel, das vom Forschungszentrum Informatik (fzi) und dem Land Baden-Württemberg\nins Leben gerufen wurde. Die Cyberwehr möchte – analog zur Feuerwehr – als Notfallnummer und Anlaufstelle für\nUnternehmen fungieren, die Opfer eines Cyberangriffs geworden sind. Dabei hilft die Cyberwehrleitstelle in\nZusammenarbeit mit externen Sicherheitsexpert*innen den Betroffenen dabei den Vorfall zu untersuchen, Schäden\nfestzustellen und Maßnahmen zur Schadensbehebung zu erstellen und umzusetzen.",[439,15751,15752],{},"Als Partnerunternehmen stellen nun auch wir als synyx unsere Kompetenzen im Bereich IT-Sicherheit dem Cyberwehr Projekt\nund damit hilfesuchenden Unternehmen zur Verfügung. Wer beim nächsten Sicherheitsvorfall die 0800-CYBERWEHR wählt, kann\nalso in Zukunft auch auf unsere Unterstützung zählen.",[439,15754,15755],{},[2205,15756],{"alt":469,"src":15757},"https://media.synyx.de/uploads/2020/05/Cyberwehr-768x384.jpg",{"title":469,"searchDepth":507,"depth":507,"links":15759},[],[1031],"2020-05-04T15:37:47","Wer bei Cyberwehr an Feuerwehr für “Cyber” denkt, liegt damit gar nicht so falsch. Wenn es brennt, wissen alle sofort,\\nwo sie Hilfe bekommen – bei der 112. Doch was ist, wenn es nicht im wortwörtlichen Sinne brennt, sondern die\\nFirmenwebseite gekapert, Server übernommen oder ganze Rechnerlandschaften lahmgelegt wurden und nun der Betrieb steht?\\nWorauf große Firmen mit eigener IT-Sicherheitsabteilung vorbereitet sind, trifft Mittelständler und Kleinunternehmen\\nbesonders nicht-technischer Branchen oft hart. Der Lieblingsbäcker oder die Autowerkstatt um die Ecke mag zwar keine\\neigene IT-Abteilung geschweige denn IT-Sicherheitsexpert*innen beschäftigen, auf Internet und eine funktionierende IT\\nkann allerdings kaum noch ein Betrieb verzichten.","https://synyx.de/blog/synyx-als-partner-der-cyberwehr-bw/",{},"/blog/synyx-als-partner-der-cyberwehr-bw",{"title":15737,"description":15746},"blog/synyx-als-partner-der-cyberwehr-bw",[11907],"Was tun, wenn die Firmen-Webseite gekapert, Server übernommen oder ganze Rechnerlandschaften lahmgelegt wurden und nun der Betrieb steht?","3ywRwFWb1KLfqXKnaYgLmNOs6hju9fV_OnEN8grnkZo",{"id":15772,"title":15773,"author":15774,"body":15775,"category":15884,"date":15885,"description":15886,"extension":1034,"link":15887,"meta":15888,"navigation":916,"path":15889,"seo":15890,"slug":15891,"stem":15892,"tags":15893,"teaser":15894,"__hash__":15895},"blog/blog/domain-driven-design-remote-workshop-mit-michael-ploed-innoq.md","Domain Driven Design Remote-Workshop mit Michael Plöd | INNOQ",[250,91],{"type":432,"value":15776,"toc":15882},[15777,15780,15783,15786,15789,15792,15809,15812,15815,15820,15823,15826,15829,15832,15835,15840,15843,15846,15849,15852,15855,15858,15861,15864,15867,15870,15873,15876,15879],[435,15778,15773],{"id":15779},"domain-driven-design-remote-workshop-mit-michael-plöd-innoq",[439,15781,15782],{},"Bereits Anfang des Jahres fand bei synyx ein Workshop mit Michael Plöd von INNOQ zum Thema Domain Driven Design statt,\ndieser war bei den teilnehmenden Mitarbeiter*innen ein voller Erfolg. So stand es außer Frage, dass es einen weiteren\nIntensivkurs über drei Tage geben sollte.",[439,15784,15785],{},"Da sich seit März die Ereignisse aufgrund der Corona-Pandemie jedoch überschlugen, stiegen sowohl INNOQ als auch wir,\nwie natürlich viele andere Firmen ebenfalls, komplett auf Remote-Arbeit im Home-Office um. Es stellte sich nun die\nFrage, wie wir mit der geplanten Schulung umgehen wollten. Eine verteilte Kollaboration bei Entwicklungstätigkeiten ist\nnatürlich längst gang und gäbe.",[439,15787,15788],{},"Weiterhin haben wir allgemein in der IT-Branche den großen Vorteil, dass Remote-Arbeit kein Neuland für uns ist und\nwir seit vielen Jahren Erfahrung mit diesem Arbeitsmodus haben. Aber eine Schulung, bei der alle Teilnehmer*innen und\nder Trainer von zu Hause aus arbeiten, hatten auch wir bis dato noch nie. So entschlossen wir uns zusammen für ein\nExperiment: wir führen die 3-Tage Schulung remote durch. Neue Herausforderungen brauchen eine offene Herangehensweise\nund sowohl synyx als auch INNOQ stehen Neuem grundsätzlich aufgeschlossen gegenüber.",[439,15790,15791],{},"Im Vorfeld des Termins machte sich der INNOQ Fellow Michael Plöd viele Gedanken darüber, wie er den Spirit seiner\nbestehenden und vom iSAQB zertifizierten Vor-Ort DDD Schulung in die Remote-Welt übertragen kann. Da das bestehende\nTraining sehr interaktiv ist und auf der Arbeit an einer komplexen Fallstudie basiert standen folgende Fragen im Raum:",[994,15793,15794,15797,15800,15803,15806],{},[997,15795,15796],{},"Was ist ein passendes Tooling für Voice, Video, Screensharing und die Übungen?",[997,15798,15799],{},"Welche Iterationen zwischen Theorie und Praxis sind für remote am besten geeignet?",[997,15801,15802],{},"Wie stelle ich als Trainer sicher, dass ich keine*n Teilnehmer*in inhaltlich verliere wenn über Video vor allem die\nkleinen Interaktionen und Emotionen verloren gehen können?",[997,15804,15805],{},"Welche neuen Möglichkeiten für Übungen gibt es durch Remote-Tools?",[997,15807,15808],{},"Was sind passende Zyklen für Pausen, vor allem wenn die meisten Teilnehmer*innen noch Kinder zu Hause haben?",[439,15810,15811],{},"Das Ziel war es unter Berücksichtigung der obigen Fragen aus einer sehr bewährten und guten Präsenzschulung eine\nremote-first Schulung mit ansprechender “Teilnehmer-Experience” zu schaffen. Die bei synyx durchgeführte remote Domain\nDriven Design Schulung setzte dabei auf zwei Tools: eine Lösung für Video, Voice und Screensharing und ein Tool für die\nÜbungen. Bei ersterem wurde eine Plattform gewählt, die sog. Breakout-Rooms unterstützt. Dieses Feature gibt dem\nTrainer die Möglichkeit die gesamten Teilnehmer*innen in Sub-Videokonferenzen aufzuteilen was vor allem bei Übungen\nein echter Vorteil ist. Damit lassen sich jederzeit Übungsgruppen bilden und als Trainer kann man einfach zwischen den\nGruppen pendeln.",[439,15813,15814],{},"Weiterhin galt die Regel: Video ist immer bei allen an, außer in den Pausen. Für die Übungen wurde ein\nBrowser-basiertes Tool zur remote Kollaboration gewählt in dem zahlreiche Übungen vorbereitet wurden. Normalerweise\nbasiert die Vor-Ort Domain Driven Design Schulung auf großen und intensiven Übungsblöcken, deren Bearbeitung durchaus\neinmal 45 Minuten dauern kann. Für die Remote-Variante wurde dieser Modus Operandi verändert indem auf kürzere\nTheorie-/Übungs-Zyklen gewechselt wurde. Nur in Ausnahmen gab es zwei längere Theorie Blöcke.",[439,15816,15817],{},[2205,15818],{"alt":469,"src":15819},"https://media.synyx.de/uploads/2020/04/8_2-Retrospektive-Context-Maps-768x811.jpg",[439,15821,15822],{},"Retrospektive Context Maps",[439,15824,15825],{},"Bei den einzelnen Übungen wurden sämtliche Möglichkeiten genutzt, die ein gutes remote Kollaborationstool bietet. Neben\nden klassischen “die Teilnehmer*innen erarbeiten etwas” Übungen gab es auch zahlreiche Aufgaben, bei denen die Gruppen\nbeispielsweise Statements gruppieren sollten oder bei denen es galt vorgefertigte Teillösungen zu einer stimmigen\nGesamtlösung zu kombinieren.",[439,15827,15828],{},"Weiterhin wurden regelmäßige Retrospektiven eingesetzt um sicherzustellen, dass alle Teilnehmer*innen den bisher\nvermittelten Stoff verstanden haben.",[439,15830,15831],{},"Im Hinblick auf die Pausen haben wir mit einer großen Mittagspause und sonst mit 1h Schulung – 10 min Pause gearbeitet.",[439,15833,15834],{},"Was uns natürlich alle interessiert, was waren denn nun die besonderen Herausforderungen und auch die Vorteile, die sich\ndurch einen Workshop im Remote-Modus ergeben haben?",[439,15836,15837],{},[2205,15838],{"alt":469,"src":15839},"https://media.synyx.de/uploads/2020/04/Retrospektive-Feedback-Schulung-768x1419.jpg",[439,15841,15842],{},"Retrospektive zur Schulung",[439,15844,15845],{},"Die gewählten kollaborativen Tools erwiesen sich für alle Teilnehmer*innen als perfekt geeignet für den interaktiven\nWorkshop. Der Einsatz des Video-Chats in Kombination mit dem virtuellem Sticky-Board ist ideal, um Übungen gemeinsam\nzu erarbeiten. Bis auf minimale Anlaufschwierigkeiten, die sich bereits durch den Set-Up-Test einige Tage vor dem\nBeginn der Schulung ausmerzen ließen, ergaben sich von der technischen Seite keinerlei Probleme.",[439,15847,15848],{},"Einige Teilnehmer*innen wünschten sich mehr zwischenmenschlichen Kontakt, der sonst beispielsweise in den Pausen\nstattfindet – das beiläufige Resümieren der Themen bei Kaffee oder Mittagessen. Vielleicht wäre hier das Einrichten\neines virtuellen Pausenraums oder eine freiwillige Socializing-Möglichkeit am Abend des zweiten Schulungstags eine\nOption, um auch zwischen den Arbeitsphasen eine Möglichkeit zum Austausch abseits der Schulung zu bieten.\nGruppenarbeiten kamen jedoch nicht zu kurz und trugen enorm zum fachlichen Austausch bei.",[439,15850,15851],{},"Durch die Verwendung von Laptop und Handy im Workshop, ist es natürlich sehr wichtig den Fokus aufrecht zu erhalten und\nsich diszipliniert nur den benötigten Anwendungen zu widmen. Die Teilnehmer*innen sind dabei gefordert, die\nAufmerksamkeit entsprechend zu bündeln.",[439,15853,15854],{},"Die kürzeren Pausenintervalle und die kürzere Taktung von Theorie und Übungen helfen hierbei aber ungemein, was von\nallen Teilnehmer*innen bestätigt wurde.",[439,15856,15857],{},"Trainer und Teilnehmer*innen sparen sich Anfahrtszeit und unter Umständen eine beschwerliche Anreise.",[439,15859,15860],{},"Der Aufwand die Räumlichkeiten vorzubereiten entfällt komplett, dafür muss selbstverständlich die virtuelle Konzeption\nsehr gut vorbereitet sein. Weiterhin bieten digitale Tools zur Kollaboration gänzlich neue didaktische Möglichkeiten um\nLernergebnisse zu vertiefen.",[439,15862,15863],{},"Das digitale Arbeiten ermöglicht zudem automatisch eine vereinfachte Dokumentation der Ergebnisse.",[439,15865,15866],{},"Keiner muss beispielsweise das Handy zücken, um das Whiteboard zu fotografieren.",[439,15868,15869],{},"Die Achievements des Tages lassen sich bequem und unabhängig rekapitulieren.",[439,15871,15872],{},"Empfehlenswert ist der Einsatz mindestens eines externen Bildschirms, um eine optimale Übersicht zu gewährleisten.",[439,15874,15875],{},"Die Verwendung von digitalen Post-its erzeugt zudem keinen Papiermüll und die Arbeitsergebnisse sind leichter\neditierbar und gut lesbar. Ein weiterer absoluter Pluspunkt für virtuelle Workshops.",[439,15877,15878],{},"Es ist natürlich immer ein verbindendes Erlebnis, gemeinsam an einem Workshop teilzunehmen, bei dem man sich vor Ort\naustauscht und zusammen Zeit verbringt.",[439,15880,15881],{},"Aber wir können nur wiederholen, neue Herausforderungen brauchen eine offene Herangehensweise und wir wurden nicht\nenttäuscht. Michael Plöd hat uns mit seiner professionellen Vorbereitung und Durchführung einmal mehr begeistert, der\nWorkshop war unglaublich produktiv und inspirierend.",{"title":469,"searchDepth":507,"depth":507,"links":15883},[],[1031],"2020-04-17T09:10:01","Bereits Anfang des Jahres fand bei synyx ein Workshop mit Michael Plöd von INNOQ zum Thema Domain Driven Design statt,\\ndieser war bei den teilnehmenden Mitarbeiter*innen ein voller Erfolg. So stand es außer Frage, dass es einen weiteren\\nIntensivkurs über drei Tage geben sollte.","https://synyx.de/blog/domain-driven-design-remote-workshop-mit-michael-ploed-innoq/",{},"/blog/domain-driven-design-remote-workshop-mit-michael-ploed-innoq",{"title":15773,"description":15782},"domain-driven-design-remote-workshop-mit-michael-ploed-innoq","blog/domain-driven-design-remote-workshop-mit-michael-ploed-innoq",[8410],"Neue Herausforderungen brauchen eine offene Herangehensweise und sowohl synyx als auch INNOQ stehen Neuem grundsätzlich aufgeschlossen gegenüber. Aus diesem Grund entschieden wir uns für ein gemeinsames Experiment: wir führen die 3-Tage Schulung remote durch.","fHthPQt2SKikCaWmBsq7YsTLZiLuLO0eXH64tcuyaOc",{"id":15897,"title":15898,"author":15899,"body":15900,"category":16358,"date":16359,"description":16360,"extension":1034,"link":16361,"meta":16362,"navigation":916,"path":16363,"seo":16364,"slug":16365,"stem":16366,"tags":16367,"teaser":16370,"__hash__":16371},"blog/blog/frauen-in-der-it.md","synyx Girls’ Week: Frauen in der IT",[91],{"type":432,"value":15901,"toc":16353},[15902,15905,15910,15920,15924,15932,15939,15947,15950,15954,15957,15964,15968,15971,15974,15979,15982,15987,15990,15995,15998,16003,16006,16011,16014,16019,16022,16027,16030,16034,16037,16040,16044,16047,16050,16053,16057,16060,16063,16067,16070,16075,16080,16083,16086,16089,16092,16095,16098,16101,16104,16109,16112,16116,16119,16122,16125,16128,16132,16135,16138,16142,16145,16148,16151,16154,16157,16160,16164,16167,16170,16173,16176,16179,16182,16185,16188,16191,16195,16198,16201,16204,16207,16210,16215,16218,16222,16225,16234,16238,16241,16245,16248,16252,16255,16260,16263,16267,16270,16274,16277,16280,16284,16287,16290,16294,16297,16302,16305,16309,16312,16316,16319,16322,16326,16329,16332,16336,16339,16342,16345,16350],[435,15903,15898],{"id":15904},"synyx-girls-week-frauen-in-der-it",[439,15906,15907],{},[990,15908,15909],{},"Wir hatten uns wie jedes Jahr sehr auf den Girls‘ Day gefreut, aber leider liegen die derzeitigen Ereignisse nicht in\nunserer Macht und Sicherheit geht absolut vor.",[439,15911,15912],{},[990,15913,3480,15914,15919],{},[1002,15915,15918],{"href":15916,"rel":15917},"https://www.girls-day.de",[1006],"Girls‘ Day",", ein Mädchen-Zukunftstag, dient der Berufsorientierung von Schülerinnen.\nSie werden dazu ermutigt, an diesem Tag Erfahrungen in Berufsfeldern zu machen, in denen Frauen bis dato noch eher\nselten anzutreffen sind.",[3938,15921,15923],{"id":15922},"rolle-der-frauen-in-der-it","Rolle der Frauen in der IT",[439,15925,15926,15927,15931],{},"Schaut man sich in Deutschland den mit 16,6% doch sehr geringen Frauenanteil in der Branche an, ist es vielen nicht\nbewusst, dass Frauen maßgeblich an der Computerentwicklung beteiligt\nwaren. [",[1002,15928,5008],{"href":15929,"rel":15930},"https://de.statista.com/infografik/13283/frauen-in-der-tech-branche/",[1006],"]",[439,15933,15934,15935,15931],{},"Um nur zwei der Pionierinnen der Informatik kurz vorzustellen, sei zum einen Ada Lovelace (1815-1852) genannt, die\nsich schon in sehr jungen Jahren für Maschinen und Technik zu interessieren begann. Weit vor der Realisierbarkeit\nentwickelte sie schon Konzepte zur Programmierung einer mechanischen\nMaschine. [",[1002,15936,4871],{"href":15937,"rel":15938},"https://www.frauen-informatik-geschichte.de/index.php-id=36.htm",[1006],[439,15940,15941,15942,15931],{},"Auch Grace Hopper (1906-1992), eine renommierte Mathematikerin, die bereits Ende der 40er Jahre des 20. Jahrhunderts\nvon den vielfältigen Anwendungsmöglichkeiten von Computern überzeugt war, darf an dieser Stelle nicht fehlen. Sie\nentwickelte den ersten Compiler, der für Menschen verständliche Programmiersprachen in von Computern genutzte\nMaschinensprache übersetzt und war überhaupt ausschlaggebend für die Entwicklung erster\nProgrammiersprachen. [",[1002,15943,15946],{"href":15944,"rel":15945},"https://www.frauen-informatik-geschichte.de/index.php-id=62.htm",[1006],"3",[439,15948,15949],{},"Die Liste an Frauen, die wesentliche Grundsteine der Computertechnik gelegt haben, ließe sich noch weiter fortführen.",[3938,15951,15953],{"id":15952},"überholte-rollenbilder-übwerwinden","Überholte Rollenbilder übwerwinden",[439,15955,15956],{},"Weibliche, wie natürlich auch andere Perspektiven, sind in der IT sehr wichtig. Wir möchten jungen Frauen ein positives\nVorbild liefern und sie damit bestärken, ihren Interessen und Begabungen frei von überholten Rollenbildern nachzugehen.",[439,15958,15959,15960,15931],{},"Es gibt viele Beispiele die zeigen, wie wichtig es ist, dass Teams divers aufgestellt sind. Amazon beispielsweise hatte\neinen Algorithmus entwickelt, der bei der Auswahl von neuen Mitarbeitern unterstützen sollte. Dieser nahm die Datensätze\nder in den letzten zehn Jahren eingestellten Personen als Basis. Durch die in der Tech-Industrie vorherrschende Anzahl\nan Männern, schloss die KI, dass sie Frauen entsprechend schlechter bewerten solle. Das ist sicherlich nicht bewusster\nDiskriminierung geschuldet, aber es ist anzunehmen, dass dieses Verhalten eher aufgefallen wäre, wenn Frauen an der\nEntwicklung beteiligt gewesen wären. [",[1002,15961,5224],{"href":15962,"rel":15963},"https://reut.rs/2Qww5k5",[1006],[3938,15965,15967],{"id":15966},"mit-positiven-beispielen-voran","Mit positiven Beispielen voran",[439,15969,15970],{},"Da es uns sehr am Herzen liegt, junge Frauen für technischen Berufe zu begeistern und sie zu überzeugen ihren Weg zu\ngehen, haben wir beschlossen für diese Woche, in der der Girls’ Day stattgefunden hätte, eine Blog-Serie zu starten.",[439,15972,15973],{},"Glücklicherweise gibt es bei synyx äußerst engagierte Mitarbeiterinnen, die Euch gerne einen Einblick in ihre Arbeit und\nihren Weg in die IT geben.",[439,15975,15976],{},[2205,15977],{"alt":78,"src":15978},"https://media.synyx.de/uploads/2019/06/duerlich_ws-768x768.jpg",[439,15980,15981],{},"Isabell",[439,15983,15984],{},[448,15985,15986],{},"Seit wann arbeitest du bei synyx und was ist dein Aufgabenfeld?",[439,15988,15989],{},"Ich arbeite seit fast 4 Jahren bei synyx. Die letzten 3,5 Jahre war ich in der Softwareüberwachung und Entwicklung\ntätig, seit Anfang des Jahres schreibe ich Software für den Logistik-Bereich.",[439,15991,15992],{},[448,15993,15994],{},"Was hat dein Interesse geweckt, einen Beruf in der IT zu wählen?",[439,15996,15997],{},"Ich hatte schon immer Interesse an Computern und anderen technischen Geräten. In meiner Freizeit habe ich oft am\nComputer gespielt. In der Oberstufe hatte ich dann für ein halbes Jahr Informatik als Nebenfach. Dort habe ich auch das\nerste mal eigene kleine Programme geschrieben, was ich sehr spaßig fand.",[439,15999,16000],{},[448,16001,16002],{},"Gibt es etwas, das du an deinem Beruf besonders magst?",[439,16004,16005],{},"Die Möglichkeit in viele spannende Bereiche einzutauchen und jeden Tag etwas Neues zu lernen.",[439,16007,16008],{},[448,16009,16010],{},"Wie war dein Werdegang?",[439,16012,16013],{},"2013 habe ich mein Abi an einem naturwissenschaftlichen Gymnasium gemacht und habe mich dann relativ spontan für ein\nInformatikstudium an der Fachhochschule in Karlsruhe entschieden. Die ersten Semester des Studiums waren sehr\ninteressant und haben mir Spaß gemacht. Nach meinem Praxissemester im Bereich Geoinformatik habe ich mich dann als\nWerkstudent bei synyx beworben, wo ich sowohl meine Bachelor- als auch meine Masterarbeit geschrieben habe. Ende 2019\nhabe ich dann mein Studium abgeschlossen und arbeite seit dem Vollzeit bei synyx.",[439,16015,16016],{},[448,16017,16018],{},"Möchtest du jungen Frauen oder speziell den Mädchen beim Girls‘ Day, die sich für technische Berufe interessieren,\netwas mit auf den Weg geben?",[439,16020,16021],{},"Wenn ihr Interesse daran habt einen technischen Beruf zu lernen, dann versucht´s einfach mal. Ich war mir damals auch\nunsicher, ob es das richtige Studium für mich ist und heute bin ich froh mich dafür entschieden zu haben. Selbst wenn\nman merkt, dass es nicht das Richtige für einen sein sollte, hat man immer noch die Möglichkeit sich für einen anderen\nBeruf zu entscheiden. Meiner Meinung nach ist das wichtigste einen Beruf zu finden, der einem Spaß macht, und dafür\nsollte man sich Zeit nehmen.",[439,16023,16024],{},[2205,16025],{"alt":55,"src":16026},"https://media.synyx.de/uploads/2019/06/julia_d_ws-768x768.jpg",[439,16028,16029],{},"Julia",[439,16031,16032],{},[448,16033,15986],{},[439,16035,16036],{},"Ich arbeite seit Oktober 2015 bei synyx und habe ein Duales Studium absolviert.",[439,16038,16039],{},"Mein Aufgabenfeld ist unheimlich vielseitig, zunächst bin ich Anwendungsentwicklerin, sowohl im Backend, als auch im\nFrontend. Dazu engagiere ich mich noch sehr in der Organisation der Devoxx4Kids, eine Veranstaltung um Kindern einen\nkreativen Umgang mit Computern und Spaß am Programmieren zu vermitteln. Da gibt es immer viel zu tun, Orga der\nWorkshops, Planung des Events, die Dokumentation etc.",[439,16041,16042],{},[448,16043,15994],{},[439,16045,16046],{},"Mein Interesse an der IT hat eine Aufgabe im Wirtschaftsgymnasium geweckt. Wir sollten",[439,16048,16049],{},"für das Schulfach Datenverarbeitung einen Steckbrief über uns in Form einer eigenen",[439,16051,16052],{},"Webseite erstellen. Mein damaliger Freund war auch Informatiker und hat mir zu der Zeit weitere Sachen im IT-Umfeld\ngezeigt.",[439,16054,16055],{},[448,16056,16002],{},[439,16058,16059],{},"Man versucht eine Lösung für ein Problem zu finden und macht damit die Anwender der",[439,16061,16062],{},"Software glücklicher.",[439,16064,16065],{},[448,16066,16010],{},[439,16068,16069],{},"Realschule, Wirtschaftsgymnasium, Duales Studium (Informatik)",[439,16071,16072],{},[448,16073,16074],{},"Möchtest du jungen Frauen oder speziell den Mädchen beim Girls‘ Day, die",[439,16076,16077],{},[448,16078,16079],{},"sich für technische Berufe interessieren, etwas mit auf den Weg geben?",[439,16081,16082],{},"Der Beruf ist sehr vielseitig, da es nicht nur einen Weg bereitstellt, sondern viele Möglichkeiten",[439,16084,16085],{},"und Bereiche bietet:",[439,16087,16088],{},"◦ Gestaltung von Benutzeroberflächen",[439,16090,16091],{},"◦ Benutzerführung",[439,16093,16094],{},"◦ Logik und Mathematik",[439,16096,16097],{},"◦ Architekturen einer Software",[439,16099,16100],{},"◦ Probleme, Bugs finden und beheben",[439,16102,16103],{},"Es gibt unheimlich viele Optionen sich persönlich weiter zu entwickeln.",[439,16105,16106],{},[2205,16107],{"alt":164,"src":16108},"https://media.synyx.de/uploads/2019/08/khrystyna_ws-768x768.jpg",[439,16110,16111],{},"Khrystyna",[439,16113,16114],{},[448,16115,15986],{},[439,16117,16118],{},"Ich mache seit 2018 bei synyx meine Ausbildung zur Fachinformatikerin im Bereich Anwendungsentwicklung.",[439,16120,16121],{},"Zur Zeit bin ich damit beschäftigt zu lernen, wie man eine App für Android programmiert.",[439,16123,16124],{},"Zwei mal pro Woche gehe ich in die Berufsschule und bereite mich auf",[439,16126,16127],{},"zukünftige Prüfungen vor.",[439,16129,16130],{},[448,16131,15994],{},[439,16133,16134],{},"Ich beschäftige mich gerne mit Aufgaben am PC. Auch als Kind war ich immer",[439,16136,16137],{},"neugierig, wie Handys oder Laptops funktionieren.",[439,16139,16140],{},[448,16141,16002],{},[439,16143,16144],{},"Natürlich, sehr viel. Ich mag, dass du als Softwareentwickler immer die",[439,16146,16147],{},"Möglichkeit hast, etwas Neues zu lernen. Es ist nie langweilig. Du arbeitest nicht nur",[439,16149,16150],{},"am PC und bist alleine. Es ist sehr wichtig sich zuerst mit Kunden und Kollegen zu besprechen,",[439,16152,16153],{},"wie ein Projekt am Ende fertig aussehen soll. Man muss im Team arbeiten,",[439,16155,16156],{},"auch voneinander lernen, und sehr viel kommunizieren, damit",[439,16158,16159],{},"der Kunde am Ende bekommt, was er will.",[439,16161,16162],{},[448,16163,16010],{},[439,16165,16166],{},"Ich komme aus der Ukraine, bin dort geboren und aufgewachsen. Habe dort",[439,16168,16169],{},"auch meinen Bachelor (Englischlehrerin, Deutsch als Nebenfach) gemacht. Da ich",[439,16171,16172],{},"in dieser Zeit an einem html/css-Kurs teil genommen habe, habe ich gemerkt,",[439,16174,16175],{},"dass es mir Spaß macht Webseiten zu stylen. Ich habe mir gewünscht, das irgendwann richtig zu lernen. Nicht nur\nFrontend, sondern auch Backend (diese Wörter waren auch für mich fremd).",[439,16177,16178],{},"Nach dem ich fertig mit dem Studium war, bin ich nach Deutschland gekommen. Zuerst um",[439,16180,16181],{},"die Sprache besser zu lernen, und dann bin ich auf diese Ausbildung gestoßen. Ich habe",[439,16183,16184],{},"mich weiter informiert, was man braucht, um Fachinformatikerin zu werden.",[439,16186,16187],{},"Danach ging alles ganz schnell: Ausbildungsbetrieb finden (synyx), Bewerbung",[439,16189,16190],{},"schreiben, Vorstellungsgespräch nicht versauen 🙂 et voilà du bist Azubi.",[439,16192,16193],{},[448,16194,16018],{},[439,16196,16197],{},"Es gibt keine Frauen- oder Männer-Berufe. Für mich ist es wichtig, dass man",[439,16199,16200],{},"herausfindet, was langfristig Spaß macht. Und ich bin der Meinung, es ist nie zu",[439,16202,16203],{},"spät etwas Neues zu lernen und sich weiter zu entwickeln.",[439,16205,16206],{},"Ich bin noch am Anfang und werde weiter gerne neue Sachen lernen",[439,16208,16209],{},"und mehr Erfahrung sammeln.",[439,16211,16212],{},[2205,16213],{"alt":31,"src":16214},"https://media.synyx.de/uploads/2019/08/aljona_ws-768x768.jpg",[439,16216,16217],{},"Aljona",[439,16219,16220],{},[448,16221,15986],{},[439,16223,16224],{},"Bei synyx bin ich schon eine halbe Ewigkeit, genauer gesagt seit 2011. Meine Reise bei synyx begann als Auszubildende\nfür Fachinformatik (Anwendungsentwicklung). Nach der Ausbildung habe ich dann einige Jahre als Softwareentwicklerin\ngearbeitet und seit einem guten Jahr bin ich nun als Scrum Master / Agile Coach tätig. Das bedeutet, mein Fokus liegt\nmittlerweile weniger auf der Technik, sondern mehr auf der kontinuierlichen Verbesserung von Prozessen und Kommunikation\ninnerhalb von Teams.",[439,16226,16227,16228,16233],{},"Schau doch auch mal in den",[1002,16229,16232],{"href":16230,"rel":16231},"https://synyx.de/blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick/",[1006],"Rückblick"," auf meine\nZeit bei synyx rein!",[439,16235,16236],{},[448,16237,15994],{},[439,16239,16240],{},"Angefangen hat bei mir damals alles mit Homepage-Basteleien. Anpassen zu können, wie Dinge im Browser dargestellt\nwerden, hat mich total fasziniert. Ganz viel war mit Ausprobieren verbunden und irgendwann habe ich beschlossen, mich da\nrichtig reinzufuchsen. In der Ausbildung habe ich dann natürlich sehr viel mehr gelernt, als nur Dinge im Browser\nanzuzeigen. Mein größter Antrieb blieb jedoch der gleiche: etwas erschaffen zu können, indem ich Zeichen in eine gewisse\nStruktur bringe.",[439,16242,16243],{},[448,16244,16002],{},[439,16246,16247],{},"Die IT ist ein super schnelllebiger Bereich, der sich ständig verändert und weiterentwickelt. Es reicht nicht aus, sich\neinmalig etwas anzueignen, stattdessen ist lebenslanges Lernen gefragt. Aber genau das macht das Berufsfeld meiner\nMeinung nach so spannend, denn es wird einfach nie langweilig.",[439,16249,16250],{},[448,16251,16018],{},[439,16253,16254],{},"Ganz egal um welchen Beruf es geht: Begeisterung ist alles. Wenn du dich für etwas begeistern kannst, ist der Grundstein\ngelegt. Den Rest lernst du schon mit der Zeit.",[439,16256,16257],{},[2205,16258],{"alt":324,"src":16259},"https://media.synyx.de/uploads/2019/06/lila_ws-768x768.jpg",[439,16261,16262],{},"Lila",[439,16264,16265],{},[448,16266,15986],{},[439,16268,16269],{},"Ich bin seit Oktober 2017 bei synyx. Noch bin ich im Studium. Das absolviere ich an der DHBW Karlsruhe, was bedeutet,\ndass ich immer mal ein paar Wochen im Betrieb und dann wieder einige Wochen an der DH bin. Bei synyx mache ich dann\nkleinere Projekte, die eigentlich immer Relevanz für eine bestehende Software, einen Partner oder synyx selbst haben.\nDarüber schreibe ich für die DH jeweils eine Praxisarbeit.",[439,16271,16272],{},[448,16273,15994],{},[439,16275,16276],{},"Eigentlich bin ich fertig ausgebildete Lehrerin. Aber je nach Fächerkombination ist es schwierig bis unmöglich gewesen\neine Stelle zu bekommen. Bei mir war es unmöglich. Daher habe ich als Erzieherin gejobbt und wusste schnell, das das\nnichts für mich ist. Nach drei Jahren habe ich dann angefangen mich nach anderen Möglichkeiten umzusehen.",[439,16278,16279],{},"Viele meiner Freunde, mit denen ich mich sehr gut verstehe, arbeiten im IT Bereich, daher kam ich auf die Idee mir das\nmal anzuschauen. Vielleicht denke ich ja nicht ohne Grund ähnlich wie meine Freunde, hatte ich mir gedacht. Kurze Zeit\nspäter habe ich einen Fernkurs zur C#-Softwareentwicklerin begonnen. Der hat mir dann so viel Spaß gemacht, dass ich\nmich informiert habe, wie ich diesen Beruf am besten erlernen und ausüben kann.",[439,16281,16282],{},[448,16283,16002],{},[439,16285,16286],{},"Zwar ist es nicht selten frustrierend, wenn etwas eine ganze Weile nicht so funktioniert, wie man gern hätte. Bei der\nSoftwareentwicklung passiert das regelmäßig. Aber man ist nie allein, kann andere im Team fragen, programmiert\ngemeinsam. Das finde ich toll. Und das Gefühl, wenn man dann die Lösung gefunden hat, ist super. Bei synyx arbeiten wir\nagil, daher ist die Teamarbeit sehr wichtig. Ich fühle mich mit den KollegInnen sehr wohl.",[439,16288,16289],{},"Außerdem kann man immer etwas Neues lernen. Alles entwickelt sich und bleibt daher spannend.",[439,16291,16292],{},[448,16293,16018],{},[439,16295,16296],{},"Lasst euch nicht erzählen, was ihr angeblich nicht könnt. Macht, was ihr mögt, denn dann könnt ihr mit eurem Beruf\nerfolgreich und glücklich werden.",[439,16298,16299],{},[2205,16300],{"alt":116,"src":16301},"https://media.synyx.de/uploads/2024/01/dani-2023-300x300.jpg",[439,16303,16304],{},"Dani",[439,16306,16307],{},[448,16308,15986],{},[439,16310,16311],{},"Seit September 2018 unterstütze ich synyx als Systementwicklerin bzw. -administratorin, sprich ich kümmere mich um\nalles was Software braucht, um laufen zu können. Dazu gehört die Konfiguration und der Betrieb von Servern und der\ndarauf ausgeführten Software, aber auch die Unterstützung der Kollegen aus der Entwicklung bei ihrer Arbeit in Bereichen\nwie beispielsweise Sicherheit und Stabilität. Bei uns Administratoren laufen viele Kommunikationsfäden zusammen, sowohl\nvon Menschen als auch von Hard- und Software. Vielseitigkeit und ein guter Überblick ist dazu sehr wichtig. Ab und zu\nmuss aber auch einem Drucker gut zugeredet oder an einem Kabel gewackelt werden.",[439,16313,16314],{},[448,16315,15994],{},[439,16317,16318],{},"Ich habe mich schon in früher Kindheit für Computer interessiert und darauf programmiert, kommuniziert, gespielt,\ngeschrieben, gestaltet, sie auf- und auseinander geschraubt und anderweitig hantiert. Dabei habe ich mir vieles selbst\nbeigebracht, das mache ich heute immer noch so.",[439,16320,16321],{},"Der Rest hat sich sehr natürlich angefühlt. In der Schule habe ich mich für die naturwissenschaftlichen und technischen\nFächer interessiert und entsprechend meinen Stundenplan gestaltet. Am Ende führte eins zum anderen,\nInformatik-Ausbildung, Informatik-Studium und nun ist es ein gewaltiger Teil meines Lebens.",[439,16323,16324],{},[448,16325,16002],{},[439,16327,16328],{},"Vielseitigkeit, ständiger Fortschritt und lebenslanges Lernen motivieren mich dran zu bleiben und bringen mich auch nach\nso vielen Jahren immer wieder zum Staunen. Das Lösen von komplexen Problemen, sowohl technisch als auch\nzwischenmenschlich, macht mir viel Spaß und fordert mich heraus.",[439,16330,16331],{},"Außerdem mag ich dass ich von überall arbeiten kann, wenn ich das möchte.",[439,16333,16334],{},[448,16335,16010],{},[439,16337,16338],{},"Kurvig, aber immer auf Informatik ausgerichtet: Technisches Wahlfach auf der Realschule, Technisches Gymnasium,\nAusbildung zur Fachinformatikerin Richtung Anwendungsentwicklung und letztendlich Studium an der Hochschule in\nInformatik.",[439,16340,16341],{},"Dazwischen ein abgebrochenes Studium an der Uni in Informatik – ich bin einfach keine Theoretikerin.",[439,16343,16344],{},"Während des Studiums und danach habe ich länger als Software-Entwicklerin gearbeitet, dabei habe ich die Affinität für\nAdministratives entwickelt und ausgebaut.",[439,16346,16347],{},[448,16348,16349],{},"Möchtest du jungen Frauen oder speziell den Mädchen beim Girls‘ Day die sich für technische Berufe interessieren,\netwas mit auf den Weg geben?",[439,16351,16352],{},"Lasst euch nicht erzählen was ihr könnt und wer ihr seid. Wenn ihr Lust auf was habt, macht es einfach – es lohnt sich.",{"title":469,"searchDepth":507,"depth":507,"links":16354},[16355,16356,16357],{"id":15922,"depth":507,"text":15923},{"id":15952,"depth":507,"text":15953},{"id":15966,"depth":507,"text":15967},[1031],"2020-03-23T08:07:36","Wir hatten uns wie jedes Jahr sehr auf den Girls‘ Day gefreut, aber leider liegen die derzeitigen Ereignisse nicht in\\nunserer Macht und Sicherheit geht absolut vor.","https://synyx.de/blog/frauen-in-der-it/",{},"/blog/frauen-in-der-it",{"title":15898,"description":15909},"frauen-in-der-it","blog/frauen-in-der-it",[16368,16369],"girls-day","women-in-tech","Wir hatten uns wie jedes Jahr sehr auf den Girls‘ Day gefreut, aber leider liegen die derzeitigen Ereignisse nicht in unserer Macht und Sicherheit geht absolut vor. Da es uns sehr am Herzen liegt, junge Frauen mit Interesse an technischen Berufen, zu überzeugen ihren Weg zu gehen, haben wir überlegt, wie wir das umsetzen können.","UlzuBd-Uze9865Q4zmGTyZJk2yrqItGvfpkbDYQNFj0",{"id":16373,"title":16374,"author":16375,"body":16376,"category":16523,"date":16524,"description":16525,"extension":1034,"link":16526,"meta":16527,"navigation":916,"path":16528,"seo":16529,"slug":16531,"stem":16532,"tags":16533,"teaser":16535,"__hash__":16536},"blog/blog/die-oop-2020-in-muenchen.md","Die OOP 2020 in München",[238,244],{"type":432,"value":16377,"toc":16517},[16378,16381,16388,16403,16407,16417,16421,16443,16447,16474,16477,16481,16504,16514],[435,16379,16374],{"id":16380},"die-oop-2020-in-münchen",[439,16382,16383,16384,16387],{},"Vom 03. bis zum 07. Februar fand in München zum bereits 29ten Male die OOP statt, eine jährlich von SIGS DATACOM\norganisierte Fachtagung für Software-Architektur. Unter dem Motto ",[448,16385,16386],{},"business meets software","fanden sich gut 2400\nTeilnehmer an der Messe in München ein, um in regen Austausch zu treten und sich fortzubilden. Insgesamt wurden 170\nVorträge in 13 Tracks angeboten, darunter waren viele hochkarätige Sprecher aus relevanten Bereichen der IT. Zusammen\nmit einer Messe mit knapp 70 Ausstellern ist die OOP damit eine der größten unabhängigen Fachkonferenzen für Software\nArchitektur in Europa.",[439,16389,16390,16391,16394,16395,16402],{},"Von daher war es uns eine große Freude, im Rahmen des Tracks ",[448,16392,16393],{},"Modern Architecture – Known & Unknown","auf der OOP 2020\neine Nachtschule geben zu dürfen, in der wir vor fast vollem Raume mit interessierten Teilnehmern über ein uns sehr am\nHerz liegendes Thema diskutieren konnten:\n",[990,16396,16397],{},[1002,16398,16401],{"href":16399,"rel":16400},"https://www.oop-konferenz.de/oop2020/programm/konferenzprogramm/sessiondetails/action/detail/session/ndo-1-3/title/systemtheorie-und-softwarearchitektur-auf-der-suche-nach-unbekannten-kontexten.html",[1006],"Systemtheorie und Softwarearchitektur – Auf der Suche nach unbekannten Kontexten",".\nTrotz bahnbedingt etwas holpriger Anreise nach München hatten wir vorher aber glücklicher Weise noch etwas Zeit, selbst\nnoch ein paar sehr gute Vorträge zu hören. Für uns als Sprecher war am Freitag dann auch die Teilnahme an den Workshops\nfrei. 😉",[1065,16404,16406],{"id":16405},"keynote-von-hannah-fry","Keynote von Hannah Fry",[439,16408,16409,16410],{},"Hannah Fry ist Professorin am University College London und beschäftigt sich als Mathematikern mit sozialen\nFragestellungen. In der Keynote ging sie auf amüsante Art und Weise der Fragestellung nach, wie vertrauensürdig\nmaschinelle Lernalgorithmen sind. Vom Anlernen von Tauben zur Melanomerkennung bis hin zu Algorithmen, die rosa Schafe\nals Blumen erkennen diskutierte sie die Glaubwürdigkeit maschinell angelernter Algorithmen. Auch auf moralische\nFragestellungen und Probleme bei der Erkennung terroristischer Aktivitäten ging sie dabei ein, immer wieder unter\nEinbeziehung des Auditoriums. So diskutierte sie beispielsweise Vor- und Nachteile von Algorithmen beim Fällen von\nGerichtsurteilen, oder suchte mit ihrem Publikum nach dem richtigen Wort um Menschen von Maschinen zu unterscheiden.\nGenaueres kann man auch in Frys zahlreichen Büchern nachlesen, unter anderem ",[1002,16411,16414],{"href":16412,"rel":16413},"https://www.chbeck.de/fry-hello-world/product/26909221",[1006],[990,16415,16416],{},"Hello World – Being Humanin the Age of\nAlgorithms.",[1065,16418,16420],{"id":16419},"session-von-gunter-dueck","Session von Gunter Dueck",[439,16422,16423,16424,16427,16428,16431,16432,16435,16436],{},"Gunter Dueck aka ",[990,16425,16426],{},"Wild Dueck","(@wilddueck) zuzuhören, ist immer ein Erlebnis und seit langem ist er fest im Programm der\nOOP verankert. Er sprach über ",[448,16429,16430],{},"Lähmungen im Angesicht des Unbekannten",", von Digitalisierung und der McDonaldisierung\ndes Alten. Den ",[448,16433,16434],{},"Fat Smoker","nahm er als Sinnbild für den Zustand in größeren Unternehmen: Man weiß, woran es krankt,\nweiß was man tun muß um zu gesunden und dennoch geht es weiter wie gehabt. Dueck erzählte dabei auf gewohnt amüsante\nWeise Anekdoten aus seinem privaten und seinem Arbeitsleben, um zu motivieren: Es geht um Lernen, Üben und Explorieren,\nnicht um Erreichen und Messen. Thematisiert wird diese kulturelle Umstand auch in seinem neuen Buch ",[1002,16437,16440],{"href":16438,"rel":16439},"https://www.campus.de/buecher-campus-verlag/wirtschaft-gesellschaft/wirtschaft/heute_schon_einen_prozess_optimiert-15859.html",[1006],[990,16441,16442],{},"Heute schon einen\nProzess optimiert?: Das Management frisst seine\nMitarbeiter.",[1065,16444,16446],{"id":16445},"nachtschule-von-uns","Nachtschule von uns 😉",[439,16448,16449,16450,16453,16454,16457,16458,16465,16466,16473],{},"Abends um 18:30h durften wir dann unsere Nachtschule halten. Trotz der fortgeschrittenen Zeit und nach einem für einige\nTeilnehmer doch schon sehr langen Tagungstag, waren recht viele und erfreulicherweise sehr diskussionsfreudige\nTeilnehmer erschienen. Es gab während unserer Präsentation als auch im Nachgang viele gute Kommentare, Nachfragen und\nauch Anregungen, so, dass wir erst weit nach dem offiziellen Ende der Nachtschule das Tagungsgebäude verlassen haben.\nDas Interesse, doch recht schwierige Themen wie ",[448,16451,16452],{},"Konstruktivismus","und ",[448,16455,16456],{},"Systemtheorie nach Niklas Luhmann","mit\nSoftwareentwicklung und -architektur zu verbinden, freut uns sehr. Es bestätigt uns auch in unserer Überzeugung, dass\ndies wichtige Themen sind. Hier noch einmal vielen herzlichen Dank an alle, die sich die Zeit genommen haben so\nausführlich mit uns zu sprechen! Wer ein bisschen was dazu lesen möchte, darf sich gerne noch unseren Artikel in der\n",[990,16459,16460],{},[1002,16461,16464],{"href":16462,"rel":16463},"https://www.informatik-aktuell.de/management-und-recht/projektmanagement/systemisch-agile-sofwareentwicklung.html",[1006],"Informatik Aktuell","\nzu Gemüte führen, oder auch eine Aufnahme eines ähnlichen Vortrags von uns auf der ",[1002,16467,16470],{"href":16468,"rel":16469},"https://www.youtube.com/watch?v=FAA31oVBrBI",[1006],[990,16471,16472],{},"JUG\nKarlsruhe"," im Dezember 2019 ansehen.",[439,16475,16476],{},"Der Freitag war vertiefenden Workshops gewidmet.",[1065,16478,16480],{"id":16479},"workshop-dave-farley","Workshop Dave Farley",[439,16482,16483,16484,16487,16488,16495,16496,16503],{},"Dave Farley von Continuous Delivery Ltd. gab einen Ganztagesworkshop zum Thema ",[448,16485,16486],{},"Continuous Delivery Advanced Deployment\nPipelines",". Als Mitautor des Buches ",[1002,16489,16492],{"href":16490,"rel":16491},"https://www.amazon.de/Continuous-Delivery-Deployment-Automation-Addison-Wesley/dp/0321601912",[1006],[990,16493,16494],{},"Continuous Delivery: Reliable Software Releases Through Build, Test, and\nDeployment Automation","\nberichtete er ausgiebig aus seinem großen Erfahrungsfundus. Der Workshop bot zwar keine Hands-on Übungen am Rechner an,\nwar aber geprägt von ausführlichen und sehr tief gehenden Diskussionen, in denen Farley die Vorteile und Notwendigkeiten\nder Continuous Delivery Philosophie herausstellte. Als ehemaliger Mitarbeiter von Thoughtworks verwies er auch immer\nwieder auf die Studien von ",[1002,16497,16500],{"href":16498,"rel":16499},"https://www.amazon.de/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339/ref=pd_sbs_14_t_2/260-3252525-6073864?_encoding=UTF8&pd_rd_i=1942788339&pd_rd_r=a9c6abf8-8070-4274-a5e0-129f2e3d59cb&pd_rd_w=rVM4V&pd_rd_wg=66liA&pf_rd_p=a2f6bca6-dcb1-4822-8e28-66b64b37970e&pf_rd_r=TDW4AGDXGE1G6JR970TH&psc=1&refRID=TDW4AGDXGE1G6JR970TH",[1006],[990,16501,16502],{},"Accelerate","\nworin Vorteile der DevOps-Kultur und Continous Delivery auch wissenschaftlich strikt untermauert sind (aus unserer\nSicht eine klare Leseempfehlung!). Farley betonte immer wieder, dass aus seiner Sicht die zwei schwierigsten Probleme\nder Informatik Nebenläufigkeit (Concurrency) und Kopplung (Coupling) seien; mit CD werde das Zweite adressiert.",[439,16505,16506,16507],{},"Als Ingenieur ist es essentiell schnell zu lernen, ob die gewählten Ansätze tragen. Dazu dienen kurze Feedbackzyklen,\ndie Ideen der CD sind hierbei die Grundphilosophie, die entstehenden Pipelines die Werkzeuge (weshalb konkrete Tools\nauch nur am Rande besprochen wurden). Schnelle Pipelines und kurze Wege in die Produktion können die Schlüsselelemente\nerfolgreicher Projekte mit gesunden Mitarbeitern sein. Eine schöne Empfehlung zur Motivation der testgetriebenen\nEntwicklung ist das Tutorial dazu von J. B. Rainsberger (@jbrains) namens ",[1002,16508,16511],{"href":16509,"rel":16510},"https://online-training.jbrains.ca/p/wbitdd-01",[1006],[990,16512,16513],{},"TDD Done\nRight.",[439,16515,16516],{},"Alles in allem waren es zwei sehr gelungene, wenn auch anstrengende Tage in München. Gerne kommen wir 2020 wieder, wenn\nsich die OOP zum 30ten Male jährt. 😉",{"title":469,"searchDepth":507,"depth":507,"links":16518},[16519,16520,16521,16522],{"id":16405,"depth":547,"text":16406},{"id":16419,"depth":547,"text":16420},{"id":16445,"depth":547,"text":16446},{"id":16479,"depth":547,"text":16480},[1031],"2020-02-17T11:51:16","Vom 03. bis zum 07. Februar fand in München zum bereits 29ten Male die OOP statt, eine jährlich von SIGS DATACOM\\norganisierte Fachtagung für Software-Architektur. Unter dem Motto business meets softwarefanden sich gut 2400\\nTeilnehmer an der Messe in München ein, um in regen Austausch zu treten und sich fortzubilden. Insgesamt wurden 170\\nVorträge in 13 Tracks angeboten, darunter waren viele hochkarätige Sprecher aus relevanten Bereichen der IT. Zusammen\\nmit einer Messe mit knapp 70 Ausstellern ist die OOP damit eine der größten unabhängigen Fachkonferenzen für Software\\nArchitektur in Europa.","https://synyx.de/blog/die-oop-2020-in-muenchen/",{},"/blog/die-oop-2020-in-muenchen",{"title":16374,"description":16530},"Vom 03. bis zum 07. Februar fand in München zum bereits 29ten Male die OOP statt, eine jährlich von SIGS DATACOM\norganisierte Fachtagung für Software-Architektur. Unter dem Motto business meets softwarefanden sich gut 2400\nTeilnehmer an der Messe in München ein, um in regen Austausch zu treten und sich fortzubilden. Insgesamt wurden 170\nVorträge in 13 Tracks angeboten, darunter waren viele hochkarätige Sprecher aus relevanten Bereichen der IT. Zusammen\nmit einer Messe mit knapp 70 Ausstellern ist die OOP damit eine der größten unabhängigen Fachkonferenzen für Software\nArchitektur in Europa.","die-oop-2020-in-muenchen","blog/die-oop-2020-in-muenchen",[9546,8276,16534],"dddesign","Vom 03. bis zum 07. Februar fand in München zum bereits 29ten Male die OOP statt, eine jährlich von SIGS DATACOM organisierte Fachtagung für Software-Architektur. Unter dem Motto 'business meets software' fanden sich gut 2400 Teilnehmer an der Messe in München ein, um in regen Austausch zu treten und sich fortzubilden.","uU-2f7bAtqDIPtnTxLniFCBDVc3G8CPQvwHobE6BYOo",{"id":16538,"title":16539,"author":16540,"body":16541,"category":16631,"date":16632,"description":16633,"extension":1034,"link":16634,"meta":16635,"navigation":916,"path":16636,"seo":16637,"slug":16545,"stem":16638,"tags":16639,"teaser":16642,"__hash__":16643},"blog/blog/kandddinsky-2019-in-berlin.md","KanDDDinsky 2019 in Berlin",[33,133,45],{"type":432,"value":16542,"toc":16629},[16543,16546,16549,16554,16561,16566,16569,16572,16575,16580,16594,16599,16602,16607,16610,16615,16618,16623,16626],[435,16544,16539],{"id":16545},"kandddinsky-2019-in-berlin",[439,16547,16548],{},"Die KanDDDinsky Konferenz in Berlin fand 2019 in einem Hotel nahe dem Alexanderplatz statt. Ein Tag vor der eigentlichen\nKonferenz gab es zusätzlich einen Workshoptag der DDD Europe. Hier wurden drei tagesfüllende Workshops angeboten, bei\ndenen tiefer und in kleinen Gruppen auf DDD Themen eingegangen wurde. Die Konferenz selbst bestand zum einen Teil aus\neinstündigen Vorträgen, als auch aus zweistündigen “Hands-on” Workshops. Dazwischen gab es in den Kaffee Pausen\nreichlich Snacks sowie Abend- und Mittagessen. Mit ca. 200 Teilnehmern war alles recht übersichtlich und nicht\nüberfüllt. Die Konferenzsäle befanden sich direkt im Hotel, sodass wir nach dem Frühstück gleich starten konnten.\nNachfolgend findet ihr Erfahrungsberichte zu den einzelnen Workshops und Vorträgen, welche uns in guter Erinnerung\ngeblieben sind sowie ein abschließendes Fazit.",[439,16550,16551],{},[448,16552,16553],{},"Workshop: Thomas Coopman — Event Storming",[439,16555,16556,16557,16560],{},"Der DDD Workshop “EventStorming Introduction” von Thomas Coopman fand am ersten Tag in Kollaboration mit DDD Europe\nstatt. Das Ziel des Workshops EventStorming Introduction war es, den Teilnehmern die Grundlagen von Eventstorming näher\nzu bringen und ihnen zu zeigen, wie sie die Methodik im eigenen Unternehmen einführen können. Dazu haben sich die\nTeilnehmer nach einer kurzen Vorstellung der Problemdomäne am Prozess selbst versucht, unter ständiger Anleitung von\nThomas Coopman. Die Problemdomäne bestand aus einem kurzfristigen Autoverleih (vergleichbar mit den E-Scootern in\nmanchen Städten). Hierzu wurde zunächst klassisch das ",[990,16558,16559],{},"Big Picture"," erarbeitet und das Modell später Schritt für Schritt\nverfeinert und ergänzt. Anschließend wurde der Happy Path im “explicit” und im “reverse” Walkthrough explizit\ndurchgespielt. Interessant war, dass Coopman Brandolinis Prozess angepasst und eine eigene Sprache entwickelt hat:\nBereits während des Big Pictures wurde nicht mit Aggregates und Repositories, sondern mit Commands, die Events\nverursachen, und Rules, die greifen, gearbeitet. Dieser Ansatz hat dabei geholfen User Stories und Acceptance-Tests\nschneller zu finden und Example Mapping effizienter durchführen zu können. Um die Einführung in den Unternehmen zu\nvereinfachen, hat Coopman ständig interveniert und wertvolle Tipps aus seiner umfangreichen Erfahrung mitgegeben, sei es\nfür Teilnehmer (“Domänenexperten sind da um ausgefragt zu werden”) oder für Moderatoren (“mind. 1m Papier/Person”,\n“keine Super-Stickys kaufen”). Im Workshop konnte Thomas viele Kniffe an uns weitergeben — es wäre interessant, an\neinem weiteren Tag zu sehen, wie Coopman den Prozess abgeschlossen hätte und beim Sprung zum Domänenmodell vorgegangen\nwäre.",[439,16562,16563],{},[448,16564,16565],{},"Workshop: Marco Heimeshoff, Roman Sachse — Why are words, how do they mean?",[439,16567,16568],{},"Einer der “Hands on”-Workshops an den beiden Konferenztagen hieß: “Why are words, how do they mean?” und wurde von\nMarco Heimeshoff und Roman Sachse moderiert. Aus der Beschreibung war nur zu erkennen, dass es sich irgendwie um\nDomain-Driven Design (DDD) drehen musste. Ich erhoffte mir einen praktischen Workshop mit geringem Theorieanteil und\nnach einer kurzen Einführung ging es direkt mit dem praktischen Teil los. Aufgabenstellung war, die Domäne für ein Hotel\nUnternehmen zu entwickeln. Der Raum wurde in mehrere kleine Gruppen aufgeteilt, von denen jede eine Methode des Event\nModeling auf einen Teilbereich der Hotelbranche anwenden durfte (beispielsweise Event Storming oder Domain Story\nTelling). Meine Gruppe wählte den Themenbereich “Verwaltung der Hotelzimmer”. Dabei ging es z.B. darum, wie ein\nKinderbett auf Anfrage des Kunden auf das Hotelzimmer gelangt oder wie die Räume gereinigt werden. Mit Hilfe des Domain\nStory Telling bildeten wir einfache Sätze, welche aus Actors, Activities und Work Objects bestanden — wodurch sich schon\nbald eine Vielzahl von vernetzten Arbeitsabläufen bildete. Hier ließ sich zum einen schon eine Sprache erkennen, zum\nanderen Stellen, welche schwerer zu modellieren waren als andere.",[439,16570,16571],{},"Nach ungefähr 45 Minuten begann der zweite Teil des Workshops. Hier ging es darum die diskutierte Domäne in Programmcode\numzusetzen. Einer der Mentoren bediente die Tastatur und der Raum mit den Teilnehmern konnte bestimmen, in welche\nRichtung entwickelt wurde. Wir begannen einen Teilbereich zu implementieren der zuvor von einer der Gruppen modelliert\nwurde. Die Mentoren sind dabei dynamisch auf Anmerkungen oder Fragen aus dem Teilnehmerkreis eingegangen. Kam eine\nProblemstellung auf, wurde die Lösung direkt erklärt und beispielhaft umgesetzt.",[439,16573,16574],{},"Im ersten Teil des Workshops konnten die Teilnehmer eine Form des Event Modeling mitgestalten und sich in die Domäne\nhineinversetzen. Der zweite Teil hat abstrakte Konzepte des DDD anschaulich erklärt und praktisch umgesetzt. Man konnte\nden Inhalt mitgestalten und sich aktiv beteiligen. Leider dauerte der Workshop lediglich zwei Stunden und die Mentoren\nkonnten nicht auf alle Modelle der einzelnen Teams eingehen. Alles in allem war es ein gelungener Workshop, der sicher\nauch in einem längeren Format gut gelungen wäre.",[439,16576,16577],{},[448,16578,16579],{},"Vortrag: Roman Sachse — Is Maybe an Option",[439,16581,16582,16583,16586,16587,2040,16590,16593],{},"Im Vortrag “Is Maybe an Option” hat sich Roman Sachse an ein grundlegendes Problem gewagt, das in DDD meist\nstillschweigend umgangen wird: ",[990,16584,16585],{},"Nullability im Domain Model."," Sei es die Abwesenheit eines Datenbank-Feldes, der\nRückgabewert einer Funktion, die etwas nicht parse-bares parsen soll oder dass Felder wegen des objektrelationalen\nMappings leer gelassen werden müssen — Sachse hat zu vielen Problemstellungen aus der echten Welt anhand kurzer\nF#-Schnipsel erklärt, mit welchen Strategien sie angegangen werden können. Dabei ist er stark auf die Datentypen\n",[990,16588,16589],{},"Option",[990,16591,16592],{},"Maybe"," eingegangen. Auch wenn sich die Erkenntnisse nicht eins zu eins auf ein Java- oder .NET-Projekt\nübertragen lassen, hat Roman Sachse mit Ansätzen, die sich auch mit Generics und Optionals umsetzen lassen, neue\nDenkanstöße gegeben.",[439,16595,16596],{},[448,16597,16598],{},"Vortrag: Dennis Doomen — A practical introduction to DDD, CQRS…",[439,16600,16601],{},"Domain-Driven Design, Command-Query-Responsibility-Segregation und Event Sourcing sind Paradigmen, die hervorragend\nzusammenpassen und meist gemeinschaftlich in einem Projekt eingeführt werden. In seinem Vortrag “A practical\nintroduction to DDD, CQRS and Event Sourcing” ist Dennis Doomen zunächst in einem kleinen Rundumschlag auf die Themen\neingegangen und hat sich dann an “echten” Problemstellungen abgearbeitet: “Was passiert, wenn ich das ReadModel anpassen\nmuss?” “Wie trenne ich die Komponenten sauber?” “Was sind Alternativen, durch die ich bestimmte Komponenten ersetzen\nkann?” Der Vortrag hat nicht nur Grundlagen für Einsteiger geliefert, sondern auch Tipps für übliche Probleme. Die\nFolien sind sicherlich eine gute Grundlage für die Verwendung der Paradigmen in großen, änderungsaffinen Projekten",[439,16603,16604],{},[448,16605,16606],{},"Vortrag: Michael Plöd — Pitching DDD to the management",[439,16608,16609],{},"In den “C-level”-Management-Ebenen wird DDD immer noch oft als esoterischer, teurer Quatsch abgetan — zu versuchen,\nmit Event Storming o.ä. zu punkten, hilft oft nicht. Doch viele der Punkte, die für das Management aktuell wichtig sind,\nsei es beispielsweise Agilität, finden sich in den Ansätzen von Domain-Driven Design wieder. Michael Plöd hat in seinem\nVortrag analytisch dargelegt, wie genau das Paradigma helfen kann, die Ziele der Manager zu erreichen — und dabei Talent\ndarin bewiesen, Management und Entwicklung auf einen gemeinsamen Nenner zu bringen, Argumentationsgrundlagen zu finden\nund mit kleinen Tricks ans Ziel zu kommen. Obwohl viele Ideen aus dem Vortrag analog oder leicht angepasst im nächsten\nMeeting verwendet werden können, ging es um mehr, nämlich um die Denkweise, mit der die unterschiedlichen Sichtweisen\nund Ziele der leitenden und ausführenden Teile zusammengeführt werden können.",[439,16611,16612],{},[448,16613,16614],{},"Vortrag: Philipp Krenn — Building Distributeted Systems in Distributed Team",[439,16616,16617],{},"Im Vortrag “Building Distributeted Systems in Distributed Team” von Philipp Krenn ging es um die verteilte Arbeit bei\nElastic. Das klassische Büro ist dort eher der Ausnahmefall, die Mitarbeiter sind weltweit verteilt und arbeiten im\nwesentlichen von zu Hause aus. Es wurde aufgezeigt wie dies funktioniert, welche Vorteile es mit sich bringt (wie z.B.\nhohe Flexibilität), aber auch welche Nachteile (z.B. die Remote-Releaseparty fällt eher kleiner aus). Wichtigste\nErkenntnis war für mich, dass es einfacher ist, wenn das gesamte Team verteilt ist, im Gegensatz zu einzelnen,\nverteilten Teammitgliedern (oder an einzelnen Tagen verteilten). Dies deckt sich auch mit der Erfahrung aus eigenen\nProjekten, wo viele klassische Arbeitsplätze im Büro haben und einzelne remote arbeiten.",[439,16619,16620],{},[448,16621,16622],{},"PARTY SESSION — So You Want to be a Rockstar Developer?",[439,16624,16625],{},"Die Party Session von Dylan Beattie fand am ersten Abend der Konferenz Tage statt. Zum Spaß entwickelte er eine\nProgrammiersprache, die aus Songtexten bestand und veröffentlichte eine Spezifikation auf Github. Er zeigte sehr\nunterhaltsam, wie sich das Projekt in der Open Source Community verselbstständigte. Bald gab es nicht nur die\nSpezifikation, sondern auch die Möglichkeit Programme in „Rockstar“ zu schreiben. Mit humorvollen Beispielen und einer\nGitarreneinlage bildete der Vortrag den perfekten Abschluss des ersten Tages.",[439,16627,16628],{},"Die Kandddinsky ist eine eher kleine Konferenz. Die Vortragssäle sind nicht überfüllt und auch in den Workshops lässt es\nsich gut mitarbeiten. Die Speaker sind Teilnehmer der Konferenz und stehen in den Pausen für Fragen und Diskussionen zur\nVerfügung. Es ist nicht unwahrscheinlich einen der Speaker als Teilnehmer eines Workshops oder Vortrags anzutreffen. Der\nInhalt der Vorträge war oft wenig technisch und auch der Anteil an DDD Themen war weniger als zuvor gedacht. Für DDD\nNeulinge wäre auch eine Einführungsveranstaltung zu Beginn der Konferenz hilfreich gewesen. Für unsere Workshopbesucher\nhat sich der zusätzliche Tag in Berlin gelohnt. Die Sessions mit kleinem Publikum haben konzentriert Wissen vermittelt\nund neue Impulse gesetzt.",{"title":469,"searchDepth":507,"depth":507,"links":16630},[],[1030,8266,1031],"2019-12-13T13:56:04","Die KanDDDinsky Konferenz in Berlin fand 2019 in einem Hotel nahe dem Alexanderplatz statt. Ein Tag vor der eigentlichen\\nKonferenz gab es zusätzlich einen Workshoptag der DDD Europe. Hier wurden drei tagesfüllende Workshops angeboten, bei\\ndenen tiefer und in kleinen Gruppen auf DDD Themen eingegangen wurde. Die Konferenz selbst bestand zum einen Teil aus\\neinstündigen Vorträgen, als auch aus zweistündigen “Hands-on” Workshops. Dazwischen gab es in den Kaffee Pausen\\nreichlich Snacks sowie Abend- und Mittagessen. Mit ca. 200 Teilnehmern war alles recht übersichtlich und nicht\\nüberfüllt. Die Konferenzsäle befanden sich direkt im Hotel, sodass wir nach dem Frühstück gleich starten konnten.\\nNachfolgend findet ihr Erfahrungsberichte zu den einzelnen Workshops und Vorträgen, welche uns in guter Erinnerung\\ngeblieben sind sowie ein abschließendes Fazit.","https://synyx.de/blog/kandddinsky-2019-in-berlin/",{},"/blog/kandddinsky-2019-in-berlin",{"title":16539,"description":16548},"blog/kandddinsky-2019-in-berlin",[16640,8276,8409,15546,16641,9556],"berlin","domain-driven-development","Erfahrungsbericht der KanDDDinsky Konferenz 2019","LkddH4oMqPym1394NkfmhIu5bWj7Mx9y4njBxfheu0A",{"id":16645,"title":16646,"author":16647,"body":16648,"category":17096,"date":17097,"description":17098,"extension":1034,"link":17099,"meta":17100,"navigation":916,"path":17101,"seo":17102,"slug":17103,"stem":17104,"tags":17105,"teaser":17106,"__hash__":17107},"blog/blog/poketime-gets-a-makeover-at-the-code-clininc.md","PokeTime gets a makeover at the Code Clinic",[154],{"type":432,"value":16649,"toc":17088},[16650,16653,16656,16659,16662,16666,16669,16672,16676,16679,16682,16732,16813,16817,16820,16869,16876,16880,16883,16890,16893,16904,16916,16919,16923,16930,16937,17062,17071,17076,17080,17083,17086],[435,16651,16646],{"id":16652},"poketime-gets-a-makeover-at-the-code-clinic",[439,16654,16655],{},"In the planning phase for our last Devoxx4Kids Event in Karlsruhe we decided to pick up the awesome PokeTime workshop by\nCassandra and Stephen Chin. I was a mentor for this workshop at JCrete4Kids this year and really liked the mixture of\nhardware tinkering, building software snippets and playing the finished game in the end.",[439,16657,16658],{},"A few weeks after the initial planning I cloned the code from Github and tried to run it on my Raspberry Pi. I was not\nable to compile it without further preparation and probably some support from Cassandra. 🙁",[439,16660,16661],{},"Being a somewhat experienced breaker and builder of things I thought that I could make it on my own. 😉 This post tells\nabout the different steps that lead to a successful PokeTime workshop in Karlsruhe.",[3938,16663,16665],{"id":16664},"build-environment","Build environment",[439,16667,16668],{},"The root cause for the failing build was the lack of the right build environment. I am sure that on the SD cards that we\nused at the JCrete4Kids event all the needed libs were in place. Since I did not have an image of those cards I was kind\nof lost.",[439,16670,16671],{},"The existing build was done with Ant. Since my time with Ant is long ago and I did not want to go back there I created a\nvanilla pom.xml descriptor for a Maven build and started my first build to see what was missing.",[3938,16673,16675],{"id":16674},"openjdk-and-javafx","OpenJDK and JavaFX",[439,16677,16678],{},"The PokeTime application is a game UI that runs with keyboard controls as well as sensor input from Raspberry Pi\nsensors. So the main (external) dependencies are JavaFX and Pi4J. My gut feeling told me that JavaFX would be the harder\npart. Indeed, JavaFX is only included in Oracle JDK 8 and not in OpenJDK. After JDK 8 it has been totally removed and\nhanded over to the the OpenFJX project and made available under GPL license.",[439,16680,16681],{},"Since I was moving parts anyways I switched from JDK 8 to JDK 11 and introduced the OpenJFX dependencies in my Maven\ndescriptor:",[464,16683,16685],{"className":6253,"code":16684,"language":6255,"meta":469,"style":469}," \u003Cproperties>\n \u003Cproject.build.sourceEncoding>UTF-8\u003C/project.build.sourceEncoding>\n \u003Cjavafx.version>11\u003C/javafx.version>\n \u003Cjava.version>11\u003C/java.version>\n \u003Cpi4j.version>1.1\u003C/pi4j.version>\n \u003Cmaven.compiler.source>${java.version}\u003C/maven.compiler.source>\n \u003Cmaven.compiler.target>${java.version}\u003C/maven.compiler.target>\n \u003Cmaven.compiler.release>${java.version}\u003C/maven.compiler.release>\n \u003C/properties>\n",[471,16686,16687,16692,16697,16702,16707,16712,16717,16722,16727],{"__ignoreMap":469},[474,16688,16689],{"class":476,"line":477},[474,16690,16691],{}," \u003Cproperties>\n",[474,16693,16694],{"class":476,"line":507},[474,16695,16696],{}," \u003Cproject.build.sourceEncoding>UTF-8\u003C/project.build.sourceEncoding>\n",[474,16698,16699],{"class":476,"line":547},[474,16700,16701],{}," \u003Cjavafx.version>11\u003C/javafx.version>\n",[474,16703,16704],{"class":476,"line":584},[474,16705,16706],{}," \u003Cjava.version>11\u003C/java.version>\n",[474,16708,16709],{"class":476,"line":607},[474,16710,16711],{}," \u003Cpi4j.version>1.1\u003C/pi4j.version>\n",[474,16713,16714],{"class":476,"line":642},[474,16715,16716],{}," \u003Cmaven.compiler.source>${java.version}\u003C/maven.compiler.source>\n",[474,16718,16719],{"class":476,"line":663},[474,16720,16721],{}," \u003Cmaven.compiler.target>${java.version}\u003C/maven.compiler.target>\n",[474,16723,16724],{"class":476,"line":694},[474,16725,16726],{}," \u003Cmaven.compiler.release>${java.version}\u003C/maven.compiler.release>\n",[474,16728,16729],{"class":476,"line":700},[474,16730,16731],{}," \u003C/properties>\n",[464,16733,16735],{"className":6253,"code":16734,"language":6255,"meta":469,"style":469}," \u003Cdependency>\n \u003CgroupId>org.openjfx\u003C/groupId>\n \u003CartifactId>javafx-controls\u003C/artifactId>\n \u003Cversion>${javafx.version}\u003C/version>\n \u003Cclassifier>linux\u003C/classifier>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.openjfx\u003C/groupId>\n \u003CartifactId>javafx-fxml\u003C/artifactId>\n \u003Cversion>${javafx.version}\u003C/version>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.openjfx\u003C/groupId>\n \u003CartifactId>javafx-graphics\u003C/artifactId>\n \u003Cversion>${javafx.version}\u003C/version>\n \u003Cclassifier>linux\u003C/classifier>\n \u003C/dependency>\n",[471,16736,16737,16741,16746,16751,16756,16761,16765,16769,16773,16778,16783,16787,16791,16796,16801,16805,16809],{"__ignoreMap":469},[474,16738,16739],{"class":476,"line":477},[474,16740,6272],{},[474,16742,16743],{"class":476,"line":507},[474,16744,16745],{}," \u003CgroupId>org.openjfx\u003C/groupId>\n",[474,16747,16748],{"class":476,"line":547},[474,16749,16750],{}," \u003CartifactId>javafx-controls\u003C/artifactId>\n",[474,16752,16753],{"class":476,"line":584},[474,16754,16755],{}," \u003Cversion>${javafx.version}\u003C/version>\n",[474,16757,16758],{"class":476,"line":607},[474,16759,16760],{}," \u003Cclassifier>linux\u003C/classifier>\n",[474,16762,16763],{"class":476,"line":642},[474,16764,6292],{},[474,16766,16767],{"class":476,"line":663},[474,16768,6272],{},[474,16770,16771],{"class":476,"line":694},[474,16772,16745],{},[474,16774,16775],{"class":476,"line":700},[474,16776,16777],{}," \u003CartifactId>javafx-fxml\u003C/artifactId>\n",[474,16779,16780],{"class":476,"line":913},[474,16781,16782],{}," \u003Cversion>${javafx.version}\u003C/version>\n",[474,16784,16785],{"class":476,"line":920},[474,16786,6292],{},[474,16788,16789],{"class":476,"line":926},[474,16790,6272],{},[474,16792,16793],{"class":476,"line":932},[474,16794,16795],{}," \u003CgroupId>org.openjfx\u003C/groupId>\n",[474,16797,16798],{"class":476,"line":938},[474,16799,16800],{}," \u003CartifactId>javafx-graphics\u003C/artifactId>\n",[474,16802,16803],{"class":476,"line":944},[474,16804,16755],{},[474,16806,16807],{"class":476,"line":950},[474,16808,16760],{},[474,16810,16811],{"class":476,"line":956},[474,16812,6292],{},[3938,16814,16816],{"id":16815},"pi4j","Pi4J",[439,16818,16819],{},"So, the JavaFX part was sorted out e.g. no more build issues for JavaFX controls. 🙂 Next one was the Pi4J library. This\none is needed to make the GPIO connector of the Raspberry Pi usable in your Java code.",[464,16821,16823],{"className":6253,"code":16822,"language":6255,"meta":469,"style":469}," \u003Cdependency>\n \u003CgroupId>com.pi4j\u003C/groupId>\n \u003CartifactId>pi4j-core\u003C/artifactId>\n \u003Cversion>${pi4j.version}\u003C/version>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>com.pi4j\u003C/groupId>\n \u003CartifactId>pi4j-device\u003C/artifactId>\n \u003Cversion>${pi4j.version}\u003C/version>\n \u003C/dependency>\n",[471,16824,16825,16829,16834,16839,16844,16848,16852,16856,16861,16865],{"__ignoreMap":469},[474,16826,16827],{"class":476,"line":477},[474,16828,6272],{},[474,16830,16831],{"class":476,"line":507},[474,16832,16833],{}," \u003CgroupId>com.pi4j\u003C/groupId>\n",[474,16835,16836],{"class":476,"line":547},[474,16837,16838],{}," \u003CartifactId>pi4j-core\u003C/artifactId>\n",[474,16840,16841],{"class":476,"line":584},[474,16842,16843],{}," \u003Cversion>${pi4j.version}\u003C/version>\n",[474,16845,16846],{"class":476,"line":607},[474,16847,6292],{},[474,16849,16850],{"class":476,"line":642},[474,16851,6272],{},[474,16853,16854],{"class":476,"line":663},[474,16855,16833],{},[474,16857,16858],{"class":476,"line":694},[474,16859,16860],{}," \u003CartifactId>pi4j-device\u003C/artifactId>\n",[474,16862,16863],{"class":476,"line":700},[474,16864,16843],{},[474,16866,16867],{"class":476,"line":913},[474,16868,6292],{},[439,16870,16871,16872,16875],{},"Once the Pi4J dependency was added I was able to start the game using the Maven JavaFX plugin: ",[471,16873,16874],{},"mvn javafx:run",". The\ngame started and I was enthusiastic. Famous last words: “Now I need to check out how this runs on my Raspberry Pi..”",[3938,16877,16879],{"id":16878},"write-once-run-everywhere","Write once run everywhere.. 😉",[439,16881,16882],{},"For development speed and overall comfort reasons I had switched from the Raspberry Pi to my Linux desktop in the\nmeantime. My naive self was still narrating the early Java slogan “write once write everywhere”. However, once I tried\nthe latest version of the code on the Raspberry Pi it failed miserably at run time. Surprise, surprise: most of the code\nin the used libraries is platform dependent.",[439,16884,16885,16886,16889],{},"On my Linux box I did not realize this since the OpenJFX Maven plugin managed the platform aspects of JavaFX and the\nPi4J code was always encapsulated by ",[471,16887,16888],{},"PiSystem.isPiUnix",". This code was just not run on my Linux desktop. 🙂",[439,16891,16892],{},"On the Raspberry Pi however, I got something like",[464,16894,16898],{"className":16895,"code":16896,"language":16897,"meta":469,"style":469},"language-plaintext shiki shiki-themes github-light github-dark","Unable to determine hardware version. I see: Hardware : BCM2835 - expecting BCM2708 or BCM2709.\n","plaintext",[471,16899,16900],{"__ignoreMap":469},[474,16901,16902],{"class":476,"line":477},[474,16903,16896],{},[439,16905,16906,16907,16912,16913,1402],{},"So somehow there was some trouble loading the right native libraries for Pi4J. Of course I was not alone with this\nerror: ",[1002,16908,16911],{"href":16909,"rel":16910},"https://github.com/Pi4J/pi4j/issues/349%5C",[1006],"https://github.com/Pi4J/pi4j/issues/349\\",". And the solution I found after some time of research looked simple.\nJust add this system property to the start of your Java program: ",[471,16914,16915],{},"-Dpi4j.linking=dynamic",[439,16917,16918],{},"However, just add a system property to startup initiated by a Maven plugin is not a trivial thing at all. Since a new\nJVM is forked from the initial Maven call all parameters that you hand over to the Maven call are lost for the JavaFX\napplication. At least I did not find a solution for this.",[3938,16920,16922],{"id":16921},"maven-shade-plugin-and-fat-jars-to-the-rescue","Maven shade plugin and fat jars to the rescue!",[439,16924,16925,16926,16929],{},"After I read the relevant parts of the code of the Open JFX Maven plugin I found no way to specify system properties for\nthe started JavaFX application and gabe up on this approach. In the end there must be a way to produce a jar file\nsuitable for application shipment. I don’t start my IntelliJ Idea (yes, it’s built with JavaFX) with ",[471,16927,16928],{},"mvn javafx:start",".\n😉",[439,16931,16932,16933,16936],{},"I used the Maven shade plugin to create a fat jar of the application and all of it’s dependencies. In order to package\nto right version for the platform-dependent OpenJFX parts I needed to add a ",[471,16934,16935],{},"\u003Cclassifier>linux\u003C/classifier>"," to the\nrelevant dependencies.",[464,16938,16940],{"className":6253,"code":16939,"language":6255,"meta":469,"style":469}," \u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n \u003Cversion>3.2.0\u003C/version>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>package\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>shade\u003C/goal>\n \u003C/goals>\n \u003Cconfiguration>\n \u003CshadedArtifactAttached>true\u003C/shadedArtifactAttached>\n \u003CshadedClassifierName>project-classifier\u003C/shadedClassifierName>\n \u003CoutputFile>shade\\${project.artifactId}.jar\u003C/outputFile>\n \u003Ctransformers>\n \u003Ctransformer implementation=\n \"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n \u003CmainClass>sample.Main\u003C/mainClass>\n \u003C/transformer>\n \u003C/transformers>\n \u003C/configuration>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n",[471,16941,16942,16947,16952,16957,16962,16967,16972,16977,16982,16987,16992,16997,17002,17007,17012,17017,17022,17027,17032,17037,17042,17047,17052,17057],{"__ignoreMap":469},[474,16943,16944],{"class":476,"line":477},[474,16945,16946],{}," \u003Cplugin>\n",[474,16948,16949],{"class":476,"line":507},[474,16950,16951],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[474,16953,16954],{"class":476,"line":547},[474,16955,16956],{}," \u003CartifactId>maven-shade-plugin\u003C/artifactId>\n",[474,16958,16959],{"class":476,"line":584},[474,16960,16961],{}," \u003Cversion>3.2.0\u003C/version>\n",[474,16963,16964],{"class":476,"line":607},[474,16965,16966],{}," \u003Cexecutions>\n",[474,16968,16969],{"class":476,"line":642},[474,16970,16971],{}," \u003Cexecution>\n",[474,16973,16974],{"class":476,"line":663},[474,16975,16976],{}," \u003Cphase>package\u003C/phase>\n",[474,16978,16979],{"class":476,"line":694},[474,16980,16981],{}," \u003Cgoals>\n",[474,16983,16984],{"class":476,"line":700},[474,16985,16986],{}," \u003Cgoal>shade\u003C/goal>\n",[474,16988,16989],{"class":476,"line":913},[474,16990,16991],{}," \u003C/goals>\n",[474,16993,16994],{"class":476,"line":920},[474,16995,16996],{}," \u003Cconfiguration>\n",[474,16998,16999],{"class":476,"line":926},[474,17000,17001],{}," \u003CshadedArtifactAttached>true\u003C/shadedArtifactAttached>\n",[474,17003,17004],{"class":476,"line":932},[474,17005,17006],{}," \u003CshadedClassifierName>project-classifier\u003C/shadedClassifierName>\n",[474,17008,17009],{"class":476,"line":938},[474,17010,17011],{}," \u003CoutputFile>shade\\${project.artifactId}.jar\u003C/outputFile>\n",[474,17013,17014],{"class":476,"line":944},[474,17015,17016],{}," \u003Ctransformers>\n",[474,17018,17019],{"class":476,"line":950},[474,17020,17021],{}," \u003Ctransformer implementation=\n",[474,17023,17024],{"class":476,"line":956},[474,17025,17026],{}," \"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n",[474,17028,17029],{"class":476,"line":962},[474,17030,17031],{}," \u003CmainClass>sample.Main\u003C/mainClass>\n",[474,17033,17034],{"class":476,"line":4876},[474,17035,17036],{}," \u003C/transformer>\n",[474,17038,17039],{"class":476,"line":4888},[474,17040,17041],{}," \u003C/transformers>\n",[474,17043,17044],{"class":476,"line":4900},[474,17045,17046],{}," \u003C/configuration>\n",[474,17048,17049],{"class":476,"line":4913},[474,17050,17051],{}," \u003C/execution>\n",[474,17053,17054],{"class":476,"line":4921},[474,17055,17056],{}," \u003C/executions>\n",[474,17058,17059],{"class":476,"line":4932},[474,17060,17061],{}," \u003C/plugin>\n",[439,17063,17064,17065,17070],{},"The resulting fat jar could then be started via\na",[1002,17066,17069],{"href":17067,"rel":17068},"https://github.com/Devoxx4KidsDE/PokeTime/blob/master/start",[1006],"start script","that included the right parameter for the\nPi4J system property. Finally the PokeTime application started on my Raspberry Pi 2b! 😀",[439,17072,17073],{},[2205,17074],{"alt":469,"src":17075},"https://media.synyx.de/uploads/2019/11/d4k_poketime.jpeg",[3938,17077,17079],{"id":17078},"fun-at-the-devoxx4kids","Fun at the Devoxx4Kids",[439,17081,17082],{},"In the end we had a successful PokeTime workshop at the October 2019 Devoxx4Kids event in Karlsruhe. A huge thank you\ngoes out to Cassandra and Stephen Chin for the initial application and concept as well as to Patrick Helm (my colleague\nfrom synyx) who led the workshop efforts including hardware order, assembly and production of the poke balls! 🙂",[439,17084,17085],{},"Also a huge thank you for all the other organziers and mentors of the Karlsruhe Devoxx4Kids events!",[1024,17087,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":17089},[17090,17091,17092,17093,17094,17095],{"id":16664,"depth":507,"text":16665},{"id":16674,"depth":507,"text":16675},{"id":16815,"depth":507,"text":16816},{"id":16878,"depth":507,"text":16879},{"id":16921,"depth":507,"text":16922},{"id":17078,"depth":507,"text":17079},[1031],"2019-11-08T11:40:03","In the planning phase for our last Devoxx4Kids Event in Karlsruhe we decided to pick up the awesome PokeTime workshop by\\nCassandra and Stephen Chin. I was a mentor for this workshop at JCrete4Kids this year and really liked the mixture of\\nhardware tinkering, building software snippets and playing the finished game in the end.","https://synyx.de/blog/poketime-gets-a-makeover-at-the-code-clininc/",{},"/blog/poketime-gets-a-makeover-at-the-code-clininc",{"title":16646,"description":16655},"poketime-gets-a-makeover-at-the-code-clininc","blog/poketime-gets-a-makeover-at-the-code-clininc",[],"Migrating educational code for Raspberry Pis from Ant and Oracle JDK 8 to Maven and OpenJDK 11","OqPActVnK2HIrvvicV8iiBcbJnZLyOMnZW82GaSmm2A",{"id":17109,"title":17110,"author":17111,"body":17112,"category":17267,"date":17268,"description":17269,"extension":1034,"link":17270,"meta":17271,"navigation":916,"path":17272,"seo":17273,"slug":17116,"stem":17275,"tags":17276,"teaser":17277,"__hash__":17278},"blog/blog/code-with-attitude-part-2.md","Code with Attitude – Part 2",[97],{"type":432,"value":17113,"toc":17260},[17114,17117,17126,17130,17133,17136,17139,17189,17196,17201,17205,17208,17211,17215,17218,17221,17224,17228,17231,17234,17237,17240,17243,17247,17250,17253],[435,17115,17110],{"id":17116},"code-with-attitude-part-2",[439,17118,17119,17120,17125],{},"After talking about the attitude concerning values and synyx’ new claim “Code with\nAttitude” ",[1002,17121,17124],{"href":17122,"rel":17123},"https://synyx.de/blog/2018-11-16-code-with-attitude-part-1",[1006],"some time ago,"," today I want to focus on the\n“attitude” towards the people you interact with.",[3938,17127,17129],{"id":17128},"part-2-people-what-people","Part 2 – People? What people?",[439,17131,17132],{},"When I was a student at the University of Applied Sciences in Karlsruhe my imagination of my future job was a little\nhazy as my mind still exclusively revolved around football, beer and Counter-Strike. “Real life” was still pretty far\naway. The one thing I was pretty sure of was that being a developer is not something “people oriented” like psychology\nor being a nurse. After graduation I was to be placed in a small office at a pretty desk with a nice computer and do the\nsweet hacking all day long.",[439,17134,17135],{},"Oh boy was I wrong. Years of project work in software development taught me that as a developer you have to deal with\npeople of different kinds every hour of every day.",[439,17137,17138],{},"You have to…",[994,17140,17141,17144,17147,17150,17153,17156,17159,17162,17165,17168,17171,17174,17177,17180,17183,17186],{},[997,17142,17143],{},"pair with your teammates",[997,17145,17146],{},"engineer requirements with your product owner",[997,17148,17149],{},"keep your janitor in a good mood",[997,17151,17152],{},"work out solutions with your team",[997,17154,17155],{},"coach students",[997,17157,17158],{},"get coached by your scrum master",[997,17160,17161],{},"deal with your boss",[997,17163,17164],{},"deal with your bosses’ boss",[997,17166,17167],{},"kiss the sysadmin’s ass",[997,17169,17170],{},"negotiate integration with other teams",[997,17172,17173],{},"chat with the coffee machine maintainer",[997,17175,17176],{},"collaborate with external suppliers",[997,17178,17179],{},"talk to the users about their needs",[997,17181,17182],{},"be creative with designers",[997,17184,17185],{},"interact with the developer community through conferences, user groups, CoPs, open source contributions, Twitter,\nmailing lists, Stackoverflow…",[997,17187,17188],{},"… and many more",[439,17190,17191,17192,17195],{},"Every single one of these humans contributes to the success of your work and this is probably only a small subset of the\ncomplete list of relevant people and interactions. So if your plan is to hide in your nerdhole and just code for\nyourself then you may still be a good coder but you are wasting a lot of potential. The interaction with other humans is\none of the main parts of a software developer’s job and it is not recommendable to half-ass this. ",[448,17193,17194],{},"Software\ndevelopment is a people oriented job!"," This realization is incorporated into our company culture and the way we work at\nsynyx. There are so many aspects of this, of having a “healthy” attitude towards your peers that I have to confine\nmyself on some of the most important ones:",[439,17197,17198],{},[2205,17199],{"alt":469,"src":17200},"https://media.synyx.de/uploads/2019/09/sticker_code-1024x768-768x576.jpg",[3938,17202,17204],{"id":17203},"respect-and-appreciation","Respect and Appreciation",[439,17206,17207],{},"One of the most important things at synyx is that everybody is on equal footing, which creates the most comfortable\nworking atmosphere. Every student, the janitor, every programmer, the cleaning lady, the office girls, our three bosses\nare part of our workforce and are treated with equal respect by each other.",[439,17209,17210],{},"We transfer the same mindset to our project work. A senior developer is not too valuable to do some dirty work and clean\nup hundreds of unit tests or empty the dishwasher from time to time. Students and juniors are integrated into actual\nproject work, if possible. When working with our clients we expect the same thing. To be able to talk openly and on\nequal terms with them without political mumbo-jumbo. To be treated as coworkers and not as freelance codemonkeys or\nexternal consultants from the ivory tower. This is the way we can contribute best to our client’s success. And in my\nopinion it is the only way self-organised, agile teams can really work.",[3938,17212,17214],{"id":17213},"openness-and-honesty","Openness and Honesty",[439,17216,17217],{},"Openness to direct communication is vital. You can help your colleagues best when they have the feeling that they can\nalways talk to you and ask for your help, ideally face to face. Always try to solve tricky problems together and seize\nopportunities to learn from each other. Teams with this mindset are fastest in problem solving and have the best\nknowledge distribution between their members.",[439,17219,17220],{},"In the other direction – don’t build walls. Don’t make yourself inaccessible with always-on-headphones, physical\ndistance or walls, narrow-minded choice of communication channels or artificial bureaucracy. I’ve known teams who only\never talked about requirements when you filled out a 9 page word document for them. Other colleagues put themselves out\nof the game by only accepting communication by email. Of course it is important to maintain focus for yourself but\nhindering communication only leads to your colleagues avoiding you.",[439,17222,17223],{},"Provide honest, regular feedback to your coworkers. Emphasizing their qualities and complimenting their work is equally\nimportant to pointing out problems. Positive feedback and downright thankfulness are a proven way to boost morale and\nwork performance. On the other side – please, please don’t procrastinate uncomfortable conversations! This only makes\nthings worse! I was witness to multiple situations in different teams where the long avoided elephant in the room\nsuddenly blew up and completely destroyed the morale and sometimes the ability to work toghether. Unspoken conflicts are\na project risk!",[3938,17225,17227],{"id":17226},"attitude-towards-yourself","Attitude towards yourself",[439,17229,17230],{},"Essential for sustainable success is a healthy self-assessment. No matter how much experience you have, you always have\nto weigh in the possibility that you are flawed, you can make mistakes, you are not all-knowing and you make wrong\ndecisions. In everything you do you should take this into account.",[439,17232,17233],{},"Don’t make important decisions by yourself. Always let one or more colleagues challenge your ideas. This is no matter of\nstrength or weakness, this is caring about the quality of the result. For example there is no such thing as the perfect\nsoftware architect who can think up everything on his own and always create the best possible solution. Architecture –\nand most other important decisions – should always be a team effort.",[439,17235,17236],{},"Question yourself whenever possible. Is this still the right way to do this? Am I rushing this? What are the downsides\nof my idea? Do I know enough to make this decision? Who could know this better than me? Is this the right way or the\nlazy way to do it? Can I learn something new here? Should I do this alone? What are the implications of my decision? Did\nI handle this situation correctly? How can I improve the way I work? One basic requirement for self improvement is being\nself-critical.",[439,17238,17239],{},"Don’t make yourself indispensable. There are companies out there where this might provide you with some kind of twisted\njob security but it neither makes you a likable colleague nor does it make you a good developer. Share your knowledge!\nEnable your team to do everything you build and learn by themselves! Create comprehensible documentation where\nnecessary. Make everything as intuitively understandable as possible. Use methods like common code ownership, pair\nprogramming, daily standups! Always take care of the truck factor! Everything you can do should also be possible to do\nfor at least two other persons, ideally your whole team. You have done a good job when you can leave your team without\nhaving to worry that anything goes downhill afterwards.",[439,17241,17242],{},"All this does not mean that it is forbidden to be self-confident. Be aware and proud of your capabilities and\nachievements! Sharing them is essential. Do good things and talk about them!",[3938,17244,17246],{"id":17245},"thats-it","Thats it!",[439,17248,17249],{},"Being a good developer requires the willingness to interact with the people around you. All of the things mentioned\nabove don’t require you being loud or an extrovert of some kind. The only prerequisites are an open mindset and active\ncommunication, which is something everybody can achieve. This is an integral part of “Code with Attitude” and something\nthat we actively encourage to the benefit of the people working at synyx and at our clients. Is it successful? Yes! And\nit feels great!",[439,17251,17252],{},"Twitter: @indyarni",[439,17254,17255,17256],{},"Mail: ",[1002,17257,17259],{"href":17258},"mailto:franke@synyx.de","franke@synyx.de",{"title":469,"searchDepth":507,"depth":507,"links":17261},[17262,17263,17264,17265,17266],{"id":17128,"depth":507,"text":17129},{"id":17203,"depth":507,"text":17204},{"id":17213,"depth":507,"text":17214},{"id":17226,"depth":507,"text":17227},{"id":17245,"depth":507,"text":17246},[1031],"2019-10-22T09:44:59","After talking about the attitude concerning values and synyx’ new claim “Code with\\nAttitude” some time ago, today I want to focus on the\\n“attitude” towards the people you interact with.","https://synyx.de/blog/code-with-attitude-part-2/",{},"/blog/code-with-attitude-part-2",{"title":17110,"description":17274},"After talking about the attitude concerning values and synyx’ new claim “Code with\nAttitude” some time ago, today I want to focus on the\n“attitude” towards the people you interact with.","blog/code-with-attitude-part-2",[],"After talking about the attitude concerning values and synyx' new claim 'Code with Attitude' some time ago, today I will focus on the 'attitude' towards the people you interact with.","WkzNfLgxLpnIRap5881FAy2ze9gn15rnA5bNIUxsVwU",{"id":17280,"title":17281,"author":17282,"body":17283,"category":17336,"date":17337,"description":17338,"extension":1034,"link":17339,"meta":17340,"navigation":916,"path":17341,"seo":17342,"slug":17287,"stem":17343,"tags":17344,"teaser":17345,"__hash__":17346},"blog/blog/limit-of-active-devices-during-android-device-tests.md","Limit of active devices during Android device tests",[409],{"type":432,"value":17284,"toc":17334},[17285,17288,17291,17294,17299,17302,17305,17317,17320,17325,17328,17331],[435,17286,17281],{"id":17287},"limit-of-active-devices-during-android-device-tests",[439,17289,17290],{},"Some time ago we decided to expand our pool of Android devices. These are used in our Continuous Integration (CI)\npipeline. But running our tests we noticed a peculiar behaviour: Only 3 devices would execute the test suite while the\nrest would wait. As soon as a device finished one of the waiting devices would proceed.",[439,17292,17293],{},"As a result our device test would run roughly twice as long. We value fast build times and rapid feedback while still\nrunning large test suites. We had to fix this problem.",[439,17295,17296],{},[2205,17297],{"alt":469,"src":17298},"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0ODgiIGhlaWdodD0iMzY3IiB2aWV3Qm94PSItMC41IC0wLjUgNDg4IDM2NyIgY29udGVudD0iJmx0O214ZmlsZSBob3N0PSZxdW90O3d3dy5kcmF3LmlvJnF1b3Q7IG1vZGlmaWVkPSZxdW90OzIwMTktMTAtMDhUMTI6MDI6MjcuNDcxWiZxdW90OyBhZ2VudD0mcXVvdDtNb3ppbGxhLzUuMCAoWDExOyBVYnVudHU7IExpbnV4IHg4Nl82NDsgcnY6NjkuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC82OS4wJnF1b3Q7IGV0YWc9JnF1b3Q7LXFQRHRJbnhRY29aVjRndkZhMTgmcXVvdDsgdmVyc2lvbj0mcXVvdDsxMi4wLjImcXVvdDsgdHlwZT0mcXVvdDtkZXZpY2UmcXVvdDsgcGFnZXM9JnF1b3Q7MSZxdW90OyZndDsmbHQ7ZGlhZ3JhbSBpZD0mcXVvdDtVeDVoZTdQWmJacEtlNEU0NG9KViZxdW90OyBuYW1lPSZxdW90O1BhZ2UtMSZxdW90OyZndDs3WnBmZDlvZ0dNWS9qWmYxR0Vnd3VXeHR0OTEwcCtkMDUyeTcybUVCRGFjeE9NUWErK2xIRFBrTFZydlZtRTF6b3p3aEpMeS9Gd0tQRHVCa25uNFVlQkhkYzBMakFSaVJkQUJ2QndBNDBQWFVSNlpzY3NWMy9WeVlDVVowcFVwNFpDOVVpeU90cmhpaHkwWkZ5WGtzMmFJcGhqeEphQ2diR2hhQ3I1dlZwanh1M25XQlo5UVFIa01jbStwWFJtU2tWUWNGMVlsUGxNMGlmV3NmalBNVGMxeFUxajFaUnBqd2RVMkNkd000RVp6TC9OczhuZEE0QzE0UmwveTZEenZPbGc4bWFDSVB1Y0JMVi9mcmE3eGVmWGJKeS9yWDk5a1BNcjRDZVN2UE9GN3BEdXVIbFpzaUFrc3ArRlBaZVdjQWI4cWVqRlNCNEdWRWlTN2dtTTBTOVQxVXowU0ZFaUk1ajZ1ckZsbVQ4M1NXcGNwd3pzT24xV0s0TGJCd09SUml5KzlHNkNUSTJzdnZQZUV4Rjl1SGdhUHRvYzVNV1J6WDlPbjJVTHJ1RHhXU3Bqc0Q1WlRoVjNsTCtaeEtzVkZWOUFXQkJxWXpGdW5pdXNMditscUxhdVJoVVJIcmxKdVZMVmRVMUJjTjVnMlFvQVVTM01HcGlBbExJaXFZYkVXcVVsWFlDVk1CTWMrY2t2ZVVWMCtrK29pMlIxZFVBUnFpeHVHYmtCRm8xWEdQeEh4c01QOUNsMUlwTktYaFNqS2VYQkxnYlFtZ0pvOTlHUUJkeTdnKzJyQXV1ckIvOHIwd1BXeFFPOEJrNnRpWU92Qm9VSDBEcW5PeVZ5cE93aWdMZTZjSTJqUGtIODJ6MEJuQ3hnR085VzQxVjBDbld3S2RncGZuOWh3UU1nRHRXdnY4bjRCOHJ4bHBwOSs4aXVtMnhzczlKMTdBYWRQb09TLzMwRlZJSDdhQUNVK291ZnZEbUpEMzJ2MEJOeGo2bHZGV0RFYzRHbnJtSW1NTW03aGN5NXJEYTFGSHh5THFYWWcyaUhxdEdSVTVOb1RXZGVLeEdIbm1MSGxoVkdjVVFKTVE2cFRRd2J1elBoQlNPTWc0K0dtM3hpZ0t3L2ZCQk5zdk8yQVpTRjZubUE1d01HbENyak1ydU1ya2tvelRCS0hDSURiZnNnZ1BnMkJjQ045endZZUZjSnRxQ0hscFV5ODlxQjIzNmxxR09CZFRKdk1XUnlPZ3kxbURWMHB3Q3FGcU1DdHNhb1YyYzNubktERmM2eFpDRlFDK0VpRTlJTU1sRmpNcTk3NVF6S1NvUWJjeEx6UkJZeXpaYy9PQmJZbWc3L0RBV1NKZk1lNWFXL2U4bi9vaVVQTy9XKzJVRTd4dXlQTmFEZVZ4TUJyYXBtWFo2Ny9JVk5NWjJER2hYT3llQTVmYUZndXZZN3ZIQ3d5b1oyWDNHSWE0K2RydTFXNEhtZXV1czdKN1FOQnp1d2VaeTY2enNuc2dhTms5UGNkbExyL095dTJCSHZxMzVqL2JiOG05M2RXYzNPMEI0OTY3UGVqaTM3M3FKTURSNmQwZWRISGtYbWRrZWMxMTYvWWc4MWVtSGhQcXlPMHhNQ0hYTnBRNjlYdVF1WXUrcGM4c3BFdURsK3E0YkVWK1g1NXJ5YUNYaFpHRk9MN1dKK2FNa093Mk4rdUlTZnE0d0Z1alphMEladHo0S2lGbEpyd0RoN1p6NGR0bU5OdUVCdDYrN1ZYRjZpK0p1ZlZSL2JFVDN2MEcmbHQ7L2RpYWdyYW0mZ3Q7Jmx0Oy9teGZpbGUmZ3Q7Ij48ZGVmcz48ZmlsdGVyIGlkPSJkcm9wU2hhZG93Ij48ZmVHYXVzc2lhbkJsdXIgaW49IlNvdXJjZUFscGhhIiBzdGREZXZpYXRpb249IjEuNyIgcmVzdWx0PSJibHVyIi8+PGZlT2Zmc2V0IGluPSJibHVyIiBkeD0iMyIgZHk9IjMiIHJlc3VsdD0ib2Zmc2V0Qmx1ciIvPjxmZUZsb29kIGZsb29kLWNvbG9yPSIjM0Q0NTc0IiBmbG9vZC1vcGFjaXR5PSIwLjQiIHJlc3VsdD0ib2Zmc2V0Q29sb3IiLz48ZmVDb21wb3NpdGUgaW49Im9mZnNldENvbG9yIiBpbjI9Im9mZnNldEJsdXIiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJvZmZzZXRCbHVyIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0ib2Zmc2V0Qmx1ciIvPjwvZmlsdGVyPjwvZGVmcz48ZyBmaWx0ZXI9InVybCgjZHJvcFNoYWRvdykiPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSI0ODAiIGhlaWdodD0iMzYwIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDAwMDAiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjI2LjY3IiBoZWlnaHQ9IjYyLjY3IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDAwMDAiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNy41LDI0LjUpIj48c3dpdGNoPjxmb3JlaWduT2JqZWN0IHN0eWxlPSJvdmVyZmxvdzp2aXNpYmxlOyIgcG9pbnRlci1ldmVudHM9ImFsbCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEyIiByZXF1aXJlZEZlYXR1cmVzPSJodHRwOi8vd3d3LnczLm9yZy9UUi9TVkcxMS9mZWF0dXJlI0V4dGVuc2liaWxpdHkiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IGZvbnQtc2l6ZTogMTJweDsgZm9udC1mYW1pbHk6IEhlbHZldGljYTsgY29sb3I6IHJnYigxMDIsIDEwMiwgMTAyKTsgbGluZS1oZWlnaHQ6IDEuMjsgdmVydGljYWwtYWxpZ246IHRvcDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgdGV4dC1hbGlnbjogY2VudGVyOyI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6aW5saW5lLWJsb2NrO3RleHQtYWxpZ246aW5oZXJpdDt0ZXh0LWRlY29yYXRpb246aW5oZXJpdDsiPiM8L2Rpdj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PHRleHQgeD0iNSIgeT0iMTIiIGZpbGw9IiM2NjY2NjYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTJweCIgZm9udC1mYW1pbHk9IkhlbHZldGljYSI+IzwvdGV4dD48L3N3aXRjaD48L2c+PHJlY3QgeD0iMTQwIiB5PSIwIiB3aWR0aD0iMzQwIiBoZWlnaHQ9IjYwIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDAwMDAiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjY0LjUsMjIuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iODkiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aGl0ZS1zcGFjZTogbm93cmFwOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0OyI+VGVzdCBleGVjdXRpb248L2Rpdj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PHRleHQgeD0iNDUiIHk9IjEyIiBmaWxsPSIjNjY2NjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPlRlc3QgZXhlY3V0aW9uPC90ZXh0Pjwvc3dpdGNoPjwvZz48cmVjdCB4PSIwIiB5PSI2MCIgd2lkdGg9IjE0MCIgaGVpZ2h0PSIxMzAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgcG9pbnRlci1ldmVudHM9Im5vbmUiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjUsNzEuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iOCIgaGVpZ2h0PSIxMiIgcmVxdWlyZWRGZWF0dXJlcz0iaHR0cDovL3d3dy53My5vcmcvVFIvU1ZHMTEvZmVhdHVyZSNFeHRlbnNpYmlsaXR5Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBmb250LXNpemU6IDEycHg7IGZvbnQtZmFtaWx5OiBIZWx2ZXRpY2E7IGNvbG9yOiByZ2IoMTAyLCAxMDIsIDEwMik7IGxpbmUtaGVpZ2h0OiAxLjI7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdoaXRlLXNwYWNlOiBub3dyYXA7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OmlubGluZS1ibG9jazt0ZXh0LWFsaWduOmluaGVyaXQ7dGV4dC1kZWNvcmF0aW9uOmluaGVyaXQ7Ij4xPC9kaXY+PC9kaXY+PC9mb3JlaWduT2JqZWN0Pjx0ZXh0IHg9IjQiIHk9IjEyIiBmaWxsPSIjNjY2NjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPjE8L3RleHQ+PC9zd2l0Y2g+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDguNSwxMDIuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iOCIgaGVpZ2h0PSIxMiIgcmVxdWlyZWRGZWF0dXJlcz0iaHR0cDovL3d3dy53My5vcmcvVFIvU1ZHMTEvZmVhdHVyZSNFeHRlbnNpYmlsaXR5Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBmb250LXNpemU6IDEycHg7IGZvbnQtZmFtaWx5OiBIZWx2ZXRpY2E7IGNvbG9yOiByZ2IoMTAyLCAxMDIsIDEwMik7IGxpbmUtaGVpZ2h0OiAxLjI7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdoaXRlLXNwYWNlOiBub3dyYXA7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OmlubGluZS1ibG9jazt0ZXh0LWFsaWduOmluaGVyaXQ7dGV4dC1kZWNvcmF0aW9uOmluaGVyaXQ7Ij4yPC9kaXY+PC9kaXY+PC9mb3JlaWduT2JqZWN0Pjx0ZXh0IHg9IjQiIHk9IjEyIiBmaWxsPSIjNjY2NjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPjI8L3RleHQ+PC9zd2l0Y2g+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDguNSwxMzMuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iOCIgaGVpZ2h0PSIxMiIgcmVxdWlyZWRGZWF0dXJlcz0iaHR0cDovL3d3dy53My5vcmcvVFIvU1ZHMTEvZmVhdHVyZSNFeHRlbnNpYmlsaXR5Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBmb250LXNpemU6IDEycHg7IGZvbnQtZmFtaWx5OiBIZWx2ZXRpY2E7IGNvbG9yOiByZ2IoMTAyLCAxMDIsIDEwMik7IGxpbmUtaGVpZ2h0OiAxLjI7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdoaXRlLXNwYWNlOiBub3dyYXA7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OmlubGluZS1ibG9jazt0ZXh0LWFsaWduOmluaGVyaXQ7dGV4dC1kZWNvcmF0aW9uOmluaGVyaXQ7Ij4zPC9kaXY+PC9kaXY+PC9mb3JlaWduT2JqZWN0Pjx0ZXh0IHg9IjQiIHk9IjEyIiBmaWxsPSIjNjY2NjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPjM8L3RleHQ+PC9zd2l0Y2g+PC9nPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDguNSwxNjUuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iOCIgaGVpZ2h0PSIxMiIgcmVxdWlyZWRGZWF0dXJlcz0iaHR0cDovL3d3dy53My5vcmcvVFIvU1ZHMTEvZmVhdHVyZSNFeHRlbnNpYmlsaXR5Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBmb250LXNpemU6IDEycHg7IGZvbnQtZmFtaWx5OiBIZWx2ZXRpY2E7IGNvbG9yOiByZ2IoMTAyLCAxMDIsIDEwMik7IGxpbmUtaGVpZ2h0OiAxLjI7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdoaXRlLXNwYWNlOiBub3dyYXA7IHRleHQtYWxpZ246IGNlbnRlcjsiPjxkaXYgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGh0bWwiIHN0eWxlPSJkaXNwbGF5OmlubGluZS1ibG9jazt0ZXh0LWFsaWduOmluaGVyaXQ7dGV4dC1kZWNvcmF0aW9uOmluaGVyaXQ7Ij40PC9kaXY+PC9kaXY+PC9mb3JlaWduT2JqZWN0Pjx0ZXh0IHg9IjQiIHk9IjEyIiBmaWxsPSIjNjY2NjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPjQ8L3RleHQ+PC9zd2l0Y2g+PC9nPjxyZWN0IHg9IjE1OS44MyIgeT0iNzAuNSIgd2lkdGg9IjE3My4zMyIgaGVpZ2h0PSIxNS42NyIgZmlsbD0iI2FhZGRmZiIgc3Ryb2tlPSJub25lIiBwb2ludGVyLWV2ZW50cz0ibm9uZSIvPjxyZWN0IHg9IjE2MCIgeT0iMTAxLjUiIHdpZHRoPSIxNDAiIGhlaWdodD0iMTYiIGZpbGw9IiNhYWRkZmYiIHN0cm9rZT0ibm9uZSIgcG9pbnRlci1ldmVudHM9Im5vbmUiLz48cmVjdCB4PSIxNjAiIHk9IjEzMyIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIxNiIgZmlsbD0iI2FhZGRmZiIgc3Ryb2tlPSJub25lIiBwb2ludGVyLWV2ZW50cz0ibm9uZSIvPjxyZWN0IHg9IjMwMCIgeT0iMTY1IiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE2IiBmaWxsPSIjZmZlNmNjIiBzdHJva2U9IiNkNzliMDAiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PHBhdGggZD0iTSAzMDAuMyAxNjQuODEgTCAyOTkuNTggMTE3LjIzIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iMyAzIiBwb2ludGVyLWV2ZW50cz0ibm9uZSIvPjxyZWN0IHg9IjAiIHk9IjIwMCIgd2lkdGg9IjE0MCIgaGVpZ2h0PSIxMzAiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMDAwMCIgcG9pbnRlci1ldmVudHM9Im5vbmUiLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjUsMjExLjUpIj48c3dpdGNoPjxmb3JlaWduT2JqZWN0IHN0eWxlPSJvdmVyZmxvdzp2aXNpYmxlOyIgcG9pbnRlci1ldmVudHM9ImFsbCIgd2lkdGg9IjgiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aGl0ZS1zcGFjZTogbm93cmFwOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0OyI+MTwvZGl2PjwvZGl2PjwvZm9yZWlnbk9iamVjdD48dGV4dCB4PSI0IiB5PSIxMiIgZmlsbD0iIzY2NjY2NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMnB4IiBmb250LWZhbWlseT0iSGVsdmV0aWNhIj4xPC90ZXh0Pjwvc3dpdGNoPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjUsMjQyLjUpIj48c3dpdGNoPjxmb3JlaWduT2JqZWN0IHN0eWxlPSJvdmVyZmxvdzp2aXNpYmxlOyIgcG9pbnRlci1ldmVudHM9ImFsbCIgd2lkdGg9IjgiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aGl0ZS1zcGFjZTogbm93cmFwOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0OyI+MjwvZGl2PjwvZGl2PjwvZm9yZWlnbk9iamVjdD48dGV4dCB4PSI0IiB5PSIxMiIgZmlsbD0iIzY2NjY2NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMnB4IiBmb250LWZhbWlseT0iSGVsdmV0aWNhIj4yPC90ZXh0Pjwvc3dpdGNoPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjUsMjczLjUpIj48c3dpdGNoPjxmb3JlaWduT2JqZWN0IHN0eWxlPSJvdmVyZmxvdzp2aXNpYmxlOyIgcG9pbnRlci1ldmVudHM9ImFsbCIgd2lkdGg9IjgiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aGl0ZS1zcGFjZTogbm93cmFwOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0OyI+MzwvZGl2PjwvZGl2PjwvZm9yZWlnbk9iamVjdD48dGV4dCB4PSI0IiB5PSIxMiIgZmlsbD0iIzY2NjY2NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMnB4IiBmb250LWZhbWlseT0iSGVsdmV0aWNhIj4zPC90ZXh0Pjwvc3dpdGNoPjwvZz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjUsMzA1LjUpIj48c3dpdGNoPjxmb3JlaWduT2JqZWN0IHN0eWxlPSJvdmVyZmxvdzp2aXNpYmxlOyIgcG9pbnRlci1ldmVudHM9ImFsbCIgd2lkdGg9IjgiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDEwMiwgMTAyLCAxMDIpOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aGl0ZS1zcGFjZTogbm93cmFwOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0OyI+NDwvZGl2PjwvZGl2PjwvZm9yZWlnbk9iamVjdD48dGV4dCB4PSI0IiB5PSIxMiIgZmlsbD0iIzY2NjY2NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMnB4IiBmb250LWZhbWlseT0iSGVsdmV0aWNhIj40PC90ZXh0Pjwvc3dpdGNoPjwvZz48cmVjdCB4PSIxNTkuODMiIHk9IjIxMC41IiB3aWR0aD0iMTczLjMzIiBoZWlnaHQ9IjE1LjY3IiBmaWxsPSIjYWFkZGZmIiBzdHJva2U9Im5vbmUiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PHJlY3QgeD0iMTYwIiB5PSIyNDEuNSIgd2lkdGg9IjE0MCIgaGVpZ2h0PSIxNiIgZmlsbD0iI2FhZGRmZiIgc3Ryb2tlPSJub25lIiBwb2ludGVyLWV2ZW50cz0ibm9uZSIvPjxyZWN0IHg9IjE2MCIgeT0iMjczIiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjE2IiBmaWxsPSIjYWFkZGZmIiBzdHJva2U9Im5vbmUiIHBvaW50ZXItZXZlbnRzPSJub25lIi8+PHJlY3QgeD0iMTYwIiB5PSIzMDQuNSIgd2lkdGg9IjE1MCIgaGVpZ2h0PSIxNiIgZmlsbD0iI2ZmZTZjYyIgc3Ryb2tlPSIjZDc5YjAwIiBwb2ludGVyLWV2ZW50cz0ibm9uZSIvPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQ1LjUsMjUuNSkiPjxzd2l0Y2g+PGZvcmVpZ25PYmplY3Qgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7IiBwb2ludGVyLWV2ZW50cz0iYWxsIiB3aWR0aD0iNDgiIGhlaWdodD0iMTIiIHJlcXVpcmVkRmVhdHVyZXM9Imh0dHA6Ly93d3cudzMub3JnL1RSL1NWRzExL2ZlYXR1cmUjRXh0ZW5zaWJpbGl0eSI+PGRpdiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgZm9udC1zaXplOiAxMnB4OyBmb250LWZhbWlseTogSGVsdmV0aWNhOyBjb2xvcjogcmdiKDAsIDAsIDApOyBsaW5lLWhlaWdodDogMS4yOyB2ZXJ0aWNhbC1hbGlnbjogdG9wOyB3aWR0aDogNDlweDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgb3ZlcmZsb3ctd3JhcDogbm9ybWFsOyB0ZXh0LWFsaWduOiBjZW50ZXI7Ij48ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIiBzdHlsZT0iZGlzcGxheTppbmxpbmUtYmxvY2s7dGV4dC1hbGlnbjppbmhlcml0O3RleHQtZGVjb3JhdGlvbjppbmhlcml0O3doaXRlLXNwYWNlOm5vcm1hbDsiPkRldmljZXM8L2Rpdj48L2Rpdj48L2ZvcmVpZ25PYmplY3Q+PHRleHQgeD0iMjQiIHk9IjEyIiBmaWxsPSIjMDAwMDAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEycHgiIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiPkRldmljZXM8L3RleHQ+PC9zd2l0Y2g+PC9nPjwvZz48L3N2Zz4=",[439,17300,17301],{},"Investigating every sensible gradle switch or task configuration we still were unsuccessful. Then, frustrated, we\ndecided to dig into the source code of the Google Android build tools project and understand just what was going on.",[439,17303,17304],{},"There, we realized that Google decided to use the Java Common Pool to trigger the device tests in parallel. This pool is\nconfigured to use n = CoreCount – 1 threads per default. This observation fit perfectly, as we were currently running\ndevice tests on a Dual Core with Hyperthreading (4 “virtual” cores).",[439,17306,17307,17308,17313,17314],{},"The default number of threads for\nthe ",[1002,17309,17312],{"href":17310,"rel":17311},"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html",[1006],"Common Pool"," can be configured\nwith the JVM option ",[471,17315,17316],{},"java.util.concurrent.ForkJoinPool.common.parallelism",[439,17318,17319],{},"Gradle is triggered by our jenkins agent. We decided to “fix” our gradle installation by updating our installation-wide\ngradle-config:",[439,17321,17322],{},[448,17323,17324],{},"/var/lib/jenkins/.gradle/gradle.properties:",[439,17326,17327],{},"`#Allow more simultaneous device tests",[439,17329,17330],{},"systemProp.java.util.concurrent.ForkJoinPool.common.parallelism=10`",[439,17332,17333],{},"Because the main load of the test execution should be carried by the devices itself we expected no negative consequences\nin this scenario. And indeed, setting this option to a higher value did allow all device to test in parallel, saving us\nvaluable time.",{"title":469,"searchDepth":507,"depth":507,"links":17335},[],[1030,11122,1031,1413],"2019-10-08T14:24:36","Some time ago we decided to expand our pool of Android devices. These are used in our Continuous Integration (CI)\\npipeline. But running our tests we noticed a peculiar behaviour: Only 3 devices would execute the test suite while the\\nrest would wait. As soon as a device finished one of the waiting devices would proceed.","https://synyx.de/blog/limit-of-active-devices-during-android-device-tests/",{},"/blog/limit-of-active-devices-during-android-device-tests",{"title":17281,"description":17290},"blog/limit-of-active-devices-during-android-device-tests",[],"Some time ago we decided to expand our pool of Android devices. These are used in our Continuous Integration (CI) pipeline. But running our tests we noticed a peculiar behaviour: Only 3 devices would execute the suite while the rest would wait. Then as soon as a device finished one of the waiting devices would proceed with the test suite.","y-Hh4822zLikheKy9i4ThKw_hAy-qX4soVfd6xPaMu4",{"id":17348,"title":17349,"author":17350,"body":17351,"category":17492,"date":17493,"description":17494,"extension":1034,"link":17495,"meta":17496,"navigation":916,"path":17497,"seo":17498,"slug":17355,"stem":17500,"tags":17501,"teaser":17506,"__hash__":17507},"blog/blog/mein-erster-besuch-auf-dem-barcamp-pforzheim.md","Mein erster Besuch auf dem BarCamp Pforzheim",[30],{"type":432,"value":17352,"toc":17482},[17353,17356,17365,17369,17376,17385,17389,17392,17395,17400,17405,17409,17412,17415,17419,17422,17425,17428,17431,17444,17448,17451,17455,17469,17473,17479],[435,17354,17349],{"id":17355},"mein-erster-besuch-auf-dem-barcamp-pforzheim",[439,17357,17358,17359,17364],{},"Die coolsten Erlebnisse entstehen bei mir meist aus einem spontanen Impuls heraus. Genauso ging es mir auch letzten\nSamstag, als ich mich plötzlich zum ersten Mal auf dem ",[1002,17360,17363],{"href":17361,"rel":17362},"https://www.barcamp-pforzheim.de/",[1006],"BarCamp Pforzheim","\nwiederfand, das auch dieses Jahr wieder von synyx gesponsert wurde. Den Freitag hatte ich leider verpasst, aber\noffensichtlich hat mir ein Tag auf dem BarCamp bereits ausgereicht, um mich auch heute noch wie berauscht zu fühlen von\nall den Eindrücken, spannenden Unterhaltungen und interessanten Menschen.",[3938,17366,17368],{"id":17367},"was-ist-so-ein-barcamp-eigentlich","Was ist so ein BarCamp eigentlich?",[439,17370,17371,17372,17375],{},"Ein BarCamp hat kein festes Programm, sondern lebt von den Teilnehmenden. Zeitslots und Räume geben zwar einen Rahmen\nvor, aber ",[448,17373,17374],{},"wie"," diese gefüllt werden, hängt völlig von den Teilnehmenden ab.",[439,17377,17378,17379,17384],{},"Nach einem leckeren Frühstück (“Endlich Kaffee!”) zum ersten Eingrooven in\nder ",[1002,17380,17383],{"href":17381,"rel":17382},"https://www.emma-pf.de/",[1006],"tollen Location"," ging es um 10 Uhr los mit einer kurzen Einführung inklusive\nVorstellungsrunde aller Teilnehmenden – es durften nur Name, drei Hashtags und Twitter-Handle genannt werden.",[1065,17386,17388],{"id":17387},"und-dann-begann-auch-schon-die-sessionplanung-für-den-tag","Und dann begann auch schon die Sessionplanung für den Tag.",[439,17390,17391],{},"Wie man sich das vorstellen kann? Ganz viele motivierte Menschen springen plötzlich von ihren Plätzen auf und eilen zu\neiner wachsenden Schlange ans Mikrofon, um ihre Session-Idee vorzustellen. Bei genügend Handzeichen (“Wer hat Lust?”)\nwird die Session einem Zeitslot und Raum zugeordnet. Tatsächlich wurden so viele Vorschläge eingereicht, dass ein\nzusätzlicher Zeitslot, nämlich die Mittagspause, ebenfalls noch mit Sessions gefüllt wurde.",[439,17393,17394],{},"Eine Session muss übrigens kein Frontalvortrag sein, sondern kann auch ein Mitmach-Workshop oder einfach eine lockere\nAustauschrunde zu einer bestimmten Fragestellung sein.",[439,17396,17397],{},[2205,17398],{"alt":469,"src":17399},"https://media.synyx.de/uploads/2019/10/barcamppf19sessions-768x576.jpeg",[439,17401,17402],{},[2205,17403],{"alt":469,"src":17404},"https://media.synyx.de/uploads/2019/10/barcamppf19d4k-768x576.jpeg",[1065,17406,17408],{"id":17407},"ganz-schön-schwer-sich-zu-entscheiden","Ganz schön schwer, sich zu entscheiden.",[439,17410,17411],{},"Man kann es sich vielleicht schon denken: Das dabei entstandene Programm war wirklich sehr vielseitig. Das bunt\ngemischte Publikum war es auch, was mir am BarCamp besonders gefallen hat. Menschen unterschiedlichen Alters und\nberuflicher Hintergründe – von Techie bis Physiotherapeut, Illustrator oder Goldschmied –genau diese bunte Mischung hat\nmeiner Meinung nach den spannenden Austausch ausgemacht.",[439,17413,17414],{},"Nicht nur die Vielseitigkeit der Themen, auch die Tatsache, dass zu jedem Zeitslot fünf(!) Sessions parallel liefen, hat\nes manchmal ganz schön schwer gemacht, sich zu entscheiden. Um eine Vorstellung zu bekommen, was für Sessions so geboten\nwurden, hier ein kleiner Einblick in die Sessions, die ich besucht habe.",[1065,17416,17418],{"id":17417},"eine-session-jagt-die-nächste","Eine Session jagt die nächste.",[439,17420,17421],{},"Die erste Session, an der ich teilnahm, drehte sich um das Thema “Alphatiere und artgerechte Mitarbeiterhaltung”, in der\nsich über die Zusammenarbeit zwischen Alphatieren und der Generation Y ausgetauscht wurde. Danach folgte eine ruhige,\nfast meditative Phase der Selbstreflexion in der Session “Dein Leben in einem kurzen Satz mit dem\nInspirationsstatement”, die noch immer in mir nachwirkt.",[439,17423,17424],{},"Sehr gerne wäre ich im Mittagspause-Slot in die Session “Agile Family”, bei der es darum ging, wie ein Kanban-Board\nsich positiv auf den Haussegen auswirken kann, aber mein Bauch knurrte leider zu sehr. Also hieß es für mich erst\neinmal: Essen fassen! Trotz leichtem Fresskoma nach dem guten Essen, das es neben der Getränkeversorgung übrigens auch\nall inclusive gab, fühlte ich mich gestärkt und bereit für die Nachmittagssessions.",[439,17426,17427],{},"Die Themen “Bitcoin”, “Hörbuch mit Musik” und “Selbstbestimmtes Sterben” lockten mich nicht so sehr, also führte ich mir\neine teilweise fast philosophische Session “Künstliche Intelligenz” zu Gemüte, die mir interessante Konzepte zu\nIntelligenz näher brachte. Trotz allem war mein Energielevel um 15 Uhr dann etwas arg weit unten. Um nicht zu riskieren,\nden Rest des BarCamps zu verschlafen, entschied ich mich spontan lieber gegen die Session “Entspannungsmethoden” und\nnahm stattdessen an einer lebhaften Austauschrunde zum Thema “Macht mehr Komplimente” teil.",[439,17429,17430],{},"Schnell noch dem Sessionverantwortlichen ein Kompliment ausgesprochen und dann ab in die spannende Session “Systemisches\nKonsensieren”, die vorbeiging wie im Flug.",[439,17432,17433,17434,17437,17438,17443],{},"Zack, schon brach die letzte Session an – “",[990,17435,17436],{},"Oh, wir sind ja jetzt dran.","” – in der wir über\nunsere ",[1002,17439,17442],{"href":17440,"rel":17441},"https://github.com/synyx/urlaubsverwaltung",[1006],"Open Source Urlaubsverwaltung"," plauderten und uns wertvolle Gedanken\naußerhalb unserer Techie-Blase einholen konnten.",[1065,17445,17447],{"id":17446},"die-session-nach-den-sessions","Die Session nach den Sessions.",[439,17449,17450],{},"Doch nach der letzten Session und einem kurzen Abschluss war es noch lange nicht vorbei. Denn die Session nach den\nSessions ging noch eine ganze Weile in der Kneipe nebenan weiter und bot die Gelegenheit zu weiteren spannenden\nAustauschrunden.",[1065,17452,17454],{"id":17453},"noch-mehr-einblick","Noch mehr Einblick?",[439,17456,17457,17462,17463,17468],{},[1002,17458,17461],{"href":17459,"rel":17460},"https://app.konferenz.guide/index.html?seite=sessionList&event=bcpf19",[1006],"Hier"," findet sich der komplette Sessionplan der\nzwei Tage. Mehr Eindrücke und Photos lassen sich beispielsweise auf\ndem ",[1002,17464,17467],{"href":17465,"rel":17466},"https://twitter.com/barcamppf",[1006],"Twitter-Account des BarCamp Pforzheim"," finden.",[3938,17470,17472],{"id":17471},"nach-dem-barcamp-ist-vor-dem-barcamp","Nach dem BarCamp ist vor dem BarCamp.",[439,17474,17475,17476,1402],{},"Der Termin für nächstes Jahr steht bereits fest: ",[448,17477,17478],{},"25. und 26. September 2020",[439,17480,17481],{},"In meinem Kalender sind die beiden Tage bereits markiert. Ich kann es wirklich nur wärmstens empfehlen. Also schnell\neintragen, nur noch 359 Tage 😉",{"title":469,"searchDepth":507,"depth":507,"links":17483},[17484,17491],{"id":17367,"depth":507,"text":17368,"children":17485},[17486,17487,17488,17489,17490],{"id":17387,"depth":547,"text":17388},{"id":17407,"depth":547,"text":17408},{"id":17417,"depth":547,"text":17418},{"id":17446,"depth":547,"text":17447},{"id":17453,"depth":547,"text":17454},{"id":17471,"depth":507,"text":17472},[8266,1031],"2019-10-01T17:00:25","Die coolsten Erlebnisse entstehen bei mir meist aus einem spontanen Impuls heraus. Genauso ging es mir auch letzten\\nSamstag, als ich mich plötzlich zum ersten Mal auf dem BarCamp Pforzheim\\nwiederfand, das auch dieses Jahr wieder von synyx gesponsert wurde. Den Freitag hatte ich leider verpasst, aber\\noffensichtlich hat mir ein Tag auf dem BarCamp bereits ausgereicht, um mich auch heute noch wie berauscht zu fühlen von\\nall den Eindrücken, spannenden Unterhaltungen und interessanten Menschen.","https://synyx.de/blog/mein-erster-besuch-auf-dem-barcamp-pforzheim/",{},"/blog/mein-erster-besuch-auf-dem-barcamp-pforzheim",{"title":17349,"description":17499},"Die coolsten Erlebnisse entstehen bei mir meist aus einem spontanen Impuls heraus. Genauso ging es mir auch letzten\nSamstag, als ich mich plötzlich zum ersten Mal auf dem BarCamp Pforzheim\nwiederfand, das auch dieses Jahr wieder von synyx gesponsert wurde. Den Freitag hatte ich leider verpasst, aber\noffensichtlich hat mir ein Tag auf dem BarCamp bereits ausgereicht, um mich auch heute noch wie berauscht zu fühlen von\nall den Eindrücken, spannenden Unterhaltungen und interessanten Menschen.","blog/mein-erster-besuch-auf-dem-barcamp-pforzheim",[17502,17503,17504,17505],"barcamp","bcpf19","pforzheim","unkonferenz","Ein kleiner Erfahrungsbericht von meinem ersten Besuch auf dem BarCamp Pforzheim 2019 #bcpf19","8d_-lkp6gdHsdaEp7xXUQg8eNMU9SldwK0g7oJuimlM",{"id":17509,"title":17510,"author":17511,"body":17512,"category":17646,"date":17647,"description":17648,"extension":1034,"link":16230,"meta":17649,"navigation":916,"path":17650,"seo":17651,"slug":17652,"stem":17653,"tags":17654,"teaser":17657,"__hash__":17658},"blog/blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick.md","8 Jahre synyx – Ein abwechslungsreicher Rückblick",[30],{"type":432,"value":17513,"toc":17640},[17514,17517,17520,17525,17531,17533,17537,17540,17549,17560,17569,17572,17576,17579,17585,17591,17597,17600,17604,17607,17613,17618,17621,17624,17627,17631,17634],[435,17515,17510],{"id":17516},"_8-jahre-synyx-ein-abwechslungsreicher-rückblick",[439,17518,17519],{},"Genau heute vor 8 Jahren, am 1. August 2011, begann meine Geschichte bei synyx. Damals war synyx noch eine kleine Bude\nmit etwa zwanzig MitarbeiterInnen, die zwei schnieke Altbauwohnungen mit Fischgrätenparkett und Deckenstuck als Büro\nnutzten.",[439,17521,17522],{},[2205,17523],{"alt":469,"src":17524},"https://media.synyx.de/uploads/2019/08/P1080729-768x576.jpg",[439,17526,17527],{},[2205,17528],{"alt":17529,"src":17530},"Altes Büro in der Karlstraße 68","https://media.synyx.de/uploads/2019/08/P1080060-768x576.jpg",[439,17532,17529],{},[3938,17534,17536],{"id":17535},"start-ins-abenteuer-ausbildung","Start ins Abenteuer Ausbildung",[439,17538,17539],{},"Nach erfolgreichem Abbruch meines Biostudiums hatte ich die fixe Idee, in die IT einzusteigen. Programmiererfahrung\nhatte ich keine vorzuweisen – nur stundenlange Spielereien mit HTML und CSS – aber dafür einen großen Berg an Motivation\nfür ein neues Abenteuer als Keyboardjockey. Ich weiß nicht, für welche Seite es das gewagtere Experiment war, doch zu\nmeiner Überraschung durfte ich tatsächlich die Ausbildung zur Fachinformatikerin für Anwendungsentwicklung beginnen.",[439,17541,17542,17543,17548],{},"So bekam ich die ersten Monate erst einmal Druckbetankung in Sachen Java, Spring und Co. von meinem Ausbilder Alex. Bis\nes im Oktober dann mit dem ersten Grüne-Wiese-Projekt losging, einer elektronischen Urlaubsverwaltung für synyx, die\nzum Open Source Projekt heranwuchs und auch heute noch stetig weiterentwickelt\nwird. (",[1002,17544,17547],{"href":17545,"rel":17546},"https://synyx.de/blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts/",[1006],"Zur Geschichte der Urlaubsverwaltung",")",[439,17550,17551,17552,17555,17556,17559],{},"Nach einigen Monaten eigenständiger Hackerei und erfolgreichem Live-Gang der Urlaubsverwaltung wurde es dann ernst für\nmich. Ich kam in ein ",[990,17553,17554],{},"richtiges"," Team und arbeitete plötzlich an einem ",[990,17557,17558],{},"richtigen"," Kundenprojekt – oder besser gesagt an\nmehreren. Denn das damalige Indy-Team klopfte wochenweise Software für unterschiedliche Kunden runter. Das war ganz\nschön spannend, weil echt abwechslungsreich.",[439,17561,17562,17563,17568],{},"Gegen Ende meiner Ausbildung durfte ich in die dunkle äh andere Seite der IT-Welt eintauchen und einige Monate bei den\nAdmins verbringen. (",[1002,17564,17567],{"href":17565,"rel":17566},"https://synyx.de/blog/devoooops-azubitausch-bei-synyx/",[1006],"Azubitausch bei synyx",") Das war eine sehr\nlehrreiche Zeit, wenn auch ehrlich gesagt nicht viel hängen geblieben ist. Mein Wissen in Sachen Netzwerk blieb\nunterirdisch. Doch einige Sachen wie zum Beispiel die Provisionierung mit Puppet fand ich echt interessant und nützlich.",[439,17570,17571],{},"Kurz vor Einsetzen des ops-typischen Bartwuchses fand meine Zeit bei den Admins ein Ende, denn mein Abschlussprojekt\nstand an. Dieses war eine Webanwendung zur Pflege von Konfigurationsproperties mit dem schönen Namen Properties\nManagement System, kurz PMS. Alles lief glatt, sodass ich im Januar 2014 das Experiment Ausbildung zur\nFachinformatikerin erfolgreich abschloss.",[3938,17573,17575],{"id":17574},"hallo-arbeitsleben","Hallo Arbeitsleben!",[439,17577,17578],{},"Bäm – und so begann das richtige Arbeitsleben. Nicht dass sich dadurch etwas großartig änderte, denn auch als Azubi\nhatte ich mich immer wie ein vollwertiges Teammitglied gefühlt. Aber zumindest konnte ich mir nun die eher ungeliebte\nBerufsschule sparen und das Konto war besser gefüllt 🙂 Auch nach meiner Ausbildung hackerte ich nebenbei immer wieder an\nder Urlaubsverwaltung herum, doch hauptsächlich war ich in mehreren Projekten unseres großen Logistikkunden Contargo\nunterwegs. Eine interessante Domäne und unterschiedliche Technologien, die ich da kennenlernen durfte. Die Arbeit am\nFrontend lag mir zwar immer einen Tick mehr, dennoch war stets Interdisziplinarität angesagt.",[439,17580,17581,17582,17584],{},"Im Herbst 2014 war es bei synyx vorbei mit dem Flair der Altbauwohnung, denn der Umzug in ein ",[990,17583,17554],{}," Bürogebäude\nstand an. Andernfalls hätten wir unsere Schreibtische wohl stapeln müssen. Mit fortschreitendem Wachstum wurde es\neinfach zu eng. So ein neues Büro ist natürlich erst einmal eine Umstellung. Aber ich denke, wir haben es uns ganz\ngemütlich gemacht, vor allem mit unseren besonderen Themenräumen. (Wieviele Menschen halten schon Meetings im Birkenwald\noder Omas Wohnzimmer ab?) Außerdem ist eine Klimaanlage im Karlsruher Sommer wirklich nicht zu verachten. Fußbäder und\nnasse Handtücher im Nacken, um überhaupt halbwegs denken zu können, sind Details aus dem alten Büro, die ich echt nicht\nvermisse 🙂",[439,17586,17587],{},[2205,17588],{"alt":17589,"src":17590},"raum_memory_leak","https://media.synyx.de/uploads/2019/08/IMG_4441-Kopie-768x512.jpg",[439,17592,17593],{},[2205,17594],{"alt":17595,"src":17596},"Blick ins synyx Wohnzimmer","https://media.synyx.de/uploads/2019/08/D9AYt99WkAYnKO0-768x512.jpeg",[439,17598,17599],{},"Impressionen aus dem aktuellen synyx Büro in der Gartenstraße 67",[3938,17601,17603],{"id":17602},"ein-neues-abenteuer-kommt-selten-allein","Ein neues Abenteuer kommt selten allein…",[439,17605,17606],{},"Ende 2016 erwartete mich dann ein Abenteuer privater Natur. Ich wurde Mama und war erst einmal knapp zwei Jahre in\nElternzeit. Trotzdem zog es mich fast jedes Mal zum monatlich stattfindenden synyx Frühstück. Anfangs mit schlafendem\nBaby im Tragetuch, später mit dem durch’s Büro krabbelnden/laufenden Kleinkind. Sich beim gemeinsamen Mampfen über\nNeuigkeiten auszutauschen und aktuelle Projektstände präsentiert zu bekommen, führt dazu, dass sich zwei Jahre\neigentlich gar nicht so lang anfühlen.",[439,17608,17609],{},[2205,17610],{"alt":17611,"src":17612},"synyx_fruehstueck","https://media.synyx.de/uploads/2019/08/IMG_9055-768x512.jpg",[439,17614,17615],{},[2205,17616],{"alt":469,"src":17617},"https://media.synyx.de/uploads/2019/08/IMG_7865-768x512.jpg",[439,17619,17620],{},"Das synyx Frühstück – Hier geht's um mehr als Essen",[439,17622,17623],{},"Solch eine lange Zeit raus zu sein und irgendwie auch nicht, lässt einen nachdenklich werden. In mir wird immer ein\nkleines Nerd-Herz schlagen, doch der Abstand hat mir gezeigt, dass mich andere Dinge noch mehr interessieren als die\nSoftwareentwicklung. So kommt es also, dass ich erneut ein Experiment bei synyx angeleiert habe und nun seit Anfang des\nJahres als Scrum Master / Agile Coach unterwegs bin. Das ist ganz schön aufregend, kann ich sagen 🙂",[439,17625,17626],{},"Wenn Du plötzlich wieder ganz viele Dinge zum ersten Mal machst und Dich manchmal fast wieder wie ein Azubi fühlst.\nNichtsdestotrotz fühlt sich die Entscheidung richtig an und ich freue mich darüber, dass solche Weiterentwicklungen bei\nsynyx möglich sind.",[3938,17628,17630],{"id":17629},"bock-mitzumachen","Bock mitzumachen?",[439,17632,17633],{},"In diesem (manchmal) echt verrückten Laden bist Du nicht einfach nur ein Schräubchen im Getriebe, sondern hast die\nMöglichkeit, Dich und die Firma weiterzuentwickeln – und zwar egal in welcher Rolle Du hier unterwegs bist. Niemand wird\nDir Deine Aufgaben auf dem Silbertablett servieren und sowas wie eine Karriereleiter suchst Du hier auch vergeblich.\nDoch wenn Du mit Leidenschaft und Eigenverantwortung bei der Sache bist, kannst Du hier ziemlich glücklich werden.",[439,17635,17636,17637,17639],{},"Du hast Bock, eigenverantwortlich an spannenden Projekten mit modernen Technologien mitzuwirken? Dann melde Dich bei uns\nunter ",[1002,17638,15565],{"href":15564}," und erzähl uns, wer Du bist und was Du so kannst. Zeugnisse sind uns nicht\nso wichtig. Statt Deine Grundschulnoten zu analysieren, wollen wir lieber mehr über Deine Interessen und Fähigkeiten\nerfahren. Du als Mensch zählst. Wir freuen uns, von Dir zu hören!",{"title":469,"searchDepth":507,"depth":507,"links":17641},[17642,17643,17644,17645],{"id":17535,"depth":507,"text":17536},{"id":17574,"depth":507,"text":17575},{"id":17602,"depth":507,"text":17603},{"id":17629,"depth":507,"text":17630},[1031],"2019-08-01T20:25:05","Genau heute vor 8 Jahren, am 1. August 2011, begann meine Geschichte bei synyx. Damals war synyx noch eine kleine Bude\\nmit etwa zwanzig MitarbeiterInnen, die zwei schnieke Altbauwohnungen mit Fischgrätenparkett und Deckenstuck als Büro\\nnutzten.",{},"/blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick",{"title":17510,"description":17519},"8-jahre-synyx-ein-abwechslungsreicher-rueckblick","blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick",[3592,17655,17656],"jobs","rueckblick","Von wegen Eintönigkeit. 8 Jahre beim gleichen Unternehmen zu sein, kann ganz schön abwechslungsreich sein.","TSFTxvrimS0wxRsZIxYadrK86y5fBc7c12YlEh6FgZI",{"id":17660,"title":17661,"author":17662,"body":17663,"category":17788,"date":17789,"description":17670,"extension":1034,"link":17790,"meta":17791,"navigation":916,"path":17792,"seo":17793,"slug":17667,"stem":17794,"tags":17795,"teaser":17797,"__hash__":17798},"blog/blog/ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete.md","Ist das jetzt Urlaub oder Arbeit?! – Ein typischer Tag auf der JCrete",[154],{"type":432,"value":17664,"toc":17786},[17665,17668,17671,17697,17703,17712,17715,17718,17724,17727,17730,17733,17736,17739,17742,17748,17751,17757,17772,17775,17783],[435,17666,17661],{"id":17667},"ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete",[439,17669,17670],{},"Zuallererst möchte ich mich bedanken:",[994,17672,17673,17679,17685,17691],{},[997,17674,17675,17678],{},[448,17676,17677],{},"Bei der synyx:"," für ein modernes, eigenverantwortliches Weiterbildungskonzept, das es mir ermöglicht ohne große\nDiskussionen an so einer Veranstaltung teilzunehmen!",[997,17680,17681,17684],{},[448,17682,17683],{},"Bei den Disorganizern der JCrete",": dafür, dass ich dabei sein durfte!",[997,17686,17687,17690],{},[448,17688,17689],{},"Bei meinen Kindern",": weil sie so super mitgemacht haben und nicht verloren gegangen sind!",[997,17692,17693,17696],{},[448,17694,17695],{},"Bei meiner Frau:"," weil sie darauf vertraut hat, dass mir die Kinder nicht verloren gehen! 😉",[439,17698,17699],{},[2205,17700],{"alt":17701,"src":17702},"jCrete: Dinner mit Meerblick","https://media.synyx.de/uploads/2019/07/IMG_20190715_075755-768x576.jpg",[439,17704,17705,17706,17711],{},"Wenn man als Unterkunft eins der Zimmer in der OAC (",[1002,17707,17710],{"href":17708,"rel":17709},"https://www.oac.gr/en/",[1006],"Orthodox Academy of Crete"," ) gebucht hat,\nwacht man jeden Morgen mit einer gigantischen Aussicht direkt aufs Meer auf. Die allgegenwärtigen Zikaden schlafen noch\nund man hört das Meer rauschen. Um 6:30 warten auf dem Hof vor dem Zimmer die zwei Läufergruppen. Egal bei wie viel Raki\nund Bier man den Abend zuvor über die Java-Welt philosophiert hat, Frühsport muss sein. Eine schnelle Gruppe mit 5er\nSchnitt und eine gemütliche Gruppe mit 7-8er Schnitt erklimmt die Hügel hinter der Akademie. Danach kann man direkt ins\nMeer springen und sich abkühlen.",[439,17713,17714],{},"Ich habe diesmal Kinder dabei und schaue den Läufern vom Balkon aus zu. 😉",[439,17716,17717],{},"Beim Frühstück sitze ich mit zum Teil mir unbekannten oder halt auch sehr prominenten Menschen aus der Java Community\nzusammen. Schon vor dem ersten Schluck Kaffe entwickeln sich coole Diskussionen. Kurz noch Brötchen für die Kinder auf\ndem Zimmer schmieren und dann geht’s schon los mit Konferenzprogramm.",[439,17719,17720],{},[2205,17721],{"alt":17722,"src":17723},"jCrete: Beach-Programm-Slot","https://media.synyx.de/uploads/2019/07/Image-from-iOS-768x576.jpg",[439,17725,17726],{},"In die erste Session “Rust for Java Programmers” stolpere ich mehr aus Zufall. Da ich eh mal was über Rust lernen\nwollte, bleibe ich sitzen und lerne von Alex Snaps und Ben Harper einiges über die Programmiersprache von Mozilla.\nSpeichersicherheit und Performance spielen eine große Rolle. Was ich mitnehme: Rust kann und sollte man für Dinge\neinsetzen, die man sonst in C oder C++ schreiben würde. Also nah an der Hardware oder absolut Performance-kritisch.\nFür die Entwicklung von Webapplikationen oder Services gibt es aber geeignetere Sprachen.",[439,17728,17729],{},"In der Pause inhaliere ich zwei Becher Kaffe und schaue nach den Kindern. Die spielen inzwischen mit Kindern der anderen\nTeilnehmern auf dem Hof zwischen den beiden Hauptgebäuden und überwinden dabei jegliche Sprachbarrieren.",[439,17731,17732],{},"Die zweite Session findet in der großen Halle statt. Marcus Hirt stellt Java Mission Control und Flight Recorder vor.\nWir diskutieren die gemachten Erfahrungen sowie die Stärken und Schwächen der beiden Tools. Spannede und für mich neue\nInfo war, dass beide Tools ab OpenJDK 11 Opensource und damit frei nutzbar sind. Das sind IMHO großartige Neuigkeiten\nund bieten für uns viele neue Möglichkeiten zur Überwachung und Analyse unserer Produktiv-Applikationen. Zumindest\nsofern sie schon auf JDK 11 laufen. Es gab allerdings auch Andeutungen, dass ein Downport für OpenJDK 8 in Arbeit ist.",[439,17734,17735],{},"In der Pause wieder Kaffe, kurze Gespräche mit anderen Teilnehmern und Durchzählen der Kinder. 1 .. 2, alle noch da und\nnicht verhungert.",[439,17737,17738],{},"Die dritte Session dreht sich um das Testen von Microservices. Das war bereits im letzten Jahr ein großes Thema. Es wird\nlebhaft über die Testpyramide und den Aufwand, den man in unterschiedliche Arten von Tests stecken muss oder will,\ndiskutiert. Viele Teams, die größere Microservice Projekte umsetzen, scheinen stark mit dem “ich fahre gerade mal alles\nauf meinem Minikube hoch und teste dann” zu kämpfen. Es ist einfach ein nicht zu unterschätzender Aufwand. Ich erzähle\nein wenig, wie wir bei unserem Kunden Contargo entwickeln: SCS anstatt Microservices, bewusster Schnitt der Kontexte und\nlose Kopplung zwischen diesen. Viele Probleme, die diskutiert werden, ergeben sich überhaupt nicht für uns. Der richtige\nSchnitt der Kontexte kann so viel Arbeit sparen!",[439,17740,17741],{},"Nach den Sessions treffen wir uns nochmal alle in der großen Halle und wir sprechen das restliche Tagesprogramm aka\nAusflüge durch. Ich sammle meine Kinder ein und es geht los Richtung Mittagessen.",[439,17743,17744],{},[2205,17745],{"alt":17746,"src":17747},"Foodporn bei der jCrete","https://media.synyx.de/uploads/2019/07/IMG_20190718_130038-768x576.jpg",[439,17749,17750],{},"Das Mittagsessen findet wie jeden Tag auf der großen, mit Segeltuch überspannten Dachterrasse der Akademie statt. Es\ngibt überwiegend vegetarische, super leckere Spezialitäten aus Kreta. Wer will, kann sich griechischen Wein genehmigen.\n😉 Während meine Kinder über das griechische Essen jammern und lieber den Nachtisch-Kuchen als Hauptmahlzeit nehmen,\ndiskutiere ich mit Sébastien Blanc, dem DevRel-Menschen aus dem Keycloak Team, über die Erfahrungen, die wir bei synyx\nund unseren Kunden mit Keycloak machen.",[439,17752,17753],{},[2205,17754],{"alt":17755,"src":17756},"Foodtester Vivian war begeistert!","https://media.synyx.de/uploads/2019/07/IMG_20190715_202536-768x576.jpg",[439,17758,17759,17760,17765,17766,17771],{},"Den Nachmittag verbringen wir in einem Wasserpark in der Nähe von Chania. Da meine Kinder gerne Wasserrutschen rutschen,\nwar das auf jeden Fall ein Pflichttermin. Auch andere Teilnehmer hatten sich hier mit ihren Kindern eingefunden. Fotos\ngibt es keine, weil ich alle Hände damit zu tun hatte, dass die Kinder nicht verloren gehen oder ertrinken. Am Abend\nhatten die Disorganizers zwei Tavernen für alle Teilnehmer reserviert.\nDie",[1002,17761,17764],{"href":17762,"rel":17763},"https://www.tripadvisor.com/Restaurant_Review-g1191160-d4605253-Reviews-Panorama_Tavern_Falasarna-Falassarna_Chania_Prefecture_Crete.html",[1006],"Panorama Tavern in Falassarna","\nund\ndie ",[1002,17767,17770],{"href":17768,"rel":17769},"https://www.tripadvisor.com/Restaurant_Review-g1028265-d12957049-Reviews-Fish_Tavern_1960-Kissamos_Chania_Prefecture_Crete.html",[1006],"1960 Fish Tavern in Kissamos",".\nWir haben die Fisch-Taverne in Kissamos gewählt und haben uns in einem dreistündigen Seafood/Veggie DoS-Angriff\nwiedergefunden. Gegen 23 Uhr war der Tag dann vorbei. Genug erlebt.",[439,17773,17774],{},"So, das war nun nur einer von insgesamt 5 Konferenz-Tagen. 😉 Zum Abschluss noch ein Video der JCrete vom letzten Jahr.\nDort hatte uns ein Profi-Filmteam begleitet und super Aufnahmen gemacht.",[439,17776,17777,17782],{},[1002,17778,17781],{"href":17779,"rel":17780},"https://youtu.be/6-zF8JrWlt4",[1006],"Video auf YouTube ansehen"," (Mit dem Laden des Videos akzeptieren Sie die\nDatenschutzerklärung von YouTube.)",[439,17784,17785],{},"Heinz und Kirk erklären, wie die JCrete geboren wurde und was diese Unconference so besonders macht.",{"title":469,"searchDepth":507,"depth":507,"links":17787},[],[17502,8266,1031],"2019-07-25T12:02:20","https://synyx.de/blog/ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete/",{},"/blog/ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete",{"title":17661,"description":17670},"blog/ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete",[17796],"java-unconference-kreta-crete","Die JCrete ist eine, wenn nicht *die* Unconference im Java-Umfeld. Initiiert von Heinz Kabutz, Kirk Pepperdine und einem Team von Dis-Organizern, findet die Unconference seit 2011 jedes Jahr auf der griechischen Insel Kreta statt. Ich hatte die Ehre, dieses Jahr schon schon zum zweiten Mal dabei zu sein und beschreibe an dieser Stelle einen typischen Tag auf der JCrete.","EBvddf8HZccRoGUlG2DNdn17lwdnhRHgEjqYb02L0Yg",{"id":17800,"title":17801,"author":17802,"body":17803,"category":17857,"date":17858,"description":17810,"extension":1034,"link":17859,"meta":17860,"navigation":916,"path":17861,"seo":17862,"slug":17863,"stem":17864,"tags":17865,"teaser":17868,"__hash__":17869},"blog/blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen.md","Experiment JavaScript – Ein synyx Entwickler erzählt von seinen Anfängen",[211],{"type":432,"value":17804,"toc":17852},[17805,17808,17811,17815,17822,17825,17828,17831,17835,17838,17841,17843,17846],[435,17806,17801],{"id":17807},"experiment-javascript-ein-synyx-entwickler-erzählt-von-seinen-anfängen",[439,17809,17810],{},"Alles ging vor knapp drei Jahren mit einer ganz harmlosen Frage los:",[3938,17812,17814],{"id":17813},"kannst-du-dir-vorstellen-in-einem-javascript-projekt-zu-arbeiten","„Kannst Du Dir vorstellen, in einem JavaScript-Projekt zu arbeiten?“",[439,17816,17817,17818,17821],{},"Das war der Abschluss meines Bewerbungsgesprächs bei synyx. Lässt man die üblichen Website-Spielereien außer Acht, hatte ich zu diesem Zeitpunkt noch keine wirklichen Erfahrungen mit JavaScript vorzuweisen. Damals hätte ich es vermutlich nicht einmal als ",[990,17819,17820],{},"richtige"," Programmiersprache bezeichnet. Doch mein Interesse war geweckt und ich ließ mich auf das Experiment JavaScript ein.",[439,17823,17824],{},"Dann ging der für mich anfangs beschwerliche Weg los. Ich komme aus der klassischen Java-Welt und JavaScript ist doch ein bisschen anders als Java. Typsicherheit und einen Compiler habe ich vor allem anfangs sehr vermisst, aber mich auch über das direkte Ausführen von kleinen Fragmenten gefreut. JavaScript lässt sich einfach interaktiver / schneller entwickeln. Das macht Spaß, wenn man sich darauf einlässt. Die Sprache gibt einem viele Freiheiten, dynamisches Überschreiben von Methoden zum Beispiel.",[439,17826,17827],{},"Ach ja, Methoden – ein gutes Thema. Neben der Single-Threaded-Natur ist die prototypische Vererbung doch recht gewöhnungsbedürftig. Aber man sollte ja sowieso eher delegieren als vererben 😉 Und durch das Ducktyping sind Vererbungen auch gar nicht so wichtig. Die Sprache hatte für mich definitiv viele Eigenheiten, die ich mir erst nach und nach erarbeiten musste. Toll war, dass ich damit nicht allein im Team war.",[439,17829,17830],{},"Oh ja, das Team. Es ist klasse, wie offen wir über Probleme sprechen und gemeinsam nach Lösungen suchen können. Die verschiedenen Charaktere helfen dabei, zu guten Ergebnissen zu gelangen.",[3938,17832,17834],{"id":17833},"eine-interessante-sprache-und-ein-tolles-team-aber-was-machen-wir-da-eigentlich","Eine interessante Sprache und ein tolles Team, aber was machen wir da eigentlich?",[439,17836,17837],{},"Natürlich einem großen Kunden gute Software abliefern. Wobei so natürlich ist es eigentlich gar nicht. Wir bearbeiten zur Zeit zwei Projekte, die ihre Ausführungsumgebung gewechselt haben und parallel weiterentwickelt werden sollten. Von einem Headless-Browser in eine Node-Umgebung zu wechseln, klingt nach einem natürlichen Schritt. Allerdings brachte das viele kleinere und größere Probleme mit sich, die nur durch die intensive Zusammenarbeit mit dem Kunden, der die Ausführungsumgebung bereitstellt, zu bewältigen waren.",[439,17839,17840],{},"Erschwerend kam hinzu, dass nur eine Lösung komplett bei synyx erstellt wurde. Bei der anderen unterstützen wir einen Dienstleister, der an dem Programm seit rund sechs Jahren arbeitet. Gemeinsam machen wir die Anwendung fit für die Zukunft. Musik-Streaming ins Auto, ein interessantes, wenn auch stellenweise stressiges Projekt. Vor sechs Jahren wurde JavaScript doch noch ganz anders geschrieben als heute.",[3938,17842,17630],{"id":17629},[439,17844,17845],{},"Wenn Dich JavaScript als allgegenwärtige Sprache interessiert, Du Deine Erfahrungen einbringen willst oder einfach mal schauen möchtest, wie es ist, mit einem großen Kunden zusammenzuarbeiten, der versucht Probleme prozessorientiert zu lösen, komm doch mal bei uns vorbei und wir schauen, ob es für Dich und uns passt.",[439,17847,17848,17849,17851],{},"Melde Dich einfach bei uns unter ",[1002,17850,15565],{"href":15564}," und erzähl uns, wer Du bist und was Du so kannst. Zeugnisse sind uns nicht so wichtig. Statt Deine Grundschulnoten zu analysieren, wollen wir lieber mehr über Deine Interessen und Fähigkeiten erfahren. Wir freuen uns, von Dir zu hören!",{"title":469,"searchDepth":507,"depth":507,"links":17853},[17854,17855,17856],{"id":17813,"depth":507,"text":17814},{"id":17833,"depth":507,"text":17834},{"id":17629,"depth":507,"text":17630},[1031],"2019-05-28T16:26:03","https://synyx.de/blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen/",{},"/blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen",{"title":17801,"description":17810},"experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen","blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen",[3592,17866,17867,15201,17655],"developer","entwickler","Werdegang des JavaScript Developers Christian Lange bei synyx.","AqRd7fJ4-roSV9bB721fTkY674GmJh6n6gpoi-HeEUI",{"id":17871,"title":17872,"author":17873,"body":17874,"category":17956,"date":17957,"description":17881,"extension":1034,"link":17958,"meta":17959,"navigation":916,"path":17960,"seo":17961,"slug":17962,"stem":17963,"tags":17964,"teaser":17965,"__hash__":17966},"blog/blog/schulesynyx-spass-mit-stiften.md","schule@synyx – Spaß mit Stiften",[30],{"type":432,"value":17875,"toc":17953},[17876,17879,17882,17885,17893,17896,17899,17903,17918,17921,17924,17929,17934,17939],[435,17877,17872],{"id":17878},"schulesynyx-spaß-mit-stiften",[439,17880,17881],{},"(Fast) jeden Freitag treffen sich interessierte synyxer in der Schule@synyx, um sich gegenseitig etwas Neues beizubringen.",[439,17883,17884],{},"In verschiedenen Formaten (Vortrag, Workshop, Diskussion, …) möchten wir Wissen verteilen und unsere Erkenntnisse mit den Kollegen challengen. Hier macht es keinen Unterschied, ob der Inhalt aus der aktuellen Arbeit beim Kunden, einem R&D-Projekt oder einer Spielerei entstanden ist.",[439,17886,17887,17888,17892],{},"(Mein Kollege Arnold hat die Schule@synyx ",[1002,17889,8234],{"href":17890,"rel":17891},"https://synyx.de/blog/schulesynyx-the-self-training-company/",[1006]," schonmal ausführlicher erklärt.)",[17894,17895],"hr",{},[439,17897,17898],{},"Heute gibt’s in der Schule@synyx bereits die dritte Runde “Spaß mit Stiften”. Da bei den beiden ersten Runden im März noch nicht alle Interessierten dabei sein konnten, gibt’s heute erneut eine Einheit.",[3938,17900,17902],{"id":17901},"spaß-mit-stiften-notizen-visuell-aufpeppen","Spaß mit Stiften – Notizen visuell aufpeppen",[439,17904,17905,17906,17911,17912,17917],{},"Bei “Spaß mit Stiften” geht’s weder um Kunst noch um Perfektion. Die liebe Dani (",[1002,17907,17910],{"href":17908,"rel":17909},"https://twitter.com/dgrammlich",[1006],"@dgrammlich",") und ich (",[1002,17913,17916],{"href":17914,"rel":17915},"https://twitter.com/fraulyoner",[1006],"@fraulyoner",") geben im Rahmen eines einstündigen Mitmach-Workshops eine kleine Einführung zum Thema Bildsprache (Sketch Notes). Wir zeigen ein paar einfache Kniffe, mit denen sich Notizen visuell aufpeppen lassen und wollen dabei vor allem eins: Spaß vermitteln.",[439,17919,17920],{},"Notizblock, Whiteboard, Flipcharts oder Post-its – Notizen sind in unserem Arbeitsalltag allgegenwärtig. Ja, auch wenn wir hier hauptsächlich Techies sind. Visuelle Notizen fördern das gemeinsame Verständnis in Diskussionen und sorgen dafür, dass Informationen einfacher und länger im Gedächtnis verankert werden. Doch leider herrscht gerade im Techie-Umfeld allzu oft große Hemmung, überhaupt einen Stift in die Hand zu nehmen, um Gedanken visuell darzustellen. Genau diese Hemmung versuchen wir bei “Spaß mit Stiften” zu mindern.",[439,17922,17923],{},"Hier ein paar Eindrücke von den vergangenen Runden:",[439,17925,17926],{},[2205,17927],{"alt":469,"src":17928},"https://media.synyx.de/uploads/2019/06/schule-spass-mit-stiften-bildsprache-768x1024.jpg",[439,17930,17931],{},[2205,17932],{"alt":469,"src":17933},"https://media.synyx.de/uploads/2019/06/schule-spass-mit-stiften-eindruck-768x576.jpg",[439,17935,17936],{},[2205,17937],{"alt":469,"src":17938},"https://media.synyx.de/uploads/2019/06/schule-spass-mit-stiften-feedback-768x1024.jpg",[439,17940,17941,17942,17947,17948,17548],{},"Im Dezember 2015 hatten wir übrigens ",[1002,17943,17946],{"href":17944,"rel":17945},"https://www.frauhoelle.com/",[1006],"Frau Hoelle"," zu Besuch bei synyx und hatten damals inhouse gleich mehrere Workshop-Tage zum Thema Sketch Notes / Visual Thinking. (siehe ",[1002,17949,17952],{"href":17950,"rel":17951},"https://synyx.de/blog/visual-thinking-synyx-sketcht/",[1006],"Blog-Beitrag",{"title":469,"searchDepth":507,"depth":507,"links":17954},[17955],{"id":17901,"depth":507,"text":17902},[1031],"2019-05-17T16:20:52","https://synyx.de/blog/schulesynyx-spass-mit-stiften/",{},"/blog/schulesynyx-spass-mit-stiften",{"title":17872,"description":17881},"schulesynyx-spass-mit-stiften","blog/schulesynyx-spass-mit-stiften",[],"Spaß mit Stiften – Notizen in der schule@synyx visuell aufpeppen.","ZLTmGm-q4Sa4ynHAVlVlnp9Ff5VmCOnGkr3AYw0CUE0",{"id":17968,"title":17969,"author":17970,"body":17971,"category":18057,"date":18058,"description":18059,"extension":1034,"link":18060,"meta":18061,"navigation":916,"path":18062,"seo":18063,"slug":17975,"stem":18064,"tags":18065,"teaser":18066,"__hash__":18067},"blog/blog/schulesynyx-security-testing.md","schule@synyx – security testing",[208],{"type":432,"value":17972,"toc":18054},[17973,17976,17979,17982,17988,17990,17993,17997,18000,18017,18027,18038,18041,18044,18046],[435,17974,17969],{"id":17975},"schulesynyx-security-testing",[439,17977,17978],{},"(Fast) jeden Freitag treffen sich interessierte synyxer in der Schule@synyx, um sich gegenseitig etwas Neues\nbeizubringen.",[439,17980,17981],{},"In verschiedenen Formaten (Vortrag, Workshop, Diskussion, …) möchten wir Wissen verteilen und unsere Erkenntnisse mit\nden Kollegen challengen. Hier macht es keinen Unterschied, ob der Inhalt aus der aktuellen Arbeit beim Kunden, einem\nR&D-Projekt oder einer Spielerei entstanden ist.",[439,17983,17887,17984,17987],{},[1002,17985,8234],{"href":17890,"rel":17986},[1006]," schonmal\nausführlicher erklärt)",[17894,17989],{},[439,17991,17992],{},"Ich habe zuletzt über das Thema “Security Testing in Continuous Integration” berichtet, das ich aktuell vertiefe und\nüber das ich dieses Jahr auf Meetups und Konferenzen spreche.",[1065,17994,17996],{"id":17995},"security-testing-in-continuous-integration","Security Testing in Continuous Integration",[439,17998,17999],{},"Die meisten aktuell genutzten Programmiersprachen bieten komplexe Frameworks an, mit deren Hilfe man leicht bestehende\nBibliotheken von Dritten einbinden kann, um sich viel Aufwand bei der Implementierung einer Lösung zu sparen. Leider\nbesteht hier die Chance, dass diese Bibliotheken Sicherheitslücken mit sich bringen, über die man aber schnell den\nÜberblick verliert. Da in den meisten Softwareprojekten sowieso schon Continuous Integration benutzt wird, um die\nbekannten Arbeitsschritte wie Tests, Packaging, Release, Deployments und Integrationstests ohne händischen Aufwand der\nEntwickler abzuwickeln, bietet es sich an, Tool-gestützte Sicherheitstests in die bestehenden CI-Jobs einzubinden. Für\ndiverse Sicherheitstests stehen Open-Source-Projekte zur Verfügung.",[439,18001,18002,18003,18008,18009,18016],{},"Nach einer grundlegenden Einführung zu Sicherheit in Softwareprojekten und -betrieb, zur Klassifizierung von\nSicherheitslücken in CVE, CPE und co, wurde anhand eines Java-Projekts auf Basis von SpringBoot und\nMaven ",[1002,18004,18007],{"href":18005,"rel":18006},"https://github.com/cy4n/broken",[1006],"(Link)"," demonstriert, wie man Schwachstellen in Dependencies mit Hilfe von ",[1002,18010,18013],{"href":18011,"rel":18012},"https://www.owasp.org/index.php/OWASP_Dependency_Check",[1006],[990,18014,18015],{},"OWASP Dependency-Check"," erkennen kann, ohne viel Ahnung von\nIT-Sicherheit zu haben.",[439,18018,18019,18020,1402],{},"Weiterhin wurde auf Basis eines bereitgestellten Docker-Images (Dockerfile im o.g. github-repo) gezeigt, dass auch im\nBetrieb leider viel schiefgehen kann, wenn man auf veraltete oder unbekannte Docker-Container setzt. Zum Scannen nach\nbekannten Schwachstellen in Containern empfiehlt sich der Einsatz von ",[1002,18021,18024],{"href":18022,"rel":18023},"https://github.com/coreos/clair",[1006],[990,18025,18026],{},"CoreOS Clair",[439,18028,18029,18030,18037],{},"Wenn man die Software dann gebaut und deployed hat, empfiehlt es sich (insofern die Applikation eine API oder Webseite\nanbietet), auch die angebotenen (HTTP-)Endpunkte zu scannen. An dieser Stelle wird keine statische Prüfung über den\nCode oder die installierten Pakete ausgeführt, sondern tatsächlich dynamisch ein Webservice oder eine API angegriffen.\nMit dem ",[1002,18031,18034],{"href":18032,"rel":18033},"https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project",[1006],[990,18035,18036],{},"OWASP ZAProxy"," lassen sich hier Links\nerkennen (spidern), danach wird versucht, diese Links, Formulare und weitere dynamische Elemente über Query-Parameter,\nFormulardaten und Schadcode anzugreifen (natürlich nur eigene Systeme).",[439,18039,18040],{},"All diese Tools lassen sich mit 2-3 Shell-Befehlen ausführen und sind daher super geeignet, um bestehende\nCI/CD-Pipelines zu ergänzen. Alle erlauben das Whitelisting von bestimmten Schwachstellen, so dass man sich wirklich\nauf neue Schwachstellen konzentrieren kann, um diese schnell (je nach Priorität) zu beheben oder Updates auszuführen.",[439,18042,18043],{},"Im Anschluss an die Präsentation und Demo haben einige Teilnehmer noch diskutiert, wie man nun mit den gefundenen\nSchwachstellen umgehen sollte, sowohl technisch als auch im Prozess der Weiterentwicklung, z.B. wer den Fix priorisiert\nund ausführt.",[17894,18045],{},[439,18047,18048,18049,18053],{},"Die Folien findet man ",[1002,18050,8234],{"href":18051,"rel":18052},"https://github.com/cy4n/talk-securitytesting/blob/master/securitytesting_slides.pdf",[1006],", ein\nausführlicher Artikel zu diesem Thema ist in Arbeit.",{"title":469,"searchDepth":507,"depth":507,"links":18055},[18056],{"id":17995,"depth":547,"text":17996},[1031],"2019-05-13T16:01:48","(Fast) jeden Freitag treffen sich interessierte synyxer in der Schule@synyx, um sich gegenseitig etwas Neues\\nbeizubringen.","https://synyx.de/blog/schulesynyx-security-testing/",{},"/blog/schulesynyx-security-testing",{"title":17969,"description":17978},"blog/schulesynyx-security-testing",[],"Freitags treffen sich interessierte synyxer in der Schule@synyx, um sich gegenseitig Neues beizubringen. Christian Kühn informierte in diesem Rahmen über “Security Testing in Continuous Integration”.","glJT62QMdgIAuJ3I57VV-cS4labBwL0DNFUyz1Kmu_s",{"id":18069,"title":18070,"author":18071,"body":18072,"category":18487,"date":18488,"description":18489,"extension":1034,"link":18490,"meta":18491,"navigation":916,"path":18492,"seo":18493,"slug":18076,"stem":18494,"tags":18495,"teaser":18498,"__hash__":18499},"blog/blog/java-deep-dive-class-file-format-for-debug-information.md","Java Deep Dive – class file format for debug information",[409],{"type":432,"value":18073,"toc":18481},[18074,18077,18080,18083,18095,18101,18112,18117,18120,18130,18134,18137,18148,18151,18160,18165,18192,18211,18216,18231,18237,18243,18248,18252,18262,18266,18269,18276,18281,18288,18293,18300,18310,18347,18350,18355,18358,18361,18370,18373,18379,18383,18386,18389,18415,18426,18429,18432,18436,18452,18459,18479],[435,18075,18070],{"id":18076},"java-deep-dive-class-file-format-for-debug-information",[439,18078,18079],{},"We are developing Apps for Android and one important aspect of our pipeline is automated device testing using Espresso.\nMy co-worker recently had to debug an Android test that would fail on only one of our various devices.",[439,18081,18082],{},"The test claimed that one of the checked Views was not visible (enough), under visual inspection the User Interface\nlooked fine though. To find out exactly why the offending View made Espresso fail, he decided to debug into the\nViewMatcher.",[439,18084,18085,18086,18089,18090,18094],{},"We were surprised that no variable values where available when debugging inside\nEspresso",[474,18087,18088],{},"[1","](",[1002,18091,18092],{"href":18092,"rel":18093},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#1",[1006],"). There was no way for us to read\nthe values of the offending View-object:",[439,18096,18097],{},[2205,18098],{"alt":18099,"src":18100},"Code Beispiel","https://media.synyx.de/uploads/2019/03/matcher-no-debug-1-cut-768x718.png",[439,18102,18103,18104,18107,18108,18111],{},"Here we even get the warning ",[471,18105,18106],{},"Variables are not avaible","when at least the local variable ",[990,18109,18110],{},"areaPercent"," with its value\nshould be displayed:",[439,18113,18114],{},[2205,18115],{"alt":18099,"src":18116},"https://media.synyx.de/uploads/2019/03/matcher-no-debug-2-768x634.png",[439,18118,18119],{},"Debugging our own code was working as expected, so the IDE and debugger itself seemed to work fine. We suspected that\nthe Espresso library might miss some critical debug information. The test was fixed shortly after, but we were still\ncurious about the behaviour of the debugger and wanted to know if the initial guess was correct.",[439,18121,18122,18125,18126,18129],{},[990,18123,18124],{},"What"," exactly is stored in java classes for debugging? ",[990,18127,18128],{},"How"," is that information stored? We will find out right here.",[3938,18131,18133],{"id":18132},"debug-information-format-in-java-class-files","Debug information format in Java class files",[439,18135,18136],{},"A class file can contain three different pieces of debug information:",[994,18138,18139,18142,18145],{},[997,18140,18141],{},"line information",[997,18143,18144],{},"local variable information",[997,18146,18147],{},"source information",[439,18149,18150],{},"This is explained when calling",[464,18152,18154],{"className":709,"code":18153,"language":711,"meta":469,"style":469},"javac -help\n",[471,18155,18156],{"__ignoreMap":469},[474,18157,18158],{"class":476,"line":477},[474,18159,18153],{},[439,18161,18162],{},[2205,18163],{"alt":18099,"src":18164},"https://media.synyx.de/uploads/2019/03/javac-options.png",[439,18166,18167,18168,18171,18172,18175,18176,18179,18180,18089,18183,18187,18188,18191],{},"Compiling with",[471,18169,18170],{},"javac","using default settings results in classes containing ",[990,18173,18174],{},"lines"," and ",[990,18177,18178],{},"vars","\ninformation",[474,18181,18182],{},"[2",[1002,18184,18185],{"href":18185,"rel":18186},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#2",[1006],"). Existing class files can be\nanalyzed using the ",[471,18189,18190],{},"javap","tool that is part of the JDK.",[439,18193,18194,18195,18089,18198,18202,18203,18206,18207,18210],{},"We decided to download",[474,18196,18197],{},"[3",[1002,18199,18200],{"href":18200,"rel":18201},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#3",[1006],") the Espresso core\nlibrary from a maven repository, unzip the Android ",[990,18204,18205],{},"aar","-Archive and finally unzip the contained classes.jar. Running\n",[471,18208,18209],{},"javap -l"," on our ViewMatchers class gave the following output:",[439,18212,18213],{},[2205,18214],{"alt":18099,"src":18215},"https://media.synyx.de/uploads/2019/03/javap-debuggable-768x158.png",[439,18217,18218,18219,18222,18223,18226,18227,18230],{},"Adding the ",[990,18220,18221],{},"-l"," option should print line ",[448,18224,18225],{},"and"," variable information but we only find ",[990,18228,18229],{},"LineNumberTable"," entries.",[439,18232,18233,18234,18236],{},"Using a hex-editor we can verify that the class file seems to contain a ",[990,18235,18229],{}," of some kind:",[439,18238,18239],{},[2205,18240],{"alt":18241,"src":18242},"espresso test","https://media.synyx.de/uploads/2019/03/bless-espresso-768x439.png",[439,18244,18245,18246,6562],{},"We check one of our own, fully debuggable class files again with ",[471,18247,18209],{},[439,18249,18250],{},[2205,18251],{"alt":18099,"src":18215},[439,18253,18254,18255,18257,18258,18261],{},"We can see the ",[990,18256,18229],{}," as well as a ",[990,18259,18260],{},"LocalVariableTable"," that was missing in the ViewMatchers class.",[3938,18263,18265],{"id":18264},"assumption-verification","Assumption & Verification",[439,18267,18268],{},"At this point we can assume:",[439,18270,18271],{},[448,18272,18273,18275],{},[990,18274,18260],{}," is required to debug variable values",[439,18277,18278],{},[990,18279,18280],{},"Comparing behaviour and class content between Espresso and our own code we can assume LocalVariableTable is required to\ndebug variable values.",[439,18282,18283],{},[448,18284,18285,18287],{},[990,18286,18229],{}," is required to use breakpoints",[439,18289,18290],{},[990,18291,18292],{},"Unrelated to the above behaviour: A single line of source code can be translated to many bytecode statements. Placing a\nbreakpoint on a source code line requires a debugger to know which bytecode statement to break at. Therefor, omitting\nthe LineNumberTable should break the ability to set and trigger breakpoints.",[439,18294,18295,18296,18299],{},"We will try to verify both of these assumptions by compiling our own class using ",[471,18297,18298],{},"javac -g:none"," and observe how the\ndebug behaviour changes.",[439,18301,18302,18303,18089,18306,17548],{},"The Android Gradle/Groovy build is sometimes difficult to work with, this is a solution to add arguments to the java\ncompiler ",[474,18304,18305],{},"[4",[1002,18307,18308],{"href":18308,"rel":18309},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#4",[1006],[464,18311,18313],{"className":709,"code":18312,"language":711,"meta":469,"style":469},"allprojects {\n gradle.projectsEvaluated {\n tasks.withType(JavaCompile) {\n options.compilerArgs << "-g:none"\n }\n }\n}\n",[471,18314,18315,18320,18325,18330,18335,18339,18343],{"__ignoreMap":469},[474,18316,18317],{"class":476,"line":477},[474,18318,18319],{},"allprojects {\n",[474,18321,18322],{"class":476,"line":507},[474,18323,18324],{}," gradle.projectsEvaluated {\n",[474,18326,18327],{"class":476,"line":547},[474,18328,18329],{}," tasks.withType(JavaCompile) {\n",[474,18331,18332],{"class":476,"line":584},[474,18333,18334],{}," options.compilerArgs << "-g:none"\n",[474,18336,18337],{"class":476,"line":607},[474,18338,5704],{},[474,18340,18341],{"class":476,"line":642},[474,18342,1276],{},[474,18344,18345],{"class":476,"line":663},[474,18346,703],{},[439,18348,18349],{},"After cleaning & rebuilding our app we restart the test and try to debug:",[439,18351,18352],{},[2205,18353],{"alt":18099,"src":18354},"https://media.synyx.de/uploads/2019/03/debug-own-no-debuginfo-768x301.png",[439,18356,18357],{},"We can see the breakpoint is disabled! Additionally, after starting the test we fell right through to the next\nstackframe in ViewMatchers. We did not stop at the breakpoint! Breaking in ViewMatchers and dropping down the stack we\ncan also confirm we there is no local variable information available.",[439,18359,18360],{},"Checking the output of",[464,18362,18364],{"className":709,"code":18363,"language":711,"meta":469,"style":469},"javap -l\n",[471,18365,18366],{"__ignoreMap":469},[474,18367,18368],{"class":476,"line":477},[474,18369,18363],{},[439,18371,18372],{},"again we can verify the missing debug information:",[439,18374,18375],{},[2205,18376],{"alt":18377,"src":18378},"Code Ausgabe","https://media.synyx.de/uploads/2019/03/javap-own-nondebuggable-768x183.png",[3938,18380,18382],{"id":18381},"final-thoughts","Final Thoughts",[439,18384,18385],{},"We have seen how the IDEs debug behaviour and capabilities relate to the information embedded in the class files.",[439,18387,18388],{},"This is relevant to Android just as well as “traditional” java applications. Android also relies on the information\nembedded in the class files, as we have seen when unpacking the Espresso-Core AAR archive. We were able to use basic\ntools from the JDK (javap) to analyze Espresso classes.",[439,18390,18391,18392,18395,18396,18398,18399,18402,18403,18406,18407,18089,18410,18414],{},"We still don’t know ",[448,18393,18394],{},"why"," Espresso is missing the ",[990,18397,18260],{},". Building our App in the ",[990,18400,18401],{},"Release","-Variant\nwith ",[990,18404,18405],{},"debuggable=false"," did still include the full debug information. It is possible that Proguard is responsible for\nstripping the information from the class files, it seems at least\ncapable ",[474,18408,18409],{},"[5",[1002,18411,18412],{"href":18412,"rel":18413},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#5",[1006],") to do so. Unfortunately, the\ndocumentation for ProGuard is not explaining when and how debug information is manipulated.",[439,18416,18417,18418,18089,18421,18425],{},"For a deeper technical reading of the class file format you can consult the documentation provided by\nOracle ",[474,18419,18420],{},"[6",[1002,18422,18423],{"href":18423,"rel":18424},"https://synyx.de/blog/2019-03-14-android-debug-class-format/?page=1#6",[1006],"). Be careful to check the correct\njava version.",[439,18427,18428],{},"And finally, the next time you debug an application you hopefully have a better understanding of some of the involved\nparts.",[439,18430,18431],{},"We might possibly explore the remaining open questions in a future blog post.",[3938,18433,18435],{"id":18434},"references","References",[8310,18437,18438,18444,18450],{},[997,18439,18440],{},[1002,18441,18442],{"href":18442,"rel":18443},"https://developer.android.com/training/testing/espresso",[1006],[997,18445,18446],{},[1002,18447,18448],{"href":18448,"rel":18449},"https://www.logicbig.com/how-to/java-command/compile-with-debug-info.html",[1006],[997,18451],{},[439,18453,18454,18455],{},"e.g.: ",[1002,18456,18457],{"href":18457,"rel":18458},"https://mvnrepository.com/artifact/com.android.support.test.espresso/espresso-core/3.0.2",[1006],[8310,18460,18461,18467,18473],{"start":584},[997,18462,18463],{},[1002,18464,18465],{"href":18465,"rel":18466},"https://stackoverflow.com/a/42297051",[1006],[997,18468,18469],{},[1002,18470,18471],{"href":18471,"rel":18472},"https://stackoverflow.com/a/5258014",[1006],[997,18474,18475],{},[1002,18476,18477],{"href":18477,"rel":18478},"https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.12",[1006],[1024,18480,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":18482},[18483,18484,18485,18486],{"id":18132,"depth":507,"text":18133},{"id":18264,"depth":507,"text":18265},{"id":18381,"depth":507,"text":18382},{"id":18434,"depth":507,"text":18435},[1031],"2019-03-14T12:15:02","We are developing Apps for Android and one important aspect of our pipeline is automated device testing using Espresso.\\nMy co-worker recently had to debug an Android test that would fail on only one of our various devices.","https://synyx.de/blog/java-deep-dive-class-file-format-for-debug-information/",{},"/blog/java-deep-dive-class-file-format-for-debug-information",{"title":18070,"description":18079},"blog/java-deep-dive-class-file-format-for-debug-information",[11132,711,18496,18497],"mobile","test","We are developing Apps for Android and one important aspect of our pipeline is automated device testing using Espresso. My co-worker recently had to debug an Android test that would fail on only one of our various devices.","5pjbPbLHPEoE12jNYmD37eHff1rUiJO5bfuWB6Ddsvc",{"id":18501,"title":18502,"author":18503,"body":18504,"category":18633,"date":18634,"description":18635,"extension":1034,"link":18636,"meta":18637,"navigation":916,"path":18638,"seo":18639,"slug":18641,"stem":18642,"tags":18643,"teaser":18644,"__hash__":18645},"blog/blog/von-schlipstraegern-raketenwissenschaften-und-einhoernern-die-geschichte-eines-entwicklers-bei-synyx.md","Von Schlipsträgern, Raketenwissenschaften und Einhörnern – Die Geschichte eines Entwicklers bei synyx",[30],{"type":432,"value":18505,"toc":18627},[18506,18509,18518,18523,18526,18532,18536,18539,18542,18545,18549,18556,18568,18571,18597,18603,18607,18610,18616,18618,18621],[435,18507,18502],{"id":18508},"von-schlipsträgern-raketenwissenschaften-und-einhörnern-die-geschichte-eines-entwicklers-bei-synyx",[439,18510,18511,18512,18517],{},"Ein dickes Grinsen steht dem Vorlesenden ins Gesicht, als er zum Abschluss des Dailys den Spruch des Tages aus\ndem ",[1002,18513,18516],{"href":18514,"rel":18515},"http://www.der-falsche-kalender.de/",[1006],"Falschen Kalender"," von Marc-Uwe Kling präsentiert:",[11947,18519,18520],{},[439,18521,18522],{},"“Glück ist ganz einfach. Gute Gesundheit und ein schlechtes Gedächtnis.\" — Dorie",[439,18524,18525],{},"Das ist der Sebastian, genannt Seb. In der Kürze liegt die Würze, außer es geht um die Benennung von sprechenden\nJava-Methoden. Der Seb ist nicht nur leidenschaftlicher Erklimmer von Felsbrocken aller Art — und das sogar an der\nfrischen Luft — sondern auch ein synyx Urgestein.",[439,18527,18528],{},[2205,18529],{"alt":18530,"src":18531},"Seb am Arbeiten","https://media.synyx.de/uploads/2019/02/seb1-768x512.jpg",[3938,18533,18535],{"id":18534},"von-schlipsträgern-und-defekten-live-cds","Von Schlipsträgern und defekten Live-CDs",[439,18537,18538],{},"Einst, in einer fast vergessenen Ära seines Studentenlebens — es war das Jahr 2007 — schlenderte der Seb auf einer\nFirmenkontaktmesse umher. Auf der Suche nach Antworten auf die wirklich wichtigen Fragen des Lebens stieß er so auf den\nMessestand einer kleinen Klitsche namens synyx. Nach einem kleinen Plausch mit den zwei lässigen Gestalten, die da so\nrumhingen, war für Seb klar: Hier hatte er keine Schlips tragenden Dudes von der Personalabteilung vor sich, sondern\nTechies aus Leidenschaft, die wissen, wovon sie sprechen. Auf dieses Arbeitsumfeld hatte er richtig Bock.",[439,18540,18541],{},"Das Giveaway in Form der sagenumwobenen Minos Live-CD — ein damals von synyx entwickelter modularer Baukasten für\nUnternehmenssoftware — stellte sich zuhause zwar als inkompatibel mit seiner Hardware heraus, aber das störte den Seb\nnicht weiter. Denn das Gesamtbild stimmte. Prompt wurde aus einer Bewerbung ein Arbeitsvertrag. Seb stieg bei synyx als\nstudentische Hilfskraft ein, brachte seine Diplomarbeit erfolgreich hinter sich und blieb. Bis zum heutigen Tage.",[439,18543,18544],{},"Mit mittlerweile über 65 Mitarbeitern lässt sich synyx längst nicht mehr als kleine Klitsche bezeichnen. Doch auch heute\nnoch sucht man hier vergeblich nach Schlipsträgern und einer Personalabteilung. Minos Live-CDs sind schon eine ganze\nWeile Geschichte, aber wer weiß, was die Zukunft noch bringt. Irgendwann gilt ja fast alles wieder als retro.",[3938,18546,18548],{"id":18547},"von-hölzernen-usb-sticks-und-raketenwissenschaften","Von hölzernen USB-Sticks und Raketenwissenschaften",[439,18550,18551,18552,18555],{},"Da der Seb praktisch zum synyx Inventar gehört, ist er auch ein waschechter Pionier in einem großen Kundenprojekt. Er\nsaß in vorderster Reihe, als im Jahr 2010 die wilde Fahrt mit dem\nLogistikunternehmen ",[1002,18553,8573],{"href":8571,"rel":18554},[1006]," begann.",[439,18557,18558,18561,18562,18567],{},[990,18559,18560],{},"“Schau‘ dir mal den Code an”"," — hieß es, als er eines schönen Tages einen kunstvollen USB-Stick aus Holz in die Hand\ngedrückt bekam. Dies war nicht nur die Geburtsstunde eines aufregenden ",[1002,18563,18566],{"href":18564,"rel":18565},"https://synyx.de/was/codeclinic",[1006],"Code Clinic","\nProjektes, sondern auch einer bis zum heutigen Tage andauernden Partnerschaft zwischen synyx und Contargo. Mittlerweile\ngibt es unzählige freshe Projekte, die synyx für und mit Contargo auf die Straße bringt.",[439,18569,18570],{},"Wie es sich für einen alten Hasen gehört, hat der Seb schon viele Projekte und Teams durchlaufen. Heute ist er Teil des\ngrandiosen Team Rocket. Mit Raketenwissenschaften hat das zwar weniger zu tun, aber schließlich gehören Raketen auch (\nnoch) nicht zur Transportkette.",[439,18572,18573,18574,18577,18578,18581,18582,18587,18588,18593,18594],{},"Team Rocket ist zuhause in einer verteilten System-Landschaft. Manch einer würde hier vielleicht den Begriff\n",[990,18575,18576],{},"“Microservices”"," raushauen. Doch da das einen gewissen Beigeschmack von ",[990,18579,18580],{},"“Wir wollen voll hipp und trendy wirken”","\nerregen könnte, spricht der Seb lieber von vielfältigen Projekten, meist basierend\nauf ",[1002,18583,18586],{"href":18584,"rel":18585},"http://spring.io/projects/spring-boot",[1006],"Spring Boot",", die über ",[1002,18589,18592],{"href":18590,"rel":18591},"https://www.amqp.org/",[1006],"AMQP"," miteinander\nkommunizieren. Allein beim Team Rocket liegen rund 50 solcher Projekte an der Zahl. Da den Durchblick zu behalten, ist\nnicht immer leicht. Doch zum Glück heißt es trotzdem nur selten: ",[990,18595,18596],{},"“Das war mal wieder ein Schuss in den Ofen!”",[439,18598,18599],{},[2205,18600],{"alt":18601,"src":18602},"Kalender zitat: Um Jahre voraus","https://media.synyx.de/uploads/2019/02/seb2-768x512.jpg",[3938,18604,18606],{"id":18605},"von-code-monkeys-und-einhörnern","Von Code Monkeys und Einhörnern",[439,18608,18609],{},"Empathische Informatiker mit kommunikativen Skills — das sind doch bloß Fabelwesen wie Einhörner, oder? Tja, falsch\ngedacht. Code Monkeys, die im stillen Kämmerlein präzise Anforderungen in die Tasten hauen, wären bei synyx reichlich\nfehl am Platze. Stattdessen heißt es hier mitdenken, mitdenken und — Überraschung — mitdenken.",[439,18611,18612,18615],{},[990,18613,18614],{},"“Was bringt dem Kunden (den größten) Mehrwert?”"," — Diese Frage steht stets im Vordergrund und erfordert Empathie für\nden Kunden. Die Teammitglieder bringen eigenständig Themen in die Plannings mit ein und sprechen Schwachstellen in\nPlanung, Umsetzung und Technologien an. Natürlich entscheidet am Ende der Kunde, welche Themen wann eingeplant werden,\ndoch die Verantwortung für gut benutzbare Software liegt letztlich bei allen Beteiligten. Genau das gibt der täglichen\nArbeit erst die richtige Würze.",[3938,18617,17630],{"id":17629},[439,18619,18620],{},"Du ziehst Techies aus Leidenschaft Schlipsträgern vor und hast Bock, eigenverantwortlich an spannenden Projekten mit\nmodernen Technologien mitzuwirken?",[439,18622,18623,18624,18626],{},"Dann melde Dich bei uns unter ",[1002,18625,15565],{"href":15564}," und erzähl uns, wer Du bist und was Du so kannst.\nZeugnisse sind uns nicht so wichtig. Statt Deine Grundschulnoten zu analysieren, wollen wir lieber mehr über Deine\nInteressen und Fähigkeiten erfahren. Wir freuen uns, von Dir zu hören!",{"title":469,"searchDepth":507,"depth":507,"links":18628},[18629,18630,18631,18632],{"id":18534,"depth":507,"text":18535},{"id":18547,"depth":507,"text":18548},{"id":18605,"depth":507,"text":18606},{"id":17629,"depth":507,"text":17630},[1031],"2019-02-12T10:47:40","Ein dickes Grinsen steht dem Vorlesenden ins Gesicht, als er zum Abschluss des Dailys den Spruch des Tages aus\\ndem Falschen Kalender von Marc-Uwe Kling präsentiert:","https://synyx.de/blog/von-schlipstraegern-raketenwissenschaften-und-einhoernern-die-geschichte-eines-entwicklers-bei-synyx/",{},"/blog/von-schlipstraegern-raketenwissenschaften-und-einhoernern-die-geschichte-eines-entwicklers-bei-synyx",{"title":18502,"description":18640},"Ein dickes Grinsen steht dem Vorlesenden ins Gesicht, als er zum Abschluss des Dailys den Spruch des Tages aus\ndem Falschen Kalender von Marc-Uwe Kling präsentiert:","von-schlipstraegern-raketenwissenschaften-und-einhoernern-die-geschichte-eines-entwicklers-bei-synyx","blog/von-schlipstraegern-raketenwissenschaften-und-einhoernern-die-geschichte-eines-entwicklers-bei-synyx",[3592,17866,17867,17655],"Ein ganz normaler Morgen. Ein ganz normales Daily. Die versammelte Mannschaft.","OEqZ98Gd5nLrPjyxiPm6Kxbmyf8khdQ3bhmWOwdZYUc",{"id":18647,"title":18648,"author":18649,"body":18650,"category":18796,"date":18797,"description":18798,"extension":1034,"link":18799,"meta":18800,"navigation":916,"path":18801,"seo":18802,"slug":18654,"stem":18804,"tags":18805,"teaser":18809,"__hash__":18810},"blog/blog/devoxx4kids-goes-iot.md","Devoxx4Kids goes IoT",[60,54],{"type":432,"value":18651,"toc":18791},[18652,18655,18669,18675,18678,18681,18690,18694,18701,18707,18710,18714,18721,18727,18733,18736,18740,18743,18749,18752,18755,18774,18782,18788],[435,18653,18648],{"id":18654},"devoxx4kids-goes-iot",[439,18656,18657,18661,18662,18664],{},[2205,18658],{"alt":18659,"src":18660},"YouTube","http://localhost:8080/wp-content/plugins/borlabs-cookie/assets/images/cb-no-thumbnail.png?rel=0&modestbranding=1&iv_load_policy=3","\nMit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.",[12024,18663],{},[1002,18665,18668],{"href":18666,"rel":18667},"https://policies.google.com/privacy",[1006],"Mehr erfahren",[439,18670,18671],{},[1002,18672,18674],{"href":18673},"#","Video laden",[439,18676,18677],{},"YouTube immer entsperren",[439,18679,18680],{},"Devoxx4Kids 2018 | Workshops Arduino, Calliope und Mindstorms",[439,18682,18683,18684,18689],{},"Bei der neunten Devoxx4Kids in Karlsruhe am 20.10.2018 stand Elektronik und das Internet der Dinge hoch im Kurs! In\nden Räumlichkeiten der ",[1002,18685,18688],{"href":18686,"rel":18687},"https://karlshochschule.de/de/",[1006],"Karlshochschule"," konnten insgesamt 30 Kinder zusammen mit\nunseren 19 Mentoren spielerisch Konzepte der Elektronik und Grundlagen der Programmierung erlernen. Für die Herbst\nAusgabe der Devoxx4Kids wurden dieses Mal drei komplett neue Workshop ausgearbeitet um auch “Wiederholungstätern” unter\nden Kindern viel Spaß und Spannung bieten zu können. Für die Herbstausgabe der Devoxx4Kids wurden dieses Mal drei\nkomplett neue Workshop ausgearbeitet, um auch “Wiederholungstätern” unter den Kindern viel Spaß und Spannung bieten zu\nkönnen.",[3938,18691,18693],{"id":18692},"arduino","Arduino",[439,18695,3786,18696,18700],{},[1002,18697,18693],{"href":18698,"rel":18699},"https://www.arduino.cc/",[1006]," Workshop tauchten die Kids ab in die Elektronik. Mit Steckbrett, LEDs und Sensoren\nging es darum, erste Schaltungen zu stecken und damit eine kleine Temperatur Messstation aufzubauen.",[439,18702,18703],{},[2205,18704],{"alt":18705,"src":18706},"LEDs im Detail","https://media.synyx.de/uploads/2019/03/06-768x512.jpg",[439,18708,18709],{},"Was ist nötig, um eine LED zum Leuchten zu bringen?",[3938,18711,18713],{"id":18712},"calliope","Calliope",[439,18715,3480,18716,18720],{},[1002,18717,18713],{"href":18718,"rel":18719},"https://calliope.cc/",[1006]," ist eine kleine Platine mit vielen Sensoren, Lautsprecher und einem kleinen\neinfachen Display. Damit ausgestattet wurden geheime Nachrichten versendet oder aber auch die Lautstärke im Raum\ngemessen.",[439,18722,18723],{},[2205,18724],{"alt":18725,"src":18726},"Calliope Lautstärke Messungen","https://media.synyx.de/uploads/2019/03/07-768x512.jpg",[439,18728,18729],{},[2205,18730],{"alt":18731,"src":18732},"calliope übungen auf der devoxx4kids","https://media.synyx.de/uploads/2019/03/08-768x512.jpg",[439,18734,18735],{},"Die Calliope-Sterne zur Lautstärke Messung: Wenn es zu laut ist, wird die Ampel rot!",[3938,18737,18739],{"id":18738},"mbot","MBot",[439,18741,18742],{},"Dieser Workshop war der Liebling der Kinder! Hier kam richtig Fahrt auf! Und das nicht nur weil der MBot durch sein\nLächeln besticht sondern auch die Bewegung mit ins Spiel kam. Ein MBot ist kleiner Roboter welcher sich mit Motoren\nfortbewegen kann. Über Abstandssensoren kann er Hindernissen ausweichen und somit auf seine Umwelt reagieren.",[439,18744,18745],{},[2205,18746],{"alt":18747,"src":18748},"mbots Test auf der Devoxx4kids","https://media.synyx.de/uploads/2019/03/09-768x512.jpg",[439,18750,18751],{},"Die MBots hatten es schwer, einen Weg aus dem Käfig zu finden.",[439,18753,18754],{},"Um die Ergebnisse des Tages festzuhalten, konnten die Kinder dieses Mal ihre ersten kleinen Programme auf einem\ngesponsorten USB-Stick mit nach Hause nehmen.",[439,18756,18757,18758,18762,18763,18768,18769,18773],{},"Ein großer Dank geht natürlich an die Mentoren und unsere Sponsoren ",[1002,18759,389],{"href":18760,"rel":18761},"https://synyx.de",[1006],"\nund ",[1002,18764,18767],{"href":18765,"rel":18766},"http://emendare.de",[1006],"Emendare"," ohne die die Devoxx4Kids nicht möglich wäre. Außerdem sind wir sehr dankbar für die\nRäumlichkeiten in der ",[1002,18770,18688],{"href":18771,"rel":18772},"http://karlshochschule.de",[1006],"!",[439,18775,18776,18777,18781],{},"Die Materialien der verschiedenen Workshops der Devoxx4Kids Deutschland sind\nauf ",[1002,18778,4042],{"href":18779,"rel":18780},"https://github.com/Devoxx4KidsDE",[1006]," zu finden.",[439,18783,18784],{},[2205,18785],{"alt":18786,"src":18787},"devoxx4kids Abschied","https://media.synyx.de/uploads/2019/03/10-768x512.jpg",[439,18789,18790],{},"Bis zum nächsten Mal!",{"title":469,"searchDepth":507,"depth":507,"links":18792},[18793,18794,18795],{"id":18692,"depth":507,"text":18693},{"id":18712,"depth":507,"text":18713},{"id":18738,"depth":507,"text":18739},[4216,1031],"2018-11-30T11:57:58","\\nMit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.Mehr erfahren","https://synyx.de/blog/devoxx4kids-goes-iot/",{},"/blog/devoxx4kids-goes-iot",{"title":18648,"description":18803},"\nMit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von YouTube.Mehr erfahren","blog/devoxx4kids-goes-iot",[18806,18807,18808],"devoxx4dis","iot","karlsruhe","Bei der neunten Devoxx4Kids in Karlsruhe am 20.10.2018 stand Elektronik und das Internet der Dinge hoch im Kurs! In den Räumlichkeiten der Karlshochschule konnten insgesamt 30 Kinder zusammen mit unseren 19 Mentoren spielerisch Konzepte der Elektronik und Grundlagen der Programmierung erlernen.","GsYHF7tRhPozLBRdH5SQkthit1kuegh6znaVr6CycPA",{"id":18812,"title":18813,"author":18814,"body":18815,"category":18902,"date":18903,"description":18904,"extension":1034,"link":18905,"meta":18906,"navigation":916,"path":18907,"seo":18908,"slug":18819,"stem":18909,"tags":18910,"teaser":18914,"__hash__":18915},"blog/blog/marketing-bei-synyx-was-geht-denn-da-so.md","Marketing bei synyx – Was geht denn da so?",[30],{"type":432,"value":18816,"toc":18897},[18817,18820,18826,18829,18832,18838,18842,18845,18851,18854,18857,18860,18864,18867,18873,18876,18882,18885,18889,18892],[435,18818,18813],{"id":18819},"marketing-bei-synyx-was-geht-denn-da-so",[439,18821,18822,18825],{},[990,18823,18824],{},"„Guten Morgen, liebe Kellerkinder“"," — schallt eine sonore Stimme durch das jäh erhellte Großraumbüro.",[439,18827,18828],{},"Das ist der Thomas. Der Thomas ist der Chef in diesem kuriosen Laden namens synyx. Zumindest einer der drei Chefs. Der\nThomas hat es gerne hell. Er ist die aufgehende Sonne des Marketing Teams und erleuchtet dieses nicht nur mit\nHalogenstrahlern, sondern auch als Product Owner.",[439,18830,18831],{},"Dieses Marketing Team — Wer sind die denn eigentlich und was machen die so den ganzen Tag?",[439,18833,18834],{},[2205,18835],{"alt":18836,"src":18837},"Code with attitude Sticker","https://media.synyx.de/uploads/2019/02/sticker_code-768x576.jpg",[3938,18839,18841],{"id":18840},"ey-dude-wo-kommen-eigentlich-die-geilen-sticker-her","Ey Dude, wo kommen eigentlich die geilen Sticker her?",[439,18843,18844],{},"Es ist 10:30 Uhr an einem nebligen Dienstag im Kreativraum. Drei fröhlich schnatternde Gestalten kritzeln an einer Wand\nherum, die von oben bis unten mit Whiteboards ausgekleidet ist. Konzeptionsmeeting nennen die das. Vielleicht atmen sie\naber auch nur gerne die Dämpfe der Whiteboard-Marker ein.",[439,18846,18847,18850],{},[990,18848,18849],{},"„Ständig twittern wir bloß darüber, was für Hippies wir sind. Dabei haben wir hier nicht nur Barfußhelden und\nLongboardfahrer, die Leute können ja sogar tatsächlich was. Und zwar nicht grad wenig. Das sollten wir viel mehr nach\naußen zeigen.“"," — philosophiert der langhaarige Hippie der Gruppe namens Schdaff.",[439,18852,18853],{},"Eigentlich heißt der Schdaff ja Stephan, aber als Stephan kennt ihn hier niemand. Der Schdaff hat dauernd neue Ideen im\nKopf und redet gerne und viel darüber. Manchmal sagt er sogar etwas Sinnvolles. In diesem Haufen von sonderbaren\nSoftwareentwicklern und bärtigen Admins ist der Schdaff ein kleiner Exot. Der Schdaff ist nämlich\nKommunikationsdesigner.",[439,18855,18856],{},"Im Zoo hält man bedrohte Tierarten meistens pärchenweise. Ähnlich läuft das mit Designern in einer IT-Firma ab. Vor\netwa einem Jahr wurde dem Schdaff ein Partner zur Seite gestellt. Nämlich der Joff. Der Joff ist ebenfalls Designer. Man\nerkennt ihn ganz leicht an seinen Batman-Hausschuhen und der Batman-Maske. Das ist nämlich seine wahre Identität. Aber\npsst, das ist natürlich streng geheim.",[439,18858,18859],{},"Zusammen tüfteln die zwei Paradiesvögel an visuellen Konzepten herum, bis aus Ideen und wilden Kritzeleien echt geile\nDinge herauspurzeln – von Plakaten und Icons, bis hin zu T-Shirts und Stickern. Die Beiden produzieren heißen Scheiß am\nlaufenden Band.",[3938,18861,18863],{"id":18862},"ja-ist-denn-schon-wieder-weihnachten","Ja, ist denn schon wieder Weihnachten?",[439,18865,18866],{},"Es ist Anfang November. Obwohl die Regale in den Supermärkten bereits seit August mit Lebkuchen bestückt sind, kommt\nWeihnachten wie jedes Jahr völlig überraschend.",[439,18868,18869,18872],{},[990,18870,18871],{},"“Wir müssen uns endlich um die Weihnachtspost kümmern!”"," — ertönt eine besorgte Frauenstimme.",[439,18874,18875],{},"Das ist die Katja. Die Katja ist ein waschechtes synyx Urgestein. Sie ist Teil von synyx, seit es synyx gibt. Und das\nsind inzwischen ein paar Jährchen. Die Katja ist die eierlegende Wollmilchsau des Marketing Teams. Genauer gesagt war\ndie Katja viele Jahre lang ganz alleine das Marketing Team. Die Katja hat den Durchblick und Überblick über so ziemlich\nalles. Sie organisiert Events und Sponsorings, betreibt Kommunikation mit der halben Welt und kümmert sich um alles, was\nin irgendeiner Weise mit Marketing zu tun hat. Dazu gehört zum Beispiel auch die Konzeption und Organisation von\nMerchandise-Artikeln und das Pflegen und Überwachen von Social-Media-Kanälen. Bei der vielen Kommunikation mit\nKollegen und Chefs ist ihr ursprünglich erlernter Beruf der Erzieherin eine ungemeine Hilfe. Denn bei synyx gibt es\nSechsjährige jeden Alters.",[439,18877,18878],{},[2205,18879],{"alt":18880,"src":18881},"Synema Darth Vader und Stormtrooper Figuren","https://media.synyx.de/uploads/2019/02/synema_lichtschwerter-768x512.jpg",[439,18883,18884],{},"Die Katja macht ihren Job echt gern. Oft bezeichnet sie synyx als ihr Baby. Doch was die Katja noch lieber hat, ist das\nMeer. Deshalb hat die Katja leider beschlossen, in den Norden Deutschlands zu ziehen und ihren Posten als eierlegende\nWollmilchsau aufzugeben. Vielleicht ist das mit dem Meer aber auch bloß eine Ausrede, um vor dem beginnenden Umbau der\nRheinbrücke zu fliehen.",[3938,18886,18888],{"id":18887},"eierlegende-marketing-wollmilchsau-mit-durchblick-gesucht","Eierlegende Marketing-Wollmilchsau mit Durchblick gesucht",[439,18890,18891],{},"Was auch immer nun stimmen mag, Katjas anstehender Ausstieg hat dazu geführt, einen Blog Post zu konzipieren, der einen\nauthentischen Einblick in den Arbeitsalltag des Marketing Teams liefern soll. Herausgekommen ist dieser Text. Falls Du,\nlieber Leser, tatsächlich bis hierhin gelesen haben solltest und noch nicht die Hände über dem Kopf zusammengeschlagen\nhast, vielleicht hast Du ja Interesse an einem Job bei synyx als eierlegende Marketing-Wollmilchsau? 😉",[439,18893,18894,18895,18626],{},"Melde Dich doch bei uns unter ",[1002,18896,15565],{"href":15564},{"title":469,"searchDepth":507,"depth":507,"links":18898},[18899,18900,18901],{"id":18840,"depth":507,"text":18841},{"id":18862,"depth":507,"text":18863},{"id":18887,"depth":507,"text":18888},[1031],"2018-11-29T10:46:42","„Guten Morgen, liebe Kellerkinder“ — schallt eine sonore Stimme durch das jäh erhellte Großraumbüro.","https://synyx.de/blog/marketing-bei-synyx-was-geht-denn-da-so/",{},"/blog/marketing-bei-synyx-was-geht-denn-da-so",{"title":18813,"description":18904},"blog/marketing-bei-synyx-was-geht-denn-da-so",[3592,18911,18912,18913],"design","marketing","unternehmenskommunikation","„Guten Morgen, liebe Kellerkinder“ — schallt eine sonore Stimme durch das jäh erhellte Großraumbüro Das ist der Thomas. Der Thomas ist der Chef in diesem kuriosen Laden namens synyx. Zumindest einer der drei Chefs. Der Thomas hat es gerne hell. Er ist die aufgehende Sonne des Marketing Teams und erleuchtet dieses nicht nur mit Halogenstrahlern, sondern auch als Product Owner Dieses Marketing Team — Wer sind die denn eigentlich und was machen die so den ganzen Tag?","IL-7ErnYK_7GvNrvwj3aF6cLB2kk_1QygLorrdU6Quk",{"id":18917,"title":18918,"author":18919,"body":18920,"category":19013,"date":19014,"description":19015,"extension":1034,"link":19016,"meta":19017,"navigation":916,"path":19018,"seo":19019,"slug":18924,"stem":19020,"tags":19021,"teaser":19022,"__hash__":19023},"blog/blog/code-with-attitude-part-1-values.md","Code with Attitude – Part 1: Values",[97],{"type":432,"value":18921,"toc":19010},[18922,18925,18928,18933,18940,18944,18947,18956,18962,18965,18971,18974,18977,18983,18986,18989,18992,18997,19000,19003],[435,18923,18918],{"id":18924},"code-with-attitude-part-1-values",[439,18926,18927],{},"At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive\nconcerning the way we work with clients, the software community and each other. The line is:",[439,18929,18930],{},[448,18931,18932],{},"Code with Attitude",[439,18934,18935,18936,18939],{},"In the instant I heard this simple sentence for the first time it triggered a multitude of associations, memories and\nemotions that I connect with the word “attitude” in conjunction with software development. One after another all the\nsituations from different projects during my time at synyx came to mind, where a certain expression of attitude helped\non the way to success. This tells me that our marketing dudes did an excellent job expressing the synyx mindset in one\npowerful and simple statement and also that I have ",[990,18937,18938],{},"a lot"," to say about it. This is why I want to share my\ninterpretation of “Code with Attitude” within this blog series.",[3938,18941,18943],{"id":18942},"part-1-courage-to-be-true-to-your-values","Part 1 – Courage to be true to your values",[439,18945,18946],{},"As a human being everybody has a bunch of values and moral standards that you deem to be important and that greatly\ninfluence where and how you live and work. Now you are probably thinking of something like “Don’t code guidance software\nfor nuclear weapons” or “If you manager tells you to tweak the emission control software for the new car model – say\nno!”. Although these are obvious examples, this is not quite what I mean as I have never been in an extreme situation\nlike this – nor have 99% of software developers. What I mean is a set of values, that influence your day-to-day work,\nlike the dedication to quality and sustainability, respectful communication or keeping an open mind.",[439,18948,18949,18950,18955],{},"Behind the term “your values” lies more complexity than you might expect. It does not only mean your personal,\nindividual values but also those ",[1002,18951,18954],{"href":18952,"rel":18953},"https://synyx.de/wer/werte/",[1006],"of your employer"," and – if your employer is a service\nprovider – even the values of your clients. If you are working for your client it is in his best interest when you take\nhis sense of what is right or wrong into account for the actions you take or don’t take. Ideally those three value\nsystems should be mostly overlapping. If they are heavily misaligned this is a situation that you should not be in.",[439,18957,18958],{},[2205,18959],{"alt":18960,"src":18961},"Synyx Werte: Nachhaltigkeit, Soziale Verantwortung, Auf dem Weg zur Exzellenz, Leidenschaft, Eigenverantwortung, offen & ehrlich","https://media.synyx.de/uploads/2019/03/synyx_values-768x432.png",[439,18963,18964],{},"The synyx values - sustainability, social responsibility, pursuit of excellence, passion, individuality,\nself-responsibility, honest&candor",[439,18966,18967,18968,1402],{},"Becoming aware of these values is only the first step. The second one is to venerate and defend them, which often needs\none of the core agile values – ",[990,18969,18970],{},"courage",[439,18972,18973],{},"In fact I am convinced that it is an integral part of our job to bring up the courage to act on our own and our client’s\nvalues. But what does this mean in practice?",[439,18975,18976],{},"There are countless examples of situations where that courage is needed. There is the business guy from the “quality is\nkey” company, who wants to cut software tests to deliver faster to the clients. There are multi-team environments in\nthe “collaborative mind” company where every team is keeping to their own instead of collaborating, feeding to the “Us\nand Them” mindset. There is the Project Manager in the “customer centric” company, conducting a\ndesign-up-front-waterfall-big-bang-release project without the feedback of one single customer. There is the\nwhatever-guy from the “security first” company not caring about data encryption. There is the developer in the “open\nsource” company claiming ownership of “his” part of code within the project. There are quality engineers in the “work\nsmarter not harder” company clicking through thousands of pages of manual testing plans. If you witness any of those or\nsimilar situations at your employer or client, it is your obligation as a rational and professional person to address\nand try to improve the issue. More often than not I observe external contractors or employees resigning to the\ncircumstances thinking “Fuck it, I’m doing my job and getting paid for what I am told”. In my opinion this is the\nopposite of “doing your job”. Your job is to do what is best for your employer/client/project and that means to bring up\nthe courage to question established practices, to actively address problems, to call out bullshit if necessary.",[439,18978,18979],{},[2205,18980],{"alt":18981,"src":18982},"Scrum Werte","https://media.synyx.de/uploads/2019/03/agile_values.png",[439,18984,18985],{},"For many people this might seem counter-intuitive. The conservative employer thinks he needs people, who do as they are\ntold. The conservative employee keeps to himself being afraid to offend anybody and fearing the repercussions. In fact\nthe opposite is true and both would highly benefit from “showing more attitude”. As an employer you should embrace\nemployees and external contractors, who question things and who challenge everything against your values and their own\nvalues. This is the only way to improve, to produce sustainable products, to come closer to excellence and to create the\nsolutions you really want! As an employee questioning things and standing up for your values makes your dayjob more\nexciting and meaningful instantly! It gives you the chance to do something that you really care about from your own\naccord, thus increasing your motivation and quality of life and consequentially improving the quality of your work\nresults!",[439,18987,18988],{},"For me this is one of the core parts of “Code with attitude” and one of the most basic mindsets that we live and breathe\nhere at synyx. It is the attitude to have the strong intention to understand our client’s value system and needs and to\nhave the courage to actively act in their best interest, while incorporating our own values for our client’s good.",[439,18990,18991],{},"During my work in different projects this mindset has helped me and our clients countless times. I don’t know how often\nwe had to defend our values of sustainability, quality and excellence by emphasizing the importance of things like code\nquality, automated tests, slack time and refactorings to keep a project afloat or even rescue it from certain failure.\nThere were times when clients needed to be reminded of their own core values like customer friendliness, innovativeness,\ndata minimization or accessability. Otherwise their projects would have gone in (for them!) completely undesirable\ndirections.",[439,18993,18994],{},[990,18995,18996],{},"It is part of our job to get to know the client’s values and remind them!",[439,18998,18999],{},"One time I actually witnessed a project manager from a larger corporation with a\nhundreds-of-developers-software-department make the claim that his company is _not_ a software company and\ntherefore the software doesn’t have to be developed on a professional level. The guy in a room of ten people who spoke\nup was me – the only guy not employed by the company. I love being that guy, it is a great part of what makes my job\nmeaningful.",[439,19001,19002],{},"The bottom line is that to be of maximum value for you and your client you have to be aware of that complex value system\nmentioned above and you have to bring up the courage to act on it. To say “no” at the right times, to say “yes, but” at\nthe right times, to encourage at the right times. Code with attitude!",[439,19004,19005],{},[1002,19006,19009],{"href":19007,"rel":19008},"https://twitter.com/indyarni",[1006],"@indyarni",{"title":469,"searchDepth":507,"depth":507,"links":19011},[19012],{"id":18942,"depth":507,"text":18943},[1030,1031],"2018-11-16T12:29:29","At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive\\nconcerning the way we work with clients, the software community and each other. The line is:","https://synyx.de/blog/code-with-attitude-part-1-values/",{},"/blog/code-with-attitude-part-1-values",{"title":18918,"description":18927},"blog/code-with-attitude-part-1-values",[9546,15546,389]," At synyx we recently came forward with a new tagline, trying to express our general mindset that is distinctive concerning the way we work with clients, the software community and each other. The line is: 'Code with Attitude'. In the instant I heard this simple sentence for the first time it triggered a multitude of associations, memories and emotions that I connect with the word 'attitude' in conjunction with software development.","GRITrrOcGKCKR8PeWuCatbM7NCiVp3-snKujpv496nk",{"id":19025,"title":19026,"author":19027,"body":19028,"category":19963,"date":19964,"description":19965,"extension":1034,"link":19966,"meta":19967,"navigation":916,"path":19968,"seo":19969,"slug":19032,"stem":19970,"tags":19971,"teaser":19972,"__hash__":19973},"blog/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist.md","Wie meine Entwicklungsumgebung eingerichtet ist",[335],{"type":432,"value":19029,"toc":19958},[19030,19033,19036,19039,19062,19065,19068,19083,19088,19091,19172,19191,19194,19300,19303,19318,19321,19352,19355,19436,19545,19552,19559,19806,19809,19812,19952,19955],[435,19031,19026],{"id":19032},"wie-meine-entwicklungsumgebung-eingerichtet-ist",[439,19034,19035],{},"Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten.\nIm Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen\nArbeit nicht mehr missen möchte.",[439,19037,19038],{},"Kurz angerissen werden:",[994,19040,19041,19048,19055],{},[997,19042,19043],{},[1002,19044,19047],{"href":19045,"rel":19046},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#terminal",[1006],"Terminal",[997,19049,19050],{},[1002,19051,19054],{"href":19052,"rel":19053},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#git",[1006],"Git",[997,19056,19057],{},[1002,19058,19061],{"href":19059,"rel":19060},"https://synyx.de/blog/2018-11-09-entwicklungsumgebung/?page=1#programme",[1006],"Programme",[3938,19063,19047],{"id":19064},"terminal",[439,19066,19067],{},"Ich arbeite mit einem MacBook und habe von Anfang an iTerm2 genutzt. Weshalb weiß ich leider nicht mehr. Vielleicht gab\nes damals für das von Haus aus installierte Terminal keine Möglichkeit ein Dark Theme zu verwenden. Vielleicht gab es\ndamals auch nicht die Möglichkeit ein Terminalfenster in der Horizontalen zu splitten. Beides ist mittlerweile möglich.\nAndere von mir genutzte iTerm2 Features kommen mir gerade nicht in den Sinn…",[439,19069,19070,19071,19076,19077,19082],{},"Viel interessanter finde ich jedenfalls die Frage, welche Shell installiert ist und wie sie personalisiert ist. Ich\nverwende ",[1002,19072,19075],{"href":19073,"rel":19074},"https://github.com/zsh-users/zsh",[1006],"zsh"," und natürlich(?)\nauch ",[1002,19078,19081],{"href":19079,"rel":19080},"https://github.com/robbyrussell/oh-my-zsh",[1006],"oh-my-zsh",", um meine Shell im Look & Feel anzupassen und mit Plugins\nerweitern zu können.",[439,19084,19085],{},[2205,19086],{"alt":18099,"src":19087},"https://media.synyx.de/uploads/2019/03/prompt-1-768x578.png",[439,19089,19090],{},"Folgende oh-my-zsh Plugins habe ich installiert:",[994,19092,19093,19114,19142,19157],{},[997,19094,19095,19096],{},"z",[994,19097,19098,19104],{},[997,19099,19100,19101,19103],{},"führt eine Liste von Pfaden, in welche man auf der Shell mit ",[471,19102,14718],{}," navigiert und matcht die eingegebenen Zeichen\nauf den meist besuchten Pfad",[997,19105,19106,19107,19110,19111],{},"z. B. führt bei mir der Befehl ",[471,19108,19109],{},"z dot"," zu ",[471,19112,19113],{},"/Users/seber/projects/git/dotfiles",[997,19115,19116,19121],{},[1002,19117,19120],{"href":19118,"rel":19119},"https://github.com/lukechilds/zsh-better-npm-completion",[1006],"zsh-better-npm-completion",[994,19122,19123,19126],{},[997,19124,19125],{},"drücke Tab, um projektspezifische npm Task Vorschläge zu bekommen",[997,19127,19128,19129,520,19132,520,19135,2040,19138,19141],{},"z. B. bekomme ich im Screenshot die Tasks ",[471,19130,19131],{},"lint",[471,19133,19134],{},"lint:watch",[471,19136,19137],{},"start",[471,19139,19140],{},"start:watch"," vorgeschlagen",[997,19143,19144,19149,19154,19156],{},[1002,19145,19148],{"href":19146,"rel":19147},"https://github.com/zsh-users/zsh-autosuggestions",[1006],"zsh-autosuggestions",[994,19150,19151],{},[997,19152,19153],{},"fange an zu tippen und der Befehl wird ausgegraut vervollständigt",[12024,19155],{},"drücke Pfeiltaste rechts und Enter, um ihn auszuführen",[997,19158,19159,19164,19169,19171],{},[1002,19160,19163],{"href":19161,"rel":19162},"https://github.com/zsh-users/zsh-syntax-highlighting",[1006],"zsh-syntax-highlighting",[994,19165,19166],{},[997,19167,19168],{},"hebt das auszuführende Programm farblich hervor",[12024,19170],{},"(nützlich um zu sehen, ob ich mich vertippt habe)",[439,19173,19174,19175,19180,19181,19186,19187,19190],{},"Als Prompt habe ich lange Zeit die ",[1002,19176,19179],{"href":19177,"rel":19178},"https://github.com/sindresorhus/pure",[1006],"pure prompt"," verwendet. Sie ist super schlank\nund zeigt nützliche Infos zum aktuellen git Repository an. Vor wenigen Wochen bin ich jedoch\nzur ",[1002,19182,19185],{"href":19183,"rel":19184},"https://github.com/denysdovhan/spaceship-prompt",[1006],"spaceship-prompt"," gewechselt. Einerseits um einfach mal wieder\nwas neues auszuprobieren, andererseits weil sie mir von Haus aus die Version der Programmiersprache des aktuellen\nProjektes anzeigt. Im Screenshot oben sieht man nach dem Wechsel in das ",[471,19188,19189],{},"workshop-maze-vr"," Verzeichnis den Hinweis, dass\ndas Projekt ein nodeJS Projekt ist und die bash session aktuell v10.9.0 verwendet. Neben nodeJS werden einige andere\nProgrammiersprachen unterstützt, Java leider nicht.",[439,19192,19193],{},"Die Anordnung der spaceship prompt Dinge und ob sie überhaupt angezeigt werden sollen, kann konfiguriert werden:",[464,19195,19197],{"className":466,"code":19196,"language":468,"meta":469,"style":469},"SPACESHIP_PROMPT_ORDER=(\n time # Time stamps section\n user # Username section\n dir # Current directory section\n git # Git section (git_branch + git_status)\n node # Node.js section\n docker # Docker section\n exec_time # Execution time\n line_sep # Line break\n jobs # Background jobs indicator\n exit_code # Exit code section\n char # Prompt character\n)\n",[471,19198,19199,19208,19216,19224,19232,19240,19248,19256,19264,19272,19280,19288,19296],{"__ignoreMap":469},[474,19200,19201,19204,19206],{"class":476,"line":477},[474,19202,19203],{"class":503},"SPACESHIP_PROMPT_ORDER",[474,19205,811],{"class":810},[474,19207,1555],{"class":503},[474,19209,19210,19213],{"class":476,"line":507},[474,19211,19212],{"class":810}," time",[474,19214,19215],{"class":2277}," # Time stamps section\n",[474,19217,19218,19221],{"class":476,"line":547},[474,19219,19220],{"class":484}," user",[474,19222,19223],{"class":2277}," # Username section\n",[474,19225,19226,19229],{"class":476,"line":584},[474,19227,19228],{"class":484}," dir",[474,19230,19231],{"class":2277}," # Current directory section\n",[474,19233,19234,19237],{"class":476,"line":607},[474,19235,19236],{"class":484}," git",[474,19238,19239],{"class":2277}," # Git section (git_branch + git_status)\n",[474,19241,19242,19245],{"class":476,"line":642},[474,19243,19244],{"class":484}," node",[474,19246,19247],{"class":2277}," # Node.js section\n",[474,19249,19250,19253],{"class":476,"line":663},[474,19251,19252],{"class":484}," docker",[474,19254,19255],{"class":2277}," # Docker section\n",[474,19257,19258,19261],{"class":476,"line":694},[474,19259,19260],{"class":484}," exec_time",[474,19262,19263],{"class":2277}," # Execution time\n",[474,19265,19266,19269],{"class":476,"line":700},[474,19267,19268],{"class":484}," line_sep",[474,19270,19271],{"class":2277}," # Line break\n",[474,19273,19274,19277],{"class":476,"line":913},[474,19275,19276],{"class":484}," jobs",[474,19278,19279],{"class":2277}," # Background jobs indicator\n",[474,19281,19282,19285],{"class":476,"line":920},[474,19283,19284],{"class":484}," exit_code",[474,19286,19287],{"class":2277}," # Exit code section\n",[474,19289,19290,19293],{"class":476,"line":926},[474,19291,19292],{"class":484}," char",[474,19294,19295],{"class":2277}," # Prompt character\n",[474,19297,19298],{"class":476,"line":932},[474,19299,1101],{"class":503},[439,19301,19302],{},"und auch das Symbol mit welchem eine Zeile beginnen soll. Ich wollte weiterhin das Zeichen der pure prompt verwenden und\nhabe daher den default überschrieben:",[464,19304,19306],{"className":466,"code":19305,"language":468,"meta":469,"style":469},"SPACESHIP_CHAR_SYMBOL='❯ '\n",[471,19307,19308],{"__ignoreMap":469},[474,19309,19310,19313,19315],{"class":476,"line":477},[474,19311,19312],{"class":503},"SPACESHIP_CHAR_SYMBOL",[474,19314,811],{"class":810},[474,19316,19317],{"class":484},"'❯ '\n",[3938,19319,19054],{"id":19320},"git",[439,19322,19323,19324,2040,19327,19330,19331,520,19336,520,19341,520,19346,19351],{},"Git verwende ich überwiegend über die Konsole. Mit grafischen Programmen bin ich nie richtig warm geworden. Angefangen\nmit ",[471,19325,19326],{},"gitk",[471,19328,19329],{},"git gui"," auf einem Linux Rechner bin ich\nüber ",[1002,19332,19335],{"href":19333,"rel":19334},"https://git-cola.github.io/",[1006],"git-cola",[1002,19337,19340],{"href":19338,"rel":19339},"https://www.sourcetreeapp.com/",[1006],"SourceTree",[1002,19342,19345],{"href":19343,"rel":19344},"https://www.gitkraken.com/",[1006],"GitKraken",[1002,19347,19350],{"href":19348,"rel":19349},"https://git-fork.com/",[1006],"Fork","\nund noch ein paar andere gestolpert. Aber das Geklicke und der Kontextwechsel zwischen IDE und $Werkzeug haben mich\nimmer gestört. Auch visuell fand ich bis auf GitKraken und neuerdings Fork nichts ansprechend (ja, ist mir wichtig, hat\nman anfangs schon beim Dark Theme fürs Terminal bemerkt 🙄). Hauptsächlich hat mich aber das Nichtvorhandensein bzw. die\numständliche Handhabung des rebasen gestört. Das geht für mich am schnellsten auf der Konsole mit eigenen git aliasen.",[439,19353,19354],{},"Meine häufigsten Befehle sind:",[994,19356,19357,19373,19386,19399,19416],{},[997,19358,19359,19362],{},[471,19360,19361],{},"git up",[994,19363,19364,19370],{},[997,19365,19366,19367,19369],{},"hole den aktuellen Stand von ",[471,19368,9458],{}," mit einem rebase",[997,19371,19372],{},"lösche alle lokalen Branches die bereits gemerged wurden",[997,19374,19375,19378,19383,19385],{},[471,19376,19377],{},"git cm \"jetzt funktionierts wirklich!!!1elf\"",[994,19379,19380],{},[997,19381,19382],{},"comitte alle Änderungen mit der folgenden commit message",[12024,19384],{},"(inklusive neu angelegte Dateien)",[997,19387,19388,19391,19396,19398],{},[471,19389,19390],{},"git amend",[994,19392,19393],{},[997,19394,19395],{},"füge alle Änderungen dem letzten HEAD commit hinzu",[12024,19397],{},"(exklusive neu angelegte Dateien)",[997,19400,19401,19404],{},[471,19402,19403],{},"git ll",[994,19405,19406,19409],{},[997,19407,19408],{},"drucke eine Liste aller commit hashes und messages des aktuellen Branches ab HEAD",[997,19410,19411,19412,19415],{},"die Länge der Liste kann mit ",[471,19413,19414],{},"-42"," begrenzt werden",[997,19417,19418,2040,19421,19424],{},[471,19419,19420],{},"git fix",[471,19422,19423],{},"git ri",[994,19425,19426,19433],{},[997,19427,19428,19429,19432],{},"erzeuge einen ",[471,19430,19431],{},"fixup"," commit und räume die commits mit einem interactive rebase auf",[997,19434,19435],{},"workflow ist dann wie folgt:",[464,19437,19439],{"className":466,"code":19438,"language":468,"meta":469,"style":469},"❯ git ll -5\na52747d (HEAD -> master, origin/master, origin/HEAD) foo\nb858f05 bar\n4954b3d baz\n782d959 bum\n2b1c7c6 buff\n❯ git add .\n❯ git fix 782d959\n❯ git ri 782d959^\n",[471,19440,19441,19455,19477,19485,19493,19501,19509,19521,19533],{"__ignoreMap":469},[474,19442,19443,19446,19449,19452],{"class":476,"line":477},[474,19444,19445],{"class":480},"❯",[474,19447,19448],{"class":484}," git",[474,19450,19451],{"class":484}," ll",[474,19453,19454],{"class":510}," -5\n",[474,19456,19457,19460,19463,19465,19468,19471,19474],{"class":476,"line":507},[474,19458,19459],{"class":480},"a52747d",[474,19461,19462],{"class":503}," (HEAD -",[474,19464,1500],{"class":810},[474,19466,19467],{"class":484}," master,",[474,19469,19470],{"class":484}," origin/master,",[474,19472,19473],{"class":484}," origin/HEAD",[474,19475,19476],{"class":503},") foo\n",[474,19478,19479,19482],{"class":476,"line":547},[474,19480,19481],{"class":480},"b858f05",[474,19483,19484],{"class":484}," bar\n",[474,19486,19487,19490],{"class":476,"line":584},[474,19488,19489],{"class":480},"4954b3d",[474,19491,19492],{"class":484}," baz\n",[474,19494,19495,19498],{"class":476,"line":607},[474,19496,19497],{"class":480},"782d959",[474,19499,19500],{"class":484}," bum\n",[474,19502,19503,19506],{"class":476,"line":642},[474,19504,19505],{"class":480},"2b1c7c6",[474,19507,19508],{"class":484}," buff\n",[474,19510,19511,19513,19515,19518],{"class":476,"line":663},[474,19512,19445],{"class":480},[474,19514,19448],{"class":484},[474,19516,19517],{"class":484}," add",[474,19519,19520],{"class":484}," .\n",[474,19522,19523,19525,19527,19530],{"class":476,"line":694},[474,19524,19445],{"class":480},[474,19526,19448],{"class":484},[474,19528,19529],{"class":484}," fix",[474,19531,19532],{"class":484}," 782d959\n",[474,19534,19535,19537,19539,19542],{"class":476,"line":700},[474,19536,19445],{"class":480},[474,19538,19448],{"class":484},[474,19540,19541],{"class":484}," ri",[474,19543,19544],{"class":484}," 782d959^\n",[439,19546,19547,19548,19551],{},"Eigene git aliase können in der globalen git config unter ",[471,19549,19550],{},"~/.gitconfig"," abgelegt werden.",[439,19553,19554,19555,19558],{},"Da ich meine merge requests im Normalfall erst auf den master rebase bevor ich sie zum mergen freigebe, bin ich super\nglücklich über das ",[471,19556,19557],{},"rerere"," Feature von git. Ist das Feature aktiviert, merkt sich git beim rebasen, wie ein Konflikt\ngelöst wird. Kommt es erneut zu dem selben Konflikt, weiß git wie es diesen automatisiert zu lösen hat.",[464,19560,19562],{"className":466,"code":19561,"language":468,"meta":469,"style":469},"[rerere]\n enabled = true\n[alias]\n s = status\n st = stash\n co = checkout\n cob = checkout -b\n fix = commit --fixup\n ri = rebase -i --autosquash\n up = !git pull --rebase --prune && git bclean\n cm = !git add -A && git commit -m\n amend = commit -a --amend\n ll = !git --no-pager log --oneline --decorate\n bclean = \"!f() { branches=$(git branch --merged ${1-master} | grep -v \" ${1-master}$\"); [ -z \\\"$branches\\\" ] || git branch -d$branches; }; f\"\n",[471,19563,19564,19569,19578,19583,19593,19603,19613,19626,19639,19654,19681,19704,19719,19740],{"__ignoreMap":469},[474,19565,19566],{"class":476,"line":477},[474,19567,19568],{"class":503},"[rerere]\n",[474,19570,19571,19574,19576],{"class":476,"line":507},[474,19572,19573],{"class":480}," enabled",[474,19575,1661],{"class":484},[474,19577,10413],{"class":510},[474,19579,19580],{"class":476,"line":547},[474,19581,19582],{"class":503},"[alias]\n",[474,19584,19585,19588,19590],{"class":476,"line":584},[474,19586,19587],{"class":480}," s",[474,19589,1661],{"class":484},[474,19591,19592],{"class":484}," status\n",[474,19594,19595,19598,19600],{"class":476,"line":607},[474,19596,19597],{"class":480}," st",[474,19599,1661],{"class":484},[474,19601,19602],{"class":484}," stash\n",[474,19604,19605,19608,19610],{"class":476,"line":642},[474,19606,19607],{"class":480}," co",[474,19609,1661],{"class":484},[474,19611,19612],{"class":484}," checkout\n",[474,19614,19615,19618,19620,19623],{"class":476,"line":663},[474,19616,19617],{"class":480}," cob",[474,19619,1661],{"class":484},[474,19621,19622],{"class":484}," checkout",[474,19624,19625],{"class":510}," -b\n",[474,19627,19628,19631,19633,19636],{"class":476,"line":694},[474,19629,19630],{"class":480}," fix",[474,19632,1661],{"class":484},[474,19634,19635],{"class":484}," commit",[474,19637,19638],{"class":510}," --fixup\n",[474,19640,19641,19644,19646,19649,19651],{"class":476,"line":700},[474,19642,19643],{"class":480}," ri",[474,19645,1661],{"class":484},[474,19647,19648],{"class":484}," rebase",[474,19650,12505],{"class":510},[474,19652,19653],{"class":510}," --autosquash\n",[474,19655,19656,19659,19661,19664,19667,19670,19673,19676,19678],{"class":476,"line":913},[474,19657,19658],{"class":480}," up",[474,19660,1661],{"class":484},[474,19662,19663],{"class":484}," !git",[474,19665,19666],{"class":484}," pull",[474,19668,19669],{"class":510}," --rebase",[474,19671,19672],{"class":510}," --prune",[474,19674,19675],{"class":503}," && ",[474,19677,19320],{"class":480},[474,19679,19680],{"class":484}," bclean\n",[474,19682,19683,19686,19688,19690,19692,19695,19697,19699,19701],{"class":476,"line":920},[474,19684,19685],{"class":480}," cm",[474,19687,1661],{"class":484},[474,19689,19663],{"class":484},[474,19691,19517],{"class":484},[474,19693,19694],{"class":510}," -A",[474,19696,19675],{"class":503},[474,19698,19320],{"class":480},[474,19700,19635],{"class":484},[474,19702,19703],{"class":510}," -m\n",[474,19705,19706,19709,19711,19713,19716],{"class":476,"line":926},[474,19707,19708],{"class":480}," amend",[474,19710,1661],{"class":484},[474,19712,19635],{"class":484},[474,19714,19715],{"class":510}," -a",[474,19717,19718],{"class":510}," --amend\n",[474,19720,19721,19724,19726,19728,19731,19734,19737],{"class":476,"line":932},[474,19722,19723],{"class":480}," ll",[474,19725,1661],{"class":484},[474,19727,19663],{"class":484},[474,19729,19730],{"class":510}," --no-pager",[474,19732,19733],{"class":484}," log",[474,19735,19736],{"class":510}," --oneline",[474,19738,19739],{"class":510}," --decorate\n",[474,19741,19742,19745,19747,19750,19752,19755,19758,19761,19763,19766,19769,19771,19774,19776,19779,19782,19784,19786,19788,19791,19793,19796,19798,19801,19803],{"class":476,"line":938},[474,19743,19744],{"class":480}," bclean",[474,19746,1661],{"class":484},[474,19748,19749],{"class":484}," \"!f() { branches=$(",[474,19751,19320],{"class":480},[474,19753,19754],{"class":484}," branch ",[474,19756,19757],{"class":510},"--merged",[474,19759,19760],{"class":510}," ${1",[474,19762,10143],{"class":484},[474,19764,19765],{"class":503},"master",[474,19767,19768],{"class":510},"}",[474,19770,14174],{"class":810},[474,19772,19773],{"class":480}," grep",[474,19775,2645],{"class":510},[474,19777,19778],{"class":484}," \" ",[474,19780,19781],{"class":510},"${1",[474,19783,10143],{"class":484},[474,19785,19765],{"class":503},[474,19787,19768],{"class":510},[474,19789,19790],{"class":484},"$\"); [ -z ",[474,19792,12616],{"class":510},[474,19794,19795],{"class":503},"$branches",[474,19797,12616],{"class":510},[474,19799,19800],{"class":484}," ] || git branch -d",[474,19802,19795],{"class":503},[474,19804,19805],{"class":484},"; }; f\"\n",[3938,19807,19061],{"id":19808},"programme",[439,19810,19811],{},"Weitere Programme, die ich in meiner täglichen Arbeit nicht mehr missen möchte, sind:",[994,19813,19814,19836,19854,19868,19900,19921,19942],{},[997,19815,19816,8351,19821,19824],{},[1002,19817,19820],{"href":19818,"rel":19819},"https://brew.sh/",[1006],"homebrew",[990,19822,19823],{},"(kommandozeile)",[994,19825,19826,19829],{},[997,19827,19828],{},"quasi DER Paketmanager für OSX",[997,19830,19831,19832,19835],{},"Programme installieren mit ",[471,19833,19834],{},"brew install FOO",". Schneller gehts nicht!",[997,19837,19838,8351,19843,19845],{},[1002,19839,19842],{"href":19840,"rel":19841},"https://github.com/direnv/direnv",[1006],"direnv",[990,19844,19823],{},[994,19846,19847],{},[997,19848,19849,19850,19853],{},"lege in einem Verzeichnis eine ",[471,19851,19852],{},".envrc"," Datei ab und definiere dort z. B. die Java oder NodeJS Version, oder auch\neine andere git E-Mail-Adresse, falls man hier eine andere als die globale verwenden möchte für commits",[997,19855,19856,8351,19861,19863],{},[1002,19857,19860],{"href":19858,"rel":19859},"https://stedolan.github.io/jq/",[1006],"jq",[990,19862,19823],{},[994,19864,19865],{},[997,19866,19867],{},"JSON Prozessor für die Kommandozeile",[997,19869,19870,8351,19875,19877],{},[1002,19871,19874],{"href":19872,"rel":19873},"https://github.com/nodenv/nodenv",[1006],"nodenv",[990,19876,19823],{},[994,19878,19879,19887],{},[997,19880,19881,19882,17548],{},"verwalte unterschiedliche Versionen von NodeJS (Alternative zu z.\nB. ",[1002,19883,19886],{"href":19884,"rel":19885},"https://github.com/creationix/nvm",[1006],"Node Version Manager (nvm)",[997,19888,19889,19890,19893,19894,19896,19897,19899],{},"Anfang des Jahres bin ich von ",[471,19891,19892],{},"nvm"," auf ",[471,19895,19874],{}," umgestiegen, weil letzteres die Shell beim Initialisieren nicht\nblockiert. ",[471,19898,19892],{}," braucht seine Zeit zum Laden von NodeJS",[997,19901,19902,19907],{},[1002,19903,19906],{"href":19904,"rel":19905},"https://www.spectacleapp.com/",[1006],"spectacle",[994,19908,19909,19912,19915,19918],{},[997,19910,19911],{},"verschiebe Fenster und ändere deren Größe mit deiner Tastatur",[997,19913,19914],{},"die Tastenkombinationen sind konfigurierbar",[997,19916,19917],{},"ich hatte einen UltraWide Monitor und habe so schnell mal drei Fenster in der Breite gedrittelt verteilt",[997,19919,19920],{},"Natürlich ist das auch auf einem normalen Laptop Display sinnvoll, Fenster ohne Maus rechts/links oder oben/unten\nausrichten zu können",[997,19922,19923,19928],{},[1002,19924,19927],{"href":19925,"rel":19926},"https://github.com/Clipy/Clipy",[1006],"clipy",[994,19929,19930,19939],{},[997,19931,19932,19933,19938],{},"wer die copy-paste aus IntelliJ lieb gewonnen hat; clipy macht das systemweit (die Alternative wäre, das\nPowerpack für ",[1002,19934,19937],{"href":19935,"rel":19936},"https://www.alfredapp.com/",[1006],"Alfred"," zu kaufen)",[997,19940,19941],{},"man achte auf Passwörter! Clipy bietet die Möglichkeit, bestimmte Programme zu ignorieren. Es ist ratsam dort\nKeepassX oder sonstigen Tresor einzutragen 😉",[997,19943,19944,19947],{},[448,19945,19946],{},"spotify",[994,19948,19949],{},[997,19950,19951],{},"Musik muss natürlich sein 🎧",[439,19953,19954],{},"Vielen Dank fürs Lesen (☞゚ヮ゚)☞ (smilie eingefügt mit clipy snippets)",[1024,19956,19957],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":469,"searchDepth":507,"depth":507,"links":19959},[19960,19961,19962],{"id":19064,"depth":507,"text":19047},{"id":19320,"depth":507,"text":19054},{"id":19808,"depth":507,"text":19061},[1030,1031],"2018-11-09T12:41:34","Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten.\\nIm Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen\\nArbeit nicht mehr missen möchte.","https://synyx.de/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist/",{},"/blog/wie-meine-entwicklungsumgebung-eingerichtet-ist",{"title":19026,"description":19035},"blog/wie-meine-entwicklungsumgebung-eingerichtet-ist",[15546],"Beim synyx Camp vor zwei Wochen haben wir uns unter anderem über das Setup unserer Entwicklungsumgebungen unterhalten. Im Folgenden möchte ich kurz berichten, wie ich meine eingerichtet habe und welche Programme ich in meiner alltäglichen Arbeit nicht mehr missen möchte.","38z-kKuLlmfwcQfMnzrrf1gJHoh-qvNZsZ_skAURUrY",{"id":19975,"title":19976,"author":19977,"body":19978,"category":20111,"date":20112,"description":469,"extension":1034,"link":20113,"meta":20114,"navigation":916,"path":20115,"seo":20116,"slug":20117,"stem":20118,"tags":20119,"teaser":20123,"__hash__":20124},"blog/blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews.md","Nachfragen statt urteilen: 4 Gedankenanstöße für effektive Code Reviews",[30],{"type":432,"value":19979,"toc":20104},[19980,19983,19988,19991,19994,19997,20001,20006,20009,20014,20017,20020,20024,20028,20031,20035,20038,20041,20045,20049,20052,20056,20059,20062,20065,20069,20073,20076,20080,20083,20086,20090,20093,20096,20101],[435,19981,19976],{"id":19982},"nachfragen-statt-urteilen-4-gedankenanstöße-für-effektive-code-reviews",[11947,19984,19985],{},[439,19986,19987],{},"My code is guaranteed 100% mistrake free.",[439,19989,19990],{},"Das Arbeiten mit Pull Requests und Code Reviews gehört bei synyx zum Alltag. Neuer Code fließt normalerweise erst dann\nin den Hauptzweig eines Projekts, sobald er ein Review durchlaufen hat. Selbstverständlich kann es berechtigte Ausnahmen\ngeben. Zum Beispiel bei Code, der komplett im Pair Programming erstellt oder lediglich durch einen winzigen Fix\nverändert wurde.",[439,19992,19993],{},"Code Reviews sind nicht als bürokratisches Kontrollwerkzeug zu betrachten, sondern als wirksames Hilfsmittel zur\nVerbesserung der Qualität nach dem Prinzip: Vier Augen sehen mehr als zwei.",[439,19995,19996],{},"Fernab von Technik und Tools möchte ich im Folgenden vier Gedankenanstöße für effektive Code Reviews liefern.",[3938,19998,20000],{"id":19999},"_1-eine-bewusste-entscheidung","1. Eine bewusste Entscheidung?",[439,20002,20003],{},[448,20004,20005],{},"Gut:",[439,20007,20008],{},"“Diese Variable sollte nicht public sein.”",[439,20010,20011],{},[448,20012,20013],{},"Besser:",[439,20015,20016],{},"“Wieso hast du dich dafür entschieden, diese Variable public zu machen?”",[439,20018,20019],{},"Die erste Herangehensweise führt lediglich zur Anpassung des Scopes von genau einer Variable. Die zweite\nHerangehensweise hingegen bietet Raum für tiefergehende Erkenntnisse. Es kann eine lehrreiche Diskussion über die\nSichtbarkeit von Variablen entstehen. Es kann aber auch sein, dass die Konfiguration der Entwicklungsumgebung angepasst\nwird. Vielleicht hat der Entwickler den Scope für die Variable gar nicht bewusst gewählt, sondern lediglich übersehen,\ndass seine IDE unpassenden Boiler Plate Code generiert hat.",[3938,20021,20023],{"id":20022},"_2-zu-kurz-gedacht","2. Zu kurz gedacht?",[439,20025,20026],{},[448,20027,20005],{},[439,20029,20030],{},"“Das funktioniert so nicht.”",[439,20032,20033,6562],{},[448,20034,20013],{},[439,20036,20037],{},"“Wieso hast du das Problem auf genau diese Weise gelöst und nicht anders? Hast du Edge Case XY eigentlich bedacht?”",[439,20039,20040],{},"Im besten Fall schreibt ihr gemeinsam einen Test für den nicht abgedeckten Edge Case und optimiert den Code im Teamwork.\nVielleicht findet sich aber auch ein guter Grund, wieso das Problem genau so gelöst wurde. Es muss nicht immer der Autor\nzu kurz gedacht haben, manchmal kann auch der Reviewer einen Aspekt übersehen haben. Durch Fragen entstehen\nDiskussionen, die zu neuen Erkenntnissen und Wissenstransfer führen können.",[3938,20042,20044],{"id":20043},"_3-wie-soll-ein-junior-einem-senior-schon-helfen","3. Wie soll ein Junior einem Senior schon helfen?",[439,20046,20047],{},[448,20048,20005],{},[439,20050,20051],{},"“Puh, das verstehe ich jetzt nicht wirklich. Naja, wird schon passen.”",[439,20053,20054],{},[448,20055,20013],{},[439,20057,20058],{},"“Ich verstehe diese Stelle im Code nicht so ganz. Kannst du mir das genauer erklären?”",[439,20060,20061],{},"Als Junior den Code eines Seniors zu reviewen ist doch sinnlos, oder? Nein, ganz im Gegenteil. Beide Seiten profitieren\ngleichermaßen. Der Junior kann eine ganze Menge dabei lernen, zu sehen, wie ein gestandener Entwickler ein bestimmtes\nProblem löst. Der Senior wiederum profitiert vom frischen Blickwinkel des Juniors. Womöglich wurde die ein oder andere\nStelle im Code derart überoptimiert, dass andere Teammitglieder sie nur noch schwer nachvollziehen können. Vielleicht\nfehlen erklärende Kommentare als Hilfe. Durch gezielte Verständnisfragen kann der Junior zu besser wartbarem Code\nbeitragen, gerade weil er wenig Erfahrung mitbringt. Abgesehen davon macht auch der erfahrenste Entwickler\nFlüchtigkeitsfehler, die durch ein zweites Paar Augen ausgemerzt werden können.",[439,20063,20064],{},"Verläuft das Code Review in umgekehrter Richtung, profitiert der Junior langfristig mehr, wenn der Senior ihm das\nProblem nur aufzeigt, statt vorgefertigte Lösungshäppchen zu servieren. Durch die richtigen Fragen entsteht Raum zur\nErschließung eigener Lösungswege und damit ein langfristiger Lerneffekt.",[3938,20066,20068],{"id":20067},"_4-über-geschmack-lässt-sich-nicht-streiten","4. Über Geschmack lässt sich (nicht) streiten?",[439,20070,20071],{},[448,20072,20005],{},[439,20074,20075],{},"“Ich kann While-Schleifen nicht ausstehen.”",[439,20077,20078],{},[448,20079,20013],{},[439,20081,20082],{},"“Wieso hast du hier eine While-Schleife benutzt und keine For-Schleife?”",[439,20084,20085],{},"Natürlich sollten sich alle Teammitglieder an gemeinsam festgelegte Konventionen und Best Practices halten. Trotz allem\nhat jeder Entwickler seinen eigenen individuellen Stil. Solange dieser nicht gegen die vereinbarten Konventionen\nverstößt, spricht doch nichts dagegen, die individuellen Vorlieben zu akzeptieren, oder? Durch neugieriges Nachfragen\nerfährt man vielleicht sogar, ob bestimmte Motive hinter einer Stilrichtung zur Problemlösung stecken.",[3938,20087,20089],{"id":20088},"nachfragen-statt-urteilen","Nachfragen statt urteilen!",[439,20091,20092],{},"Das gilt definitiv nicht nur für Code Reviews. Selbst in enger Zusammenarbeit innerhalb eines Teams sehen wir immer nur\ngewisse Momentaufnahmen unserer Mitmenschen. Wir können bloß Vermutungen anstellen, aber nie hundertprozentig sicher\nsein, wieso jemand tut, was er tut, oder wie seine Äußerungen wirklich gemeint sind. Durch zeitnahes Nachfragen lassen\nsich kleine Ärgernisse und potentielle Missverständnisse schnell aus dem Weg räumen, anstatt sich mit der Zeit zu einem\nmonströsen Konflikt anzustauen.",[439,20094,20095],{},"Wenn du also das nächste Mal Ärger in dir aufsteigen spürst, spreche lieber die betreffende Person auf ihr Verhalten an,\nstatt vorschnell zu urteilen und dich (innerlich) aufzuregen.",[439,20097,20098],{},[448,20099,20100],{},"Vielleicht ist ja alles ganz anders, als du denkst 😉",[439,20102,20103],{},"Hinweis: Aus Gründen der Lesbarkeit habe ich im Text ausschließlich die männliche Form benutzt, nichtsdestotrotz beziehe\nich mich immer auf Angehörige aller Geschlechter.",{"title":469,"searchDepth":507,"depth":507,"links":20105},[20106,20107,20108,20109,20110],{"id":19999,"depth":507,"text":20000},{"id":20022,"depth":507,"text":20023},{"id":20043,"depth":507,"text":20044},{"id":20067,"depth":507,"text":20068},{"id":20088,"depth":507,"text":20089},[1031],"2018-11-05T12:46:10","https://synyx.de/blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews/",{},"/blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews",{"title":19976,"description":469},"nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews","blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews",[20120,14820,20121,20122],"codereview","nachfragen","soft-skills","My code is guaranteed 100% mistrake free. Das Arbeiten mit Pull Requests und Code Reviews gehört bei synyx zum Alltag. Neuer Code fließt normalerweise erst dann in den Hauptzweig eines Projekts, sobald er ein Review durchlaufen hat. Selbstverständlich kann es berechtigte Ausnahmen geben. Zum Beispiel bei Code, der komplett im Pair Programming erstellt oder lediglich durch einen winzigen Fix verändert wurde. Code Reviews sind nicht als bürokratisches Kontrollwerkzeug zu betrachten, sondern als wirksames Hilfsmittel zur Verbesserung der Qualität nach dem Prinzip: Vier Augen sehen mehr als zwei.","mZG0hzuJ8JXuCMJi7Viy5YHIqbMIChTcJh_fVQZSm-I",{"id":20126,"title":20127,"author":20128,"body":20129,"category":20494,"date":20495,"description":469,"extension":1034,"link":20496,"meta":20497,"navigation":916,"path":20498,"seo":20499,"slug":20133,"stem":20500,"tags":20501,"teaser":20507,"__hash__":20508},"blog/blog/connect-to-multiple-vpns-at-once-using-networkmanager-and-systemd-resolved.md","Connect to multiple VPNs at once using NetworkManager and systemd-resolved",[154],{"type":432,"value":20130,"toc":20483},[20131,20134,20180,20190,20194,20197,20200,20204,20207,20232,20243,20247,20250,20259,20272,20276,20279,20287,20291,20294,20314,20317,20320,20330,20339,20354,20357,20366,20369,20373,20382,20392,20401,20404,20414,20417,20427,20430,20433,20436,20451,20455,20458,20462,20465,20473,20477,20480],[435,20132,20127],{"id":20133},"connect-to-multiple-vpns-at-once-using-networkmanager-and-systemd-resolved",[464,20135,20137],{"className":466,"code":20136,"language":468,"meta":469,"style":469},"It’s not DNS\nThere is a no way it’s DNS\nIt was DNS\n",[471,20138,20139,20150,20171],{"__ignoreMap":469},[474,20140,20141,20144,20147],{"class":476,"line":477},[474,20142,20143],{"class":480},"It’s",[474,20145,20146],{"class":484}," not",[474,20148,20149],{"class":484}," DNS\n",[474,20151,20152,20155,20157,20160,20163,20166,20169],{"class":476,"line":507},[474,20153,20154],{"class":480},"There",[474,20156,15134],{"class":484},[474,20158,20159],{"class":484}," a",[474,20161,20162],{"class":484}," no",[474,20164,20165],{"class":484}," way",[474,20167,20168],{"class":484}," it’s",[474,20170,20149],{"class":484},[474,20172,20173,20176,20178],{"class":476,"line":547},[474,20174,20175],{"class":480},"It",[474,20177,686],{"class":484},[474,20179,20149],{"class":484},[439,20181,20182],{},[990,20183,20184,20185],{},"from ",[1002,20186,20189],{"href":20187,"rel":20188},"https://www.cyberciti.biz/humour/a-haiku-about-dns/",[1006],"DNS Haiku",[3938,20191,20193],{"id":20192},"the-dark-side","The dark side",[439,20195,20196],{},"When I started at synyx I chose to get a MacBook in order to be productive as quick as possible. I still had the\ncumbersome Linux desktop experience from my previous employer in mind. 😉 At synyx, however, things are different: we\nhave a highly skilled admin team that provides a very convenient environment to use Linux on laptops. So, a few weeks\nago I decided to move away from MacOS to Linux (again).",[439,20198,20199],{},"One of the trickier parts of that migration effort was to setup my VPNs with the same comfort as I had on MacOS. Since\nwe are working as consultants/external developers for several customers there is the need to connect to synyx’ VPN as\nwell as our customers’ VPNs. Thus I need to connect to at least two VPNs with working DNS resolution into all connected\nnetworks.",[3938,20201,20203],{"id":20202},"dns-configuration-with-etcresolvconf","DNS configuration with /etc/resolv.conf",[439,20205,20206],{},"Traditionally on Linux systems the DNS servers used to resolve hostnames to IP addresses are listed in the configuration\nfile /etc/resolv.conf :",[464,20208,20210],{"className":709,"code":20209,"language":711,"meta":469,"style":469},"search example.com local.lan\nnameserver 1.1.1.1\nnameserver 8.8.8.8\nnameserver 9.9.9.9\n",[471,20211,20212,20217,20222,20227],{"__ignoreMap":469},[474,20213,20214],{"class":476,"line":477},[474,20215,20216],{},"search example.com local.lan\n",[474,20218,20219],{"class":476,"line":507},[474,20220,20221],{},"nameserver 1.1.1.1\n",[474,20223,20224],{"class":476,"line":547},[474,20225,20226],{},"nameserver 8.8.8.8\n",[474,20228,20229],{"class":476,"line":584},[474,20230,20231],{},"nameserver 9.9.9.9\n",[439,20233,20234,20235,20238,20239,20242],{},"When OpenVPN establishes a connection the server side returns information about the connected network. A part of this\nare the nameserver(s) that are used to resolve names that are part of the connected network. In the simplest possible\nsetup the nameserver of the VPN is appended to the list of nameservers that have been written to that file for the\n",[990,20236,20237],{},"normal"," network connection by your network manager. For the first connection this simple approach is sufficient in most\nof the cases. As soon as you configure and connect the second VPN a harsh limitation of libnss",[990,20240,20241],{},"dns library that parses\nresolv.conf kicks in: only _three"," nameserver entries in /etc/resolv.conf are supported. So only the names of the VPN\nyou connected at first are resolvable. If your network manager has for any reasons written 3 nameservers your VPN’s\nnameservers are completely ignored.",[3938,20244,20246],{"id":20245},"alternative-dns-configuration-mechanisms","Alternative DNS configuration mechanisms",[439,20248,20249],{},"Luckily name resolution in libc general is configurable via the /etc/nsswitch.conf configuration file. Besides other\nmechanisms the file defines how to lookup hostnames. In its default setting the section responsible for resolving\nhostnames points to ‘files’ and ‘dns’. The first one uses /etc/hosts and the latter uses /etc/resolv.conf for\nresolution. As the /etc/resolv.conf is limited to 3 nameserver entries this is not suitable for connecting to multiple\nVPNs simultaneously.",[439,20251,20252,20253,20258],{},"In the past I had a setup with a handcrafted ",[1002,20254,20257],{"href":20255,"rel":20256},"https://wiki.archlinux.org/index.php/Dnsmasq",[1006],"Dnsmasq"," installation that\nwas congfigured to lookup names from the VPN via the VPN’s nameserver. This time, however, I wanted to try something\nelse. In recent systemd versions a new service called systemd-resolved was introduced. It can be used to dynamically\nconfigure DNS servers dependending on the active network connection(s).",[439,20260,20261,20262,20265,20266,20271],{},"So, if the libnss_dns plugin in libc is limited to three server entries in /etc/resolv.conf some other mechanism has to\nbe used. One idea is that you statically configure the address of a locally installed nameserver that is (re-)\nconfigured by OpenVPN connections. ",[1002,20263,20257],{"href":20255,"rel":20264},[1006]," is one of the tools many\npeople (including past-me) used for this task. In this article however, I want to focus on the already\nmentioned ",[1002,20267,20270],{"href":20268,"rel":20269},"https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html",[1006],"systemd-resolved"," and how it\ncan be integrated into the hostname lookup on a modern Linux system.",[3938,20273,20275],{"id":20274},"openvpn-and-selinux","OpenVPN and SELinux",[439,20277,20278],{},"Before we get into the details of connecting systemd-resolved with OpenVPN I want to point out a dirty detail of\nconvincing OpenVPN to work together with SELinux (which is active by default on Fedora). So, I use Gnome’s\nNetworkManager as a network manager not only for WiFi and fixed networks but also for VPNs (and 4G as well). So I used\nthe very convenient UI to import my existing VPN configuration and tried to connect. Nothing happened. 😐",[439,20280,20281,20282,20286],{},"After looking into the logs I realized that the NetworkManager OpenVPN plugin was not permitted to access the VPN\ncertificate files for some reasons. For quite some time I tried to fix permissions but nothing helped. After some\ngoogling I found this ",[1002,20283,12336],{"href":20284,"rel":20285},"https://unix.stackexchange.com/questions/166807/selinux-and-openvpn#166811",[1006]," which\ndescribes that OpenVPN may only access files in the ~/.cert directory and restore the SELinux security context of the\nfiles with the restorecon command.",[3938,20288,20290],{"id":20289},"configure-and-activate-systemd-resolved","Configure and activate systemd-resolved",[439,20292,20293],{},"Before we can use systemd-resolved it needs to be configured and activated. The config file is located at\n/etc/systemd/resolved.conf:",[464,20295,20297],{"className":709,"code":20296,"language":711,"meta":469,"style":469},"[Resolve]\nFallbackDNS=8.8.8.8\nDNSSEC=false\n\n",[471,20298,20299,20304,20309],{"__ignoreMap":469},[474,20300,20301],{"class":476,"line":477},[474,20302,20303],{},"[Resolve]\n",[474,20305,20306],{"class":476,"line":507},[474,20307,20308],{},"FallbackDNS=8.8.8.8\n",[474,20310,20311],{"class":476,"line":547},[474,20312,20313],{},"DNSSEC=false\n",[439,20315,20316],{},"The first line defines a fallback DNS if all of your dynamically configured nameservers fail. At least for me this was\nuseful. The second line is more important – it deactivates DNSSEC. DNSSEC defines security extensions on top of the DNS\nprotocol. Although this sounds like a very reasonable thing to do you’ll – especially in private networks – encounter\nnameservers who do not (properly) support DNSSEC. Sadly it can not be configured per connected network but only\nglobally. So for my setup I deactivated the whole thing.",[439,20318,20319],{},"After configuration systemd-resolved can be enabled and started:",[464,20321,20323],{"className":709,"code":20322,"language":711,"meta":469,"style":469},"sudo systemctl enable systemd-resolved --now\n\n",[471,20324,20325],{"__ignoreMap":469},[474,20326,20327],{"class":476,"line":477},[474,20328,20329],{},"sudo systemctl enable systemd-resolved --now\n",[439,20331,20332,20333,20338],{},"Now that systemd-resolved is active we need to push new nameserver configurations to systemd-resoved everytime a new\nVPN is connected (and remove them if the VPN is disconnected). If you do not use NetworkManager’s OpenVPN integration\nbut operate OpenVPN directly you can\nuse ",[1002,20334,20337],{"href":20335,"rel":20336},"https://github.com/jonathanio/update-systemd-resolved",[1006],"Jonathan Wright’s very useful update-systemd-resolved","\nscript. Just configure it in your OpenVPN config file to be run on connect and disconnect. When you use NetworkManager\nthings are even simpler: Just activete systemd-resolved in the configuration by adding\n/etc/NetworkManager/conf.d/resolved.conf:",[464,20340,20342],{"className":709,"code":20341,"language":711,"meta":469,"style":469},"[main]\ndns=systemd-resolved\n\n",[471,20343,20344,20349],{"__ignoreMap":469},[474,20345,20346],{"class":476,"line":477},[474,20347,20348],{},"[main]\n",[474,20350,20351],{"class":476,"line":507},[474,20352,20353],{},"dns=systemd-resolved\n",[439,20355,20356],{},"And then restart NetworkManager:",[464,20358,20360],{"className":709,"code":20359,"language":711,"meta":469,"style":469},"sudo systemctl restart NetworkManager\n",[471,20361,20362],{"__ignoreMap":469},[474,20363,20364],{"class":476,"line":477},[474,20365,20359],{},[439,20367,20368],{},"Upon each connect and disconnect of standard networks (wifi, ethernet and 4G) or VPNs NetworkManager will update the\nnameserver information in systemd-resolved.",[3938,20370,20372],{"id":20371},"let-your-software-use-systemd-resolved-for-dns-resolution","Let your software use systemd-resolved for DNS resolution",[439,20374,20375,20376,20381],{},"I assume most of the software on Linux is using libc for name resolution. In libc the name resolution is configured via\na file called ",[1002,20377,20380],{"href":20378,"rel":20379},"http://man7.org/linux/man-pages/man5/nsswitch.conf.5.html",[1006],"/etc/nsswitch.conf",". Besides other sections\nnsswitch.conf has a section for hostname resolution. Here is the relevant line from the file:",[464,20383,20385],{"className":709,"code":20384,"language":711,"meta":469,"style":469},"hosts: files resolve [!UNAVAIL=return] dns myhostname\n\n",[471,20386,20387],{"__ignoreMap":469},[474,20388,20389],{"class":476,"line":477},[474,20390,20391],{},"hosts: files resolve [!UNAVAIL=return] dns myhostname\n",[439,20393,20394,20395,20400],{},"The important part is the ‘resolve’ behind ‘files’. It tells libc to load a plugin\ncalled ",[1002,20396,20399],{"href":20397,"rel":20398},"https://www.freedesktop.org/software/systemd/man/nss-resolve.html",[1006],"libnss_resolve"," and try to use for hostname\nresolution before other plugins like the traditional libnss_dns are tried. By this all software using libc’s name\nresolution will automatically use systemd-resolved from now on.",[439,20402,20403],{},"There are, however, other applications that parse your /etc/resolv.conf on their own and circumvent systemd-resolved\nunconsciously. For this applications /etc/resolv.conf needs to be replaced with a symlink to either",[464,20405,20407],{"className":709,"code":20406,"language":711,"meta":469,"style":469},"/run/systemd/resolve/resolv.conf\n\n",[471,20408,20409],{"__ignoreMap":469},[474,20410,20411],{"class":476,"line":477},[474,20412,20413],{},"/run/systemd/resolve/resolv.conf\n",[439,20415,20416],{},"or",[464,20418,20420],{"className":709,"code":20419,"language":711,"meta":469,"style":469},"/run/systemd/resolve/stub-resolv.conf\n\n\n",[471,20421,20422],{"__ignoreMap":469},[474,20423,20424],{"class":476,"line":477},[474,20425,20426],{},"/run/systemd/resolve/stub-resolv.conf\n",[439,20428,20429],{},"The first file just contains all nameserver entries that have been configured for systemd-resolved. This is again not\nvery helpful for our scenario since it will definitly contain more than three entries and you cannot be sure whether\nother applications parsing that file have the same limitation as the libnss_dns library.",[439,20431,20432],{},"The second file is better for our scenario. It just contains a single nameserver entry which points to 127.0.0.53\nwhich is a local DNS server that is part of systemd-resolved and just forwards name resolution requests to the\nconfigured nameservers.",[439,20434,20435],{},"So execute the following on your system:",[464,20437,20439],{"className":709,"code":20438,"language":711,"meta":469,"style":469},"sudo mv /etc/resolv.conf /etc/resolv.conf.bak\nsudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf\n\n",[471,20440,20441,20446],{"__ignoreMap":469},[474,20442,20443],{"class":476,"line":477},[474,20444,20445],{},"sudo mv /etc/resolv.conf /etc/resolv.conf.bak\n",[474,20447,20448],{"class":476,"line":507},[474,20449,20450],{},"sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf\n",[3938,20452,20454],{"id":20453},"gnome-software-and-systemd-resolved","Gnome Software and systemd-resolved",[439,20456,20457],{},"For some reason Gnome Software stopped working after I activated systemd-resolved in /etc/nsswitch.conf – it failed to\nresolve hostnames when downloading packages. I was able to fix this by changing the order in /etc/nsswitch.conf so that\ndns is before resolve. I did not dig into details here. If you happen to know what could be the reason for this – feel\nfree to get in touch with me. 😉",[3938,20459,20461],{"id":20460},"closing-words","Closing words",[439,20463,20464],{},"As you can see from the length of this article, it takes a significant amount of effort and knowledge to configure your\nLinux desktop to support multiple and simultaneously connected VPN with working DNS resolution. Nevertheless, once\nconfigured correctly everything works really well! 😀",[439,20466,20467,20468,1402],{},"So, I hope everything was understandable and helpful for you. If you have any remarks drop a line on\nTwitter ",[1002,20469,20472],{"href":20470,"rel":20471},"https://twitter.com/robinjayasinghe",[1006],"@robinjayasinghe",[3938,20474,20476],{"id":20475},"update-052020","Update (05/2020)",[439,20478,20479],{},"In the meantime some time has passed and for various reasons I switched through some distros. In Debian 10 the\nsystemd-resolved approach described in this article worked well. Only Gnome Software did not work reliably when\nresolved was active. Afterwards I switched back to Fedora and used Fedora 31 for a while. Here I had the option to use\nsystemd-resolved in NetworkManager but decided to use the alternatively offered dnsmasq plugin for NetworkManager. This\nworked like a charm and there was nothing more to configure than activating the plugin. However, Gnome Software still\nhad issues for me. I did not do any research on that. Due to a crashed upgrade to Fedora 32 I rage-migrated to Ubuntu\n20.04 LTS. I am not yet sure what Ubuntu did beneath the surface but connecting to multiple VPNs at the same time\nincluding proper DNS resolution works OOTB. Kudos to the folks at Canonical. Gnome Software works (even when connected\nto VPNs). :partyhat:",[1024,20481,20482],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":20484},[20485,20486,20487,20488,20489,20490,20491,20492,20493],{"id":20192,"depth":507,"text":20193},{"id":20202,"depth":507,"text":20203},{"id":20245,"depth":507,"text":20246},{"id":20274,"depth":507,"text":20275},{"id":20289,"depth":507,"text":20290},{"id":20371,"depth":507,"text":20372},{"id":20453,"depth":507,"text":20454},{"id":20460,"depth":507,"text":20461},{"id":20475,"depth":507,"text":20476},[1031],"2018-11-04T13:53:34","https://synyx.de/blog/connect-to-multiple-vpns-at-once-using-networkmanager-and-systemd-resolved/",{},"/blog/connect-to-multiple-vpns-at-once-using-networkmanager-and-systemd-resolved",{"title":20127,"description":469},"blog/connect-to-multiple-vpns-at-once-using-networkmanager-and-systemd-resolved",[20502,20503,20504,20505,20506],"dns","linux","networking","openvpn","vpn","It’s not DNS There is a no way it’s DNS It was DNS from DNS Haiku The dark side When I started at synyx I chose to get a MacBook in order to be productive as quick as possible. I still had the cumbersome Linux desktop experience from my previous employer in mind. ;) At synyx, however, things are different: we have a highly skilled admin team that provides a very convenient environment to use Linux on laptops.","V0f3e_bN6YwO0GDFFg6sOJp8A9JIH6--ZO6c_uAbOdI",{"id":20510,"title":20511,"author":20512,"body":20513,"category":20850,"date":20851,"description":20852,"extension":1034,"link":20853,"meta":20854,"navigation":916,"path":20855,"seo":20856,"slug":20517,"stem":20858,"tags":20859,"teaser":20861,"__hash__":20862},"blog/blog/konferenz-logbuch-bedcon-2018.md","Konferenz-Logbuch: BEDCon 2018",[60],{"type":432,"value":20514,"toc":20840},[20515,20518,20539,20545,20548,20551,20557,20560,20563,20569,20571,20575,20579,20585,20587,20590,20594,20616,20620,20635,20641,20644,20648,20659,20663,20666,20670,20689,20692,20703,20707,20721,20724,20744,20752,20756,20768,20771,20782,20785,20788,20799,20803,20806,20809,20817,20820,20822,20831,20834],[435,20516,20511],{"id":20517},"konferenz-logbuch-bedcon-2018",[439,20519,20520,20521,20526,20527,20532,20533,20538],{},"Bei der diesjährigen ",[1002,20522,20525],{"href":20523,"rel":20524},"http://www.bed-con.org/2018/",[1006],"BEDCon"," ist synyx gleich doppelt vertreten gewesen. Nicht nur der\nTalk “Observability in einer Microservice Welt” von Andreas Weigel (",[1002,20528,20531],{"href":20529,"rel":20530},"https://twitter.com/aendi",[1006],"@aendi",") und Jakob\nFels (",[1002,20534,20537],{"href":20535,"rel":20536},"https://twitter.com/JakobFels",[1006],"@JakobFels",") war dieses Mal eine Premiere. Auch unser Stand im Messe-Bereich der\nKonferenz hat das erste Mal zu Gesprächen, Diskussion und natürlich jede Menge synyx Stickern eingeladen.",[439,20540,20541],{},[2205,20542],{"alt":20543,"src":20544},"Thomas Kraft am Bedcon Stand","https://media.synyx.de/uploads/2019/04/bedcon_stand-768x865.png",[439,20546,20547],{},"Zwar nicht der größte Stand, dafür aber komplett in der Bahn transportiert.",[439,20549,20550],{},"Unsere Reisegruppe von insgesamt fünf synyxern und Jakob war zwar in allen belangen bestens auf die Konferenz\nvorbereitet (z. Bsp. waren alle Folien für den Talk bereits VOR der Abreise fertig) nur bei der getränketechnischen\nVersorgung war wohl etwas knapp kalkuliert worden, sodass am Ende auch das Boardbistro nur noch mit leeren Kästen da\nstand.",[439,20552,20553],{},[2205,20554],{"alt":20555,"src":20556},"Getting started Bedcon","https://media.synyx.de/uploads/2018/09/bedcon_getting-started-768x520.jpg",[439,20558,20559],{},"Gut vorbereitet aber was fehlt?",[439,20561,20562],{},"Immerhin konnte eine vermeindlich kaputte Zapfanlage, der Techniker war bereits informiert, wieder in Gang gebracht\nwerden, sodass wir relativ entspannt in Berlin ankamen und einen ruhigen ersten Abend mit vietnamesichen Essen und\nkleineren Arbeiten am Talk verbrachten.",[439,20564,20565],{},[2205,20566],{"alt":20567,"src":20568},"Feilen bis zur letzen Minute","https://media.synyx.de/uploads/2018/09/bedcon_wip-768x531.jpg",[439,20570,20567],{},[3938,20572,20574],{"id":20573},"talks","Talks",[1065,20576,20578],{"id":20577},"observability-in-einer-microservice-welt","Observability in einer Microservice Welt",[439,20580,20581],{},[2205,20582],{"alt":20583,"src":20584},"#somethingwithbananas","https://media.synyx.de/uploads/2018/09/bedcon_banane-768x768.png",[439,20586,20583],{},[439,20588,20589],{},"Der erste Konferenztag startete gleich mit einen Höhepunkt der zwei Tage. Direkt im ersten Slot starteten Andi und Jakob\nmit ihrem Talk zum Thema Observability. Die Migration bei dm-drogerie markt zu einer verteilten Systemarchitektur hat\nviele Herausforderungen nach sich gezogen. Neben vielen Einblicken in die tägliche Arbeit und Erkenntnissen durch den\nDevOps-Kulturwandel, wurden die Themen Logging, Metriken und Tracing vorgestellt. Dabei kam der visuelle Part nicht zu\nkurz. Die Folien von @schdaff führten nicht nur wie ein roter Faden durch das Thema, sondern machten den Talk auch noch\nsehr anschaulich und unterhaltsam.",[1633,20591,20593],{"id":20592},"kernaussagen","Kernaussagen",[994,20595,20596,20599,20613],{},[997,20597,20598],{},"Unterscheidung zwischen verschiedenen Sichten auf verteilte Systeme mit Logging, Metriken und Tracing",[997,20600,20601,20602],{},"Livedemo mit entsprechenden Umsetzungen:\n",[994,20603,20604,20607,20610],{},[997,20605,20606],{},"Logging via Logstash in Elastic Search und Visualisierung mit Kibana",[997,20608,20609],{},"Metriken mit dem Micrometer Framework als Metrikenfassade und Influx und Grafana zur Speicherung und\nVisualisierung",[997,20611,20612],{},"Tracing mit Spring Cloud Sleuth und Zipkin",[997,20614,20615],{},"DevOps-Kulturwandel bringt Chancen zur Verbesserung der Zusammenarbeit: Weg von Schuldzuweisungen hin zu\nverantwortungsbewussteren Handeln.",[1065,20617,20619],{"id":20618},"offline-first-kein-netz-kein-fehler-zufriedene-nutzer","Offline First – kein Netz, kein Fehler, zufriedene Nutzer",[439,20621,20622,20623,20628,20629,20634],{},"Viele Webanwendungen glänzen nicht gerade wenn es um ihre Offline-Fähigkeit geht. Direkt von Anfang an bei der\nEntwicklung auch Nutzer ohne oder mit sehr schlechter Verbindung etwa bei Usability Fragen mit zu beachten, liegt oft\ngenug nicht im Projektscope. Genau darum ging es\nbei ",[1002,20624,20627],{"href":20625,"rel":20626},"https://de.slideshare.net/devday-dd/devday-2018-ulrich-deiters-offline-first-kein-netz-kein-fehler-zufriedene-nutzer",[1006],"“Offline First – kein Netz, kein Fehler, zufriedene Nutzer”","\nvon ",[1002,20630,20633],{"href":20631,"rel":20632},"https://twitter.com/ulid000",[1006],"Ulrich Deiters",", der damit viele wichtige Grundsätze und Denkanstöße lieferte.",[439,20636,20637],{},[2205,20638],{"alt":20639,"src":20640},"Disconnected-Dino","https://media.synyx.de/uploads/2018/09/offline-dino.png",[439,20642,20643],{},"Die Nutzererwartung ist dagegen ganz klar – solange die Anwendung keine klare Aussage zum aktuellen Netzwerkzustand\nliefert, ist man eher enttäuscht wenn etwa der formulierte Tweet auf Grund von Timeouts oder anderen Fehlern im Nirvana\nverschwindet. Anhand von vielen teils auch sehr amüsanten Beispielen wurde einem schnell klar wie oft man sich in die\nZone begibt, in der der Endnutzer recht verärgert die App schließt und oder am liebsten deinstallieren würde. Aber auch\nCAP und der die damit verbundene Partition Tolerance kam im Talk nicht zu kurz und wurde nochmal näher beleuchtet, sowie\nFallstricke aufgezeigt, in die man laufen kann wenn der Client keine Verbindung hat.",[1633,20645,20647],{"id":20646},"ansätze-zum-handeln","Ansätze zum Handeln",[994,20649,20650,20653,20656],{},[997,20651,20652],{},"Zeitstempel der letzten Änderung der Daten bzw. der letzten Synchronisation anbieten, dadurch ist der Nutzer in der\nLage die Datenqualität und -aktualität besser einschätzen zu können.",[997,20654,20655],{},"Klare Aussage über den aktuellen Zustand der Netzwerkverbindung treffen, nicht unbedingt nur wenn der Nutzer offline\nist.",[997,20657,20658],{},"Lokale Datenbank/Caches können einem über so manche Verbindungsunterbrechung retten auch wenn dann vielleicht nicht\nalle Daten konsistent sind. Hier gilt es abzuwägen.",[1633,20660,20662],{"id":20661},"das-credo","Das Credo",[439,20664,20665],{},"“Kein Netz” ist nicht zwingend ein Fehler!",[1065,20667,20669],{"id":20668},"self-contained-integrationstests-mit-docker-und-testcontainers","Self-Contained Integrationstests mit Docker und Testcontainers",[439,20671,20672,20673,20628,20678,20683,20684,20688],{},"Wer kennt es nicht, die lokale Datenbankinstallation hat wieder mal nicht die Migration beim Initialisieren des\nIntegrationstests überlebt. Oder aber man möchte möglichst mit echter Datenbankanbindung testen da die Implementierung\nsehr vom jeweiligen Datenbanksystem abhängt und nicht durch eine In-Memory Datenbank umgesetzt wird. Ein weiteres\nEinsatzgebiet sind UI Tests die beispielsweise mit Selenium Tests durchführen und ebenfalls ein spezielles Testsetup\nbenötigen. An dieser Stelle sind die\nin ",[1002,20674,20677],{"href":20675,"rel":20676},"https://www.innoq.com/en/talks/2018/09/self-contained-integrationstests-mit-docker-und-testcontainers/",[1006],"“Self-Contained Integrationstests mit Docker und Testcontainers”",[1002,20679,20682],{"href":20680,"rel":20681},"https://twitter.com/michaelvitz",[1006],"Michael Vitz"," vorgestellten ",[1002,20685,1173],{"href":20686,"rel":20687},"http://testcontainers.org",[1006]," ein\nsinnvoller Ansatz um die Herausforderung zu meistern.",[439,20690,20691],{},"Das Konzept der Testcontainer basiert auf Docker Containern die sich als JUnit Rules in den Testcode integrieren lassen.\nDabei bieten sich auch die Möglichkeit komplett eigene Container bzw. Setups zu nutzen. Für viel verwendete Fälle gibt\nes zusätzlich dedizierte Abstraktionen die das Handling vereinfachen:",[994,20693,20694,20697,20700],{},[997,20695,20696],{},"mysql, postgresql oder oracle-xe",[997,20698,20699],{},"selenium",[997,20701,20702],{},"nginx",[1065,20704,20706],{"id":20705},"test-the-architecture","Test the Architecture",[439,20708,20709,20710,20628,20715,20720],{},"Tests können auf ganz unterschiedliche Aspekte der Software abzielen. Ein oft, vielleicht auch in Zeiten von\nMicroservices, vernachlässigter Teil ist die Architektur bzw. das einhalten, von beim Projektstart getroffenen\nArchitekturentscheidungen. An dieser Stelle setzt der\nTalk ",[1002,20711,20714],{"href":20712,"rel":20713},"https://speakerdeck.com/dennisrippinger/test-the-architecture",[1006],"“Test the Architecture”",[1002,20716,20719],{"href":20717,"rel":20718},"https://twitter.com/dennisrippinger",[1006],"Dennis Rippinger"," an und zeigt Möglichkeiten wie man getroffene Entscheidungen\nnachhaltig automatisiert festzuhalten.",[439,20722,20723],{},"Während des Talks wurden anhand von Code Beispielen verschiedene Tools vorgestellt die Architekturen in Regeln und Tests\ngießen lassen:",[994,20725,20726,20732,20738],{},[997,20727,20728,20731],{},[448,20729,20730],{},"Degraph",": Visualisierung von Klassen und Package Strukturen mit Junit Testing Support Bietet allerdings nur\nLayer-basiertes Testing und hat durch den Scala Background weniger Fokus auf Java.",[997,20733,20734,20737],{},[448,20735,20736],{},"jqAssist",": Bietet auch die Möglichkeit der Visualisierung ähnlich wie Degraph. Im Talk wurde gezeigt das auf Basis\nder Graphen eine Graphdatenbank (Neo4J) befüllt werden kann. Darauf aufbauend können dann in der Cypher Query\nLanguage (Querylanguage der Neo4J Datenbank) Tests schreiben.",[997,20739,20740,20743],{},[448,20741,20742],{},"ArchUnit",": Dieses Framework bietet die ebenfalls die Möglichkeit zum Testen der Architektur in Unittests. Dabei\nkönnen zum Beispiel Zugriffe zwischen Packages klar abgetestet werden.",[439,20745,20746,20747,20751],{},"Danke für die vielen Beispiele die dazu auf ",[1002,20748,4042],{"href":20749,"rel":20750},"https://github.com/DennisRippinger/test-the-architecture",[1006]," zu finden\nsind.",[1065,20753,20755],{"id":20754},"wie-werde-ich-ein-erfolgreicher-software-architekt","Wie werde ich ein erfolgreicher Software-Architekt?",[439,20757,20758,20759,20628,20764,20767],{},"Es gehört wohl mehr dazu gute Softwaredesignentscheidungen zu treffen als nur die neuesten Buzzwords von Konferenzen in\ndas eigene Projekt einfließen zu lassen. Genau darum ging es im\nVortrag ",[1002,20760,20763],{"href":20761,"rel":20762},"https://www.innoq.com/de/talks/2018/09/wie-werde-ich-ein-erfolgreicher-software-architekt-bedcon/",[1006],"“Wie werde ich ein erfolgreicher Software-Architekt?”",[1002,20765,375],{"href":8194,"rel":20766},[1006],", in dem die Rolle des klassischen Softwarearchitekten mit einer\nmoderneren Interpretation anhand von unterschiedlichen Aspekten vorgestellt wurde.",[439,20769,20770],{},"Generische Architekturaspekte wie",[994,20772,20773,20776,20779],{},[997,20774,20775],{},"Saubere Architektur und hohe Code Qualität",[997,20777,20778],{},"Skalierbarkeit",[997,20780,20781],{},"Favorisiertes Framework/Sprache",[439,20783,20784],{},"lösen generell erstmal keine Probleme.",[439,20786,20787],{},"Es geht viel mehr darum technische Expertise im Team zu nutzen und zu fördern statt die Expertise und damit die\nEntscheidung bei einer Person zu halten. Entsprechend nimmt der Architekt eher eine moderierende Rolle ein da die\nEntscheidung im Team getroffen wird. Im Talk wurde dieser Aspekt auch mit dem Bild eines kollaborativen\nGesellschaftsspiel verglichen da durchaus ähnliche Aspekte Rolle spielen:",[994,20789,20790,20793,20796],{},[997,20791,20792],{},"Keiner kann alleine gewinnen",[997,20794,20795],{},"Jeder übernimmt eine gewisse Rolle",[997,20797,20798],{},"Gute Kommunikation ist eine sehr wichtige Komponente",[1633,20800,20802],{"id":20801},"das-fazit-des-talks","Das Fazit des Talks",[439,20804,20805],{},"“Aufgabe eines Architekten ist es kontinuierlich Entscheidungen ohne genug Informationen zu treffen”",[439,20807,20808],{},"Dabei gilt:",[994,20810,20811,20814],{},[997,20812,20813],{},"Die besten Entscheidungen sind solche die man später treffen kann",[997,20815,20816],{},"Oft können Entscheidungen nur auf Grundlage von wenig oder unzureichender Informationslage getroffen werden und das\nist auch OK so.",[439,20818,20819],{},"Aus meiner Sicht brachte der Talk zwar wenig wirklich neue Erkenntnisse stattdessen war er eine klare Bestätigung von\nvielen Erfahrungen und Werten die wir bei synyx in der täglichen Arbeit erleben und fördern.",[3938,20821,1385],{"id":1384},[439,20823,20824,20825,20830],{},"Die diesjährige BEDCon hat sich in allen belangen gelohnt. Nicht nur wegen den vielen wirklich guten Vorträgen, sondern\nauch die synyx & Jakob Reisegruppe eilte von einer Überraschung zur nächsten. So gab es für drei von\nuns ",[1002,20826,20829],{"href":20827,"rel":20828},"https://twitter.com/aendi/status/1037749829622005760",[1006],"tolle Preise"," zu gewinnen! Zwar waren auch zwei Trostpreise\ndabei, aber hey immerhin wars kein jQuery-Buch! Und auch wenn die parallel laufende “Lack & Leder”-Konferenz nicht\nzwingend bei jedem den Geschmack getroffen hat, trug sie zur allgemeinen Heiterkeit der Truppe bei 😉",[439,20832,20833],{},"In diesem Sinne:",[439,20835,20836],{},[2205,20837],{"alt":20838,"src":20839},"Code-with-Attitude-Bier","https://media.synyx.de/uploads/2018/09/bedcon_beerwithattitude-768x1121.jpg",{"title":469,"searchDepth":507,"depth":507,"links":20841},[20842,20849],{"id":20573,"depth":507,"text":20574,"children":20843},[20844,20845,20846,20847,20848],{"id":20577,"depth":547,"text":20578},{"id":20618,"depth":547,"text":20619},{"id":20668,"depth":547,"text":20669},{"id":20705,"depth":547,"text":20706},{"id":20754,"depth":547,"text":20755},{"id":1384,"depth":507,"text":1385},[1031],"2018-09-14T14:10:55","Bei der diesjährigen BEDCon ist synyx gleich doppelt vertreten gewesen. Nicht nur der\\nTalk “Observability in einer Microservice Welt” von Andreas Weigel (@aendi) und Jakob\\nFels (@JakobFels) war dieses Mal eine Premiere. Auch unser Stand im Messe-Bereich der\\nKonferenz hat das erste Mal zu Gesprächen, Diskussion und natürlich jede Menge synyx Stickern eingeladen.","https://synyx.de/blog/konferenz-logbuch-bedcon-2018/",{},"/blog/konferenz-logbuch-bedcon-2018",{"title":20511,"description":20857},"Bei der diesjährigen BEDCon ist synyx gleich doppelt vertreten gewesen. Nicht nur der\nTalk “Observability in einer Microservice Welt” von Andreas Weigel (@aendi) und Jakob\nFels (@JakobFels) war dieses Mal eine Premiere. Auch unser Stand im Messe-Bereich der\nKonferenz hat das erste Mal zu Gesprächen, Diskussion und natürlich jede Menge synyx Stickern eingeladen.","blog/konferenz-logbuch-bedcon-2018",[20860,16640,8276,9556],"bedcon","Bei der diesjährigen BEDCon ist synyx gleich doppelt vertreten gewesen. Nicht nur der Talk “Observability in einer Microservice Welt” von Andreas Weigel (@aendi) und Jakob Fels (@JakobFels) war dieses Mal eine Premiere. Auch unser Stand im Messe-Bereich der Konferenz hat das erste Mal zu Gesprächen, Diskussion und natürlich jede Menge synyx Stickern eingeladen. Dabei handelte es sich zwar nicht um den größten Stand, dafür wurde er aber komplett in der Bahn transportiert.","deVB5Imm-u3lBGhO_fR3D94wJ4CXE9Vv02Q2Ay0l3R4",{"id":20864,"title":20865,"author":20866,"body":20867,"category":21234,"date":21235,"description":469,"extension":1034,"link":21236,"meta":21237,"navigation":916,"path":21238,"seo":21239,"slug":20871,"stem":21240,"tags":21241,"teaser":21248,"__hash__":21249},"blog/blog/implementing-at-least-once-delivery-with-rabbitmq-and-springs-rabbittemplate.md","Implementing At Least Once Delivery With RabbitMQ and Spring’s RabbitTemplate",[154],{"type":432,"value":20868,"toc":21220},[20869,20872,20876,20879,20899,20902,20905,20909,20912,20918,20921,20932,20935,20946,20950,20953,20957,20960,20963,20967,20970,20977,20980,20984,20987,20993,20996,21000,21007,21010,21025,21081,21085,21088,21091,21165,21168,21175,21179,21195,21205,21209,21212,21215,21218],[435,20870,20865],{"id":20871},"implementing-at-least-once-delivery-with-rabbitmq-and-springs-rabbittemplate",[3938,20873,20875],{"id":20874},"message-delivery-characteristics","Message Delivery Characteristics",[439,20877,20878],{},"First some theory about delivery semantics in messaging systems. When a system wants to communicate via a message broker\nthe developer needs a clear understanding of the delivery semantics. At first one needs to know if and how often a\nmessage will be delivered to the broker (and potential consumers):",[994,20880,20881,20887,20893],{},[997,20882,20883,20886],{},[990,20884,20885],{},"At most once"," – the message is delivered at most once but also not at all.",[997,20888,20889,20892],{},[990,20890,20891],{},"At least once"," – the message guaranteed to be delivered but can be delivered multiple times.",[997,20894,20895,20898],{},[990,20896,20897],{},"Exactly once"," – the message is guaranteed to be delivered exactly once.",[439,20900,20901],{},"The second point that is – at least at first sight – important for message consumers is the ordering of messages.\nOrdering in a messaging context means that messages arrive at the consumer in the same order as they have been sent by a\ngiven producer.",[439,20903,20904],{},"Even if some brokers claim to guarantee “exactly once in order delivery” it’s recommended to not take the order of\nincoming messages for granted. Because a) things can get messy in distributed systems and b) you might not want to rely\non semantics of your current broker too much since you’ll end up with another broker in the future.",[3938,20906,20908],{"id":20907},"amqp-rabbitmq-send-semantics","AMQP / RabbitMQ Send Semantics",[439,20910,20911],{},"In my current customer project we are using RabbitMQ as a message broker to implement an event-driven archticure. Since\nwe are building our services and frontend apps with Spring Boot we can use the very convenient RabbitTemplate of Spring\nAMQP.",[439,20913,20914,20917],{},[990,20915,20916],{},"Note:"," AMQP is the messaging protocol. One implemantation is RabbitMQ. In this article I might use both terms\ninterchangeably.",[439,20919,20920],{},"Once the correct connection paramters for your RabbitMQ paramters are provided to your applicaiton context your\napplication will automatically connect to the RabbitMQ server. When your code needs to send messages via AMQP you just\nneed to autowire an instance of RabbitTemplate and call the send method like this:",[464,20922,20926],{"className":20923,"code":20924,"language":20925,"meta":469,"style":469},"language-abap shiki shiki-themes github-light github-dark","rabbitTemplate.send(\"your_exchange\", \"a_routing_key\", yourMessageObject);\n","abap",[471,20927,20928],{"__ignoreMap":469},[474,20929,20930],{"class":476,"line":477},[474,20931,20924],{},[439,20933,20934],{},"If the RabbitMQ server is available the call just returns and the message seems to be delivered. However, as you are\nleaving your JVM’s process you enter the realm of distributed systems. Virtually everything can go wrong now. 😉",[439,20936,20937,20938,20941,20942],{},"So, comparing the above mentioned delivery semantics with the observations from our RabbitMQ usage in Spring Boot,\nRabbitMQ guarantees ",[990,20939,20940],{},"at least once delivery"," if not configured elsewise. Also, there is only one small cornercase where\nmessage ordering is\nguaranteed: ",[1002,20943,20944],{"href":20944,"rel":20945},"https://www.rabbitmq.com/semantics.html#ordering",[1006],[3938,20947,20949],{"id":20948},"the-dirty-details","The Dirty Details",[439,20951,20952],{},"When looking at the send semantics of the RabbitTemplate in Spring Boot we can seperate into two topics:",[1065,20954,20956],{"id":20955},"passing-the-notwork","Passing The Notwork",[439,20958,20959],{},"The first is about reaching the RabbitMQ server at all. So, the network connection can be shaky or not available. Or the\nhost is reachable but the RabbitMQ service on the host is down. In that case the RabbitTemplate will throw an exception\nto the caller. From this point on the caller code can handle the failed message delivery conciously.",[439,20961,20962],{},"From an API interaction point of view the message can be considered as delivered (to the broker) if no exception is\nthrown by the send call. So this is quite comparable to an RestTemplate / HttpClient call. However, as you’ll see later\nthings can still go wrong even if the call returned successfully.",[1065,20964,20966],{"id":20965},"delivery-inside-the-message-broker","Delivery Inside the Message Broker",[439,20968,20969],{},"When you use an AMQP message broker things are a bit different than when interacting via HTTP. This brings us to the\nsecond topic: Transferring the message to the broker is only the first part of a longer process. The broker tries to\nroute messages to the queues that are bound to the exchange on which the message is sent.",[439,20971,20972,20973,20976],{},"As mentiond – the broker ",[990,20974,20975],{},"tries"," to route the messages. If no queue is bound the message is just rushing through the\nexchange and is lost in the end. Another case is that if the target exchange does not exist (for whatever reason) the\nbroker returns an HTTP like error code: 404. On the client side the send() method call has already returned. The\nRabbitMQ template has no chance to notify it’s caller of the failed delivery. The only thing left is writing an error to\nthe application’s log.",[439,20978,20979],{},"There are probably more cases that match this problem space. One can categorize them like “received message but could\nnot deliver it to any queues”. The important point for all problems in this category is that once the\nRabbitTemplate.send() method returned the client code cannot be sure whether the message has really been delivered.",[3938,20981,20983],{"id":20982},"choose-your-weapon","Choose Your Weapon",[439,20985,20986],{},"For most of your scenarios this behavior is perfectly OK. At least with many of our cases we emit event messages in a\n“fire and forget” style. The code is written without knowledge of and excpectation towards any consumers.\nInconsistencies caused by not delivered messages need to be detected and handled seperatly.",[439,20988,20989,20990,1402],{},"There are however scenarios where you really want to be sure that a given message has a) reached the message broker and\nb) been routed – the above mentioned ",[990,20991,20992],{},"at least onće delivery",[439,20994,20995],{},"In that case the AMQP protocol, RabbitMQ and Spring’s RabbitTemplate offer some (configuration) tools to help you.",[1065,20997,20999],{"id":20998},"enable-and-handle-publisher-confirms-callbacks","Enable and Handle Publisher-Confirms Callbacks",[439,21001,21002,21003],{},"The publisher confirms callback helps the application developer to have his code to be notified when the RabbitMQ server\nhas received a message and has delivered it to the desired exchange. Technical details can be found\nhere: ",[1002,21004,21005],{"href":21005,"rel":21006},"https://www.rabbitmq.com/confirms.html",[1006],[439,21008,21009],{},"To enable the publisher confirms callbacks two things must be done:",[994,21011,21012,21022],{},[997,21013,21014,21015,21018,21019,21021],{},"The property ",[471,21016,21017],{},"spring.rabbitmq.publisher-confirms"," must be set to ",[471,21020,829],{},". Please notice that this property has global\neffect. Even if there is more than one RabbitTemplate configured.",[997,21023,21024],{},"Since the RabbitMQ server cannot return the cofirmation synchonously some callback code has to be registered so it can\nbe called once the confirm has arrived. The callback can be registered during the Creation of the RabbitTemplate.\nInstead of using the autoconfigured RabbitTemplate one needs to configure the RabbitTemplate manually and then provide\na new Instance of RabbitTemplate.CofirmCallback to the RabbitTemplate::setConfirmCallback method. One of the arguments\nof the confirm method of that class is the boolean ‘ack’ if it’s ‘true’ the publisher has confirmed that the message\nhas been received. If it’s false something went wrong.",[464,21026,21028],{"className":709,"code":21027,"language":711,"meta":469,"style":469},"template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {\n\n @Override\n public void confirm(CorrelationData correlationData, boolean ack, String cause) {\n if(ack) {\n applicationEventPublisher.publish(new PublishConfirmedEvent(correlationData.getId());\n } else {\n applicationEventPublisher.publish(new PublishNotConfirmedEvent(correlationData.getId(), cause);\n }\n }\n});\n",[471,21029,21030,21035,21039,21044,21049,21054,21059,21064,21069,21073,21077],{"__ignoreMap":469},[474,21031,21032],{"class":476,"line":477},[474,21033,21034],{},"template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {\n",[474,21036,21037],{"class":476,"line":507},[474,21038,917],{"emptyLinePlaceholder":916},[474,21040,21041],{"class":476,"line":547},[474,21042,21043],{}," @Override\n",[474,21045,21046],{"class":476,"line":584},[474,21047,21048],{}," public void confirm(CorrelationData correlationData, boolean ack, String cause) {\n",[474,21050,21051],{"class":476,"line":607},[474,21052,21053],{}," if(ack) {\n",[474,21055,21056],{"class":476,"line":642},[474,21057,21058],{}," applicationEventPublisher.publish(new PublishConfirmedEvent(correlationData.getId());\n",[474,21060,21061],{"class":476,"line":663},[474,21062,21063],{}," } else {\n",[474,21065,21066],{"class":476,"line":694},[474,21067,21068],{}," applicationEventPublisher.publish(new PublishNotConfirmedEvent(correlationData.getId(), cause);\n",[474,21070,21071],{"class":476,"line":700},[474,21072,5704],{},[474,21074,21075],{"class":476,"line":913},[474,21076,1276],{},[474,21078,21079],{"class":476,"line":920},[474,21080,15357],{},[1065,21082,21084],{"id":21083},"handle-return-callbacks","Handle Return Callbacks",[439,21086,21087],{},"In contrast to the Publisher Confirms Callback the Return Callback is not activated by a configuration property but is\ndirectly set on the RabbitTemplate object with setMandatory(true). So this has only effect for the one instance of the\nRabbitTemplate. If you want to have different behavior to this respect you can configure several RabbitTemplates with\ndifferent Qualifiers.",[439,21089,21090],{},"By activating the mandatory flag the sent messages indicate that the sender expects the message to be routed to a queue.\nIf the message is not routed the RabbitMQ server needs to return the whole message to the sender with a reply code.",[464,21092,21094],{"className":709,"code":21093,"language":711,"meta":469,"style":469},"template.setMandatory(true);\ntemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {\n\n @Override\n public void returnedMessage(Message message, int replyCode, String replyText, String exchange,\n String routingKey) {\n\n if (replyCode == AMQP.NO_ROUTE) {\n applicationEventPublisher.publish(new NoRouteEvent(message.getMessageId(),\n replyText, replyCode, exchange, routingKey);\n } else if(...) {\n // more code for other cases goes here\n }\n }\n});\n",[471,21095,21096,21101,21106,21110,21114,21119,21124,21128,21133,21138,21143,21148,21153,21157,21161],{"__ignoreMap":469},[474,21097,21098],{"class":476,"line":477},[474,21099,21100],{},"template.setMandatory(true);\n",[474,21102,21103],{"class":476,"line":507},[474,21104,21105],{},"template.setReturnCallback(new RabbitTemplate.ReturnCallback() {\n",[474,21107,21108],{"class":476,"line":547},[474,21109,917],{"emptyLinePlaceholder":916},[474,21111,21112],{"class":476,"line":584},[474,21113,21043],{},[474,21115,21116],{"class":476,"line":607},[474,21117,21118],{}," public void returnedMessage(Message message, int replyCode, String replyText, String exchange,\n",[474,21120,21121],{"class":476,"line":642},[474,21122,21123],{}," String routingKey) {\n",[474,21125,21126],{"class":476,"line":663},[474,21127,917],{"emptyLinePlaceholder":916},[474,21129,21130],{"class":476,"line":694},[474,21131,21132],{}," if (replyCode == AMQP.NO_ROUTE) {\n",[474,21134,21135],{"class":476,"line":700},[474,21136,21137],{}," applicationEventPublisher.publish(new NoRouteEvent(message.getMessageId(),\n",[474,21139,21140],{"class":476,"line":913},[474,21141,21142],{}," replyText, replyCode, exchange, routingKey);\n",[474,21144,21145],{"class":476,"line":920},[474,21146,21147],{}," } else if(...) {\n",[474,21149,21150],{"class":476,"line":926},[474,21151,21152],{}," // more code for other cases goes here\n",[474,21154,21155],{"class":476,"line":932},[474,21156,5704],{},[474,21158,21159],{"class":476,"line":938},[474,21160,1276],{},[474,21162,21163],{"class":476,"line":944},[474,21164,15357],{},[439,21166,21167],{},"Message delivery/handling inside the message broker can fail for several reasons. The constants in the\ncom.rabbitmq.client.AMQP interface can give a hint to what can go wrong. Besides obvious codes like NOT_FOUND or\nACCESS_REFUSED the NO_ROUTE code is special since other errors are mostly caused by configuration problems. NO_ROUTE is\nreturned is when no queue (with a matching routing key) is bound to the target exchange and the mandatory flag is true.\nIt is the message producer’s way to express it’s wish for ‘at least once’ semantics.",[439,21169,21170,21171],{},"Also check the official\ndocu: ",[1002,21172,21173],{"href":21173,"rel":21174},"https://www.rabbitmq.com/reliability.html#producer",[1006],[1065,21176,21178],{"id":21177},"identifying-message-deliveries-in-global-callbacks","Identifying Message Deliveries in Global Callbacks",[439,21180,21181,21182,21185,21186,21188,21189,21192,21193,1402],{},"Both of the above mentioned techniques have one thing in common. They apply globally or at least to the scope to one\nRabbitTemplate instance. So it is important to set the ",[990,21183,21184],{},"messageId"," of the sent message with some value that can later be\nused to match the context from which the message has been sent. When you get a publisher confirms callback the initially\nset ",[990,21187,21184],{}," is now the ",[990,21190,21191],{},"correllationData.Id"," and if you get a return callback you have the initial message at hand\nwhere you can directly access the ",[990,21194,21184],{},[439,21196,21197,21198,21201,21202,1402],{},"If you have different parts of your application communicating via RabbitMQ and want to be able to distinguish the\nmessages from each other it helps to prefix the id with some meaning ful token like ",[990,21199,21200],{},"orders"," or ",[990,21203,21204],{},"notifications",[3938,21206,21208],{"id":21207},"handling-callbacks-from-the-rabbittemplate","Handling Callbacks From the RabbitTemplate",[439,21210,21211],{},"It turned out to be a useful pattern to publish meaningful Application Events from the above mentioned callbacks and\nthen listen to these in some other application code. This helps to avoid tangle between your different domains and the\npotential central RabbitMQ configuration.",[439,21213,21214],{},"Also, you can easily handle the events in an async manner if needed. Be aware that this callbacks will potentially arive\nvery quickly and also do not rely on correct ordering. It helps to implement a state machine for handling async events\nwithout fixed order in a reliable manner.",[439,21216,21217],{},"also, to be more resilient against crashes (of the JVM) between delivery attempts you might want to persist your\ndelivery attempts (in a database).",[1024,21219,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":21221},[21222,21223,21224,21228,21233],{"id":20874,"depth":507,"text":20875},{"id":20907,"depth":507,"text":20908},{"id":20948,"depth":507,"text":20949,"children":21225},[21226,21227],{"id":20955,"depth":547,"text":20956},{"id":20965,"depth":547,"text":20966},{"id":20982,"depth":507,"text":20983,"children":21229},[21230,21231,21232],{"id":20998,"depth":547,"text":20999},{"id":21083,"depth":547,"text":21084},{"id":21177,"depth":547,"text":21178},{"id":21207,"depth":507,"text":21208},[1030,1031],"2018-07-28T14:44:10","https://synyx.de/blog/implementing-at-least-once-delivery-with-rabbitmq-and-springs-rabbittemplate/",{},"/blog/implementing-at-least-once-delivery-with-rabbitmq-and-springs-rabbittemplate",{"title":20865,"description":469},"blog/implementing-at-least-once-delivery-with-rabbitmq-and-springs-rabbittemplate",[21242,21243,21244,21245,21246,1426,21247],"amqp","boot","distributed","messaging","rabbitmq","systems","Message Delivery Characteristics First some theory about delivery semantics in messaging systems. When a system wants to communicate via a message broker the developer needs a clear understanding of the delivery semantics. At first one needs to know if and how often a message will be delivered to the broker (and potential consumers): At most once - the message is delivered at most once but also not at all.","Tt3Ogxa-9Ig57HD1cc-ElR-Uard7wk79ygQUyUrtxF0",{"id":21251,"title":21252,"author":21253,"body":21254,"category":21396,"date":21397,"description":469,"extension":1034,"link":21398,"meta":21399,"navigation":916,"path":21400,"seo":21401,"slug":21258,"stem":21402,"tags":21403,"teaser":21405,"__hash__":21406},"blog/blog/code-coverage-with-significance.md","Code Coverage with significance",[97],{"type":432,"value":21255,"toc":21386},[21256,21259,21263,21266,21269,21272,21275,21278,21281,21288,21299,21303,21318,21324,21327,21331,21334,21338,21341,21345,21348,21357,21363,21366,21370,21373,21376,21380,21383],[435,21257,21252],{"id":21258},"code-coverage-with-significance",[3938,21260,21262],{"id":21261},"_839-what-does-that-even-mean","83,9% – what does that even mean?",[439,21264,21265],{},"Conversations about unit test coverage usually sound like this:",[439,21267,21268],{},"A: “What’s your coverage?”",[439,21270,21271],{},"B: “About 83,9%”",[439,21273,21274],{},"C: “Meh. Solid.”",[439,21276,21277],{},"A: “Solid? That’s incredibly high!”",[439,21279,21280],{},"D: “Ours is 40% but we have a lot of generated code so it’s still high.”",[439,21282,21283,21284,21287],{},"This shows that the perception of code coverage is highly subjective and most of the time does not have the informative\nvalue that a precise percentage indicator like “83,9%” suggests. As soon as you have a portion of code in your project,\nthat is not covered on purpose (like generated code) but is still included in the coverage analysis, you lose the\nsignificance of the coverage indicator. You’re only able to tell that ",[448,21285,21286],{},"some"," of your code is covered and maybe you can\ndeviate from the percentage that your coverage is “high” or “low”. You can tell that your coverage is going up or down,\nbut not even that is dependable as it does not take into account how much of the new code is purposefully not tested.\nHow many tests are missing? What is the risk that you take on every new release? You can not provide a satisfactory\nanswer to these questions.",[439,21289,21290,21291,21298],{},"There are divergent opinions on what “good” code coverage is, varying somewhere between 60% and 90% from my experience.\nI claim: ",[990,21292,21293,21294,21297],{},"Your unit test coverage is good when ",[448,21295,21296],{},"100% of the code you want to test"," is covered by unit tests."," And in\nmy opinion this is measurable and doable and makes the coverage indicator significant again.",[3938,21300,21302],{"id":21301},"purposefully-untested-code","Purposefully untested code",[439,21304,21305,21306,21311,21312,21317],{},"In every larger project there are some portions of code that you do not want to test with unit tests. The first step\ntowards a meaningful code coverage indicator is to identify these portions. Then coverage tools\nlike ",[1002,21307,21310],{"href":21308,"rel":21309},"https://www.eclemma.org/jacoco/",[1006],"Jacoco"," and reporting tools like ",[1002,21313,21316],{"href":21314,"rel":21315},"https://www.sonarqube.org/",[1006],"SonarQube"," can help\nyou exclude these portions from the coverage report – usually by defining exclusion patterns like in the picture below.",[439,21319,21320],{},[2205,21321],{"alt":21322,"src":21323},"Code Coverage Exclusions","https://media.synyx.de/uploads/2019/04/exclusions.png",[439,21325,21326],{},"Following are some examples of code you don’t want to test and how you can exclude it to shave off some percent of your\ncoverage’s insignificance.",[1065,21328,21330],{"id":21329},"code-of-3rd-party-libraries","Code of 3rd party libraries",[439,21332,21333],{},"Just stating the obvious. As changes on this code are not in your cognizance, neither are its tests. In a normal project\nsetup 3rd party libraries are not included in the coverage report by default.",[1065,21335,21337],{"id":21336},"test-code","Test code",[439,21339,21340],{},"Usually code, that is intended for testing purposes only, is separated from the production code which makes it easily\nexcludable. There may be some exceptions like mock objects or testing infrastructure that exist near the production\ncode, in which case you should specifically exclude them from your coverage report.",[1065,21342,21344],{"id":21343},"generated-code","Generated code",[439,21346,21347],{},"Changes in this code are done by a code generator. You should assume that the generated code is correct, when the\ngenerator works correctly. That said, if you wrote the generator yourself, you obviously still have to test the\ngenerator. Often the exclusion of generated code is easy as it is usually located in a separated package and you can\ndefine a simple exclusion pattern for it.",[439,21349,21350,21351,21356],{},"Sometimes it is a bit more complicated, for example when you use a convenience framework\nlike ",[1002,21352,21355],{"href":21353,"rel":21354},"https://projectlombok.org/",[1006],"Lombok"," in Java, that generates accessors, constructors and the like for you. It\nresults in bytecode where handwritten and generated code are present within the same class, which makes it nearly\nimpossible to only exclude the generated methods. Most of the time Lombok is used in objects like DTOs, JPA entity\nclasses etc., that are only holding some property fields and no business logic. One possible solution here is to define\nthese whole classes as “not to be tested” and make them easily identifiable with a consistent naming convention or by\nmoving them into similar packages so you can use patterns like “**/*Dto.*” or “**/dto/**” to exclude them\ncompletely.",[439,21358,21359],{},[2205,21360],{"alt":21361,"src":21362},"Code-Beispiel","https://media.synyx.de/uploads/2018/06/lombok_fail.png",[439,21364,21365],{},"A little off topic: When you don’t use Lombok and you write your getters, setters and constructors/builders yourself,\nthat does not mean you have to unit test them explicitly. When all of your business code is unit tested then all needed\ngetters, setters and constructors should be tested implicitly and show up as covered in the coverage report. If they\ndon’t then it means that either the tests for your business code are not complete or that the getters/setters are not\nactually used and you should just delete them. Unfortunately things like equals() and hashcode() are a whole different\nstory because of their high cyclomatic complexity.",[1065,21367,21369],{"id":21368},"code-better-covered-by-other-types-of-tests","Code better covered by other types of tests",[439,21371,21372],{},"A good example for code that is better tested by non-unit tests are repository classes that access your database.\nWriting unit tests for them is possible but cumbersome because they usually use a lot of framework API that has to be\nmocked. Also repository classes typically don’t contain much logic besides database access so unit tests don’t test that\nmuch. IMO it is better to write tests that actually integrate with a (more or less) real database and do without unit\ntests.",[439,21374,21375],{},"Another example are classes like Spring configurations annotated with @Configuration. They are not intended to be tested\nby unit tests. Their purpose is to construct a working application context which is better checked by good integration\ntests. This is why I usually exclude “**/*Config.*” from the unit test coverage report.",[3938,21377,21379],{"id":21378},"the-new-meaning-of-839","The new meaning of 83,9%",[439,21381,21382],{},"When you put a little effort into defining the correct exclusions for your code coverage you are rewarded with an\nactually meaningful coverage percentage. It means that when you have 83.9% code coverage you are actually missing\n16.1% of unit tests that you should write – that’s a valuable piece of information! It is also possible that you can\nactually reach 100% unit test coverage! How cool is that?",[439,21384,21385],{},"Well to be honest – realistically it is still nearly impossible to reach 100% in a larger project. There are always\nthings like private constructors to prevent instantiation or code paths that can not be reached in tests because some\nstatic framework dependencies can not be mocked and probably a dozen other reasons that prevent you from providing\nreasonable unit tests in some weird cases. But if that leads to “only” 98% test coverage at least you know that exactly\n2% of your production code has the risk to break without you noticing and you can consciously accept, assess and\ncommunicate that risk.",{"title":469,"searchDepth":507,"depth":507,"links":21387},[21388,21389,21395],{"id":21261,"depth":507,"text":21262},{"id":21301,"depth":507,"text":21302,"children":21390},[21391,21392,21393,21394],{"id":21329,"depth":547,"text":21330},{"id":21336,"depth":547,"text":21337},{"id":21343,"depth":547,"text":21344},{"id":21368,"depth":547,"text":21369},{"id":21378,"depth":507,"text":21379},[1030,1031],"2018-06-11T14:53:35","https://synyx.de/blog/code-coverage-with-significance/",{},"/blog/code-coverage-with-significance",{"title":21252,"description":469},"blog/code-coverage-with-significance",[21404],"testing","83,9% - what does that even mean? Conversations about unit test coverage usually sound like this: A: “What’s your coverage?” B: “About 83,9%” C: “Meh. Solid.” A: “Solid? That’s incredibly high!” D: “Ours is 40% but we have a lot of generated code so it’s still high.” This shows that the perception of code coverage is highly subjective and most of the time does not have the informative value that a precise percentage indicator like '","AcOyY1IA8ypzp24kWOzExiIT2xYPfFYWyxjo9riO8AE",{"id":21408,"title":21409,"author":21410,"body":21411,"category":21514,"date":21515,"description":21516,"extension":1034,"link":21517,"meta":21518,"navigation":916,"path":21519,"seo":21520,"slug":21415,"stem":21521,"tags":21522,"teaser":21523,"__hash__":21524},"blog/blog/meine-ausbildung-bei-synyx.md","Meine Ausbildung bei synyx",[280],{"type":432,"value":21412,"toc":21509},[21413,21416,21419,21422,21425,21428,21432,21435,21438,21441,21444,21447,21450,21456,21460,21463,21466,21469,21475,21478,21481,21484,21488,21491,21494,21497,21500,21506],[435,21414,21409],{"id":21415},"meine-ausbildung-bei-synyx",[439,21417,21418],{},"Ich bin nun bereits seit fast einem Jahr ausgelernt und möchte euch ein bisschen über die drei jährige Ausbildung bei\nsynyx berichten.",[439,21420,21421],{},"Als Erstausbildung habe ich 2.5 Jahre Elektriker gemacht, in dieser Zeit wurde mir klar, dass dieser Beruf nicht zu mir\npasst.",[439,21423,21424],{},"Die Softwareentwicklung brachte ich mir während der Ausbildung selber bei.",[439,21426,21427],{},"Dadurch kam ich auch zu meiner Ausbildung als Fachinformatiker für Anwendungsentwicklung bei der Firma synyx.",[3938,21429,21431],{"id":21430},"mein-erstes-lehrjahr","Mein erstes Lehrjahr",[439,21433,21434],{},"Meine Ausbildung bei der synyx fing im August 2014 an, damals noch im alten Büro ohne Klimaanlage.",[439,21436,21437],{},"Vorher hatte ich bereits Erfahrungen im Frontend Bereich (HTML, CSS, und ein wenig JavaScript) gesammelt.",[439,21439,21440],{},"Mein Ausbilder war Alex. Er brachte mir die Grundlagen von Java und Softwareengineering bei.",[439,21442,21443],{},"Durch kleine Projekte wie ein einarmiger Bandit, ein Taschenrechner, ein Tic Tac Toe spiel und eine Google Kalender\nEvent Timeline vertiefte ich die saubere strukturierte Umsetzung eines Software -Projektes.",[439,21445,21446],{},"Gegen Ende meines ersten Lehrjahres bekam ich mein erstes Projekt namens BackupControl, welches heute noch im Einsatz\nist.",[439,21448,21449],{},"Das Projekt soll den Mitarbeitern eine einfache Möglichkeit bieten ihre Backups manuell zu konfigurieren.",[439,21451,21452],{},[2205,21453],{"alt":21454,"src":21455},"BackupControl","https://media.synyx.de/uploads/2019/04/screenshot-from-2018-05-04-17-22-18-768x432.png",[3938,21457,21459],{"id":21458},"das-zweite-lehrjahr","Das zweite Lehrjahr",[439,21461,21462],{},"Im zweiten Lehrjahr wurde ich für Kundenprojekte eingearbeitet.",[439,21464,21465],{},"Bevor ich am Kundenprojekt loslegen durfte, sollte ich noch einiges über Javascript und die Arbeit in einem Team lernen.",[439,21467,21468],{},"Ziel des Übungsprojektes war es Snake als eine Web Applikation zu entwickeln.",[439,21470,21471],{},[2205,21472],{"alt":21473,"src":21474},"Snake Web Applikation","https://media.synyx.de/uploads/2019/04/Snake-Pascal-Ochs-768x432.png",[439,21476,21477],{},"Nachdem das Projekt abgeschlossen wurde, kam ich in mein erstes Team.",[439,21479,21480],{},"Durch die Projekte lernte ich viel darüber, was es bedeutet, in einem Team zu arbeiten.",[439,21482,21483],{},"Auch der Umgang mit Kunden, welcher für mich Neuland war, wurde mir Schritt für Schritt beigebracht.",[3938,21485,21487],{"id":21486},"das-dritte-lehrjahr","Das dritte Lehrjahr",[439,21489,21490],{},"Im dritten Lehrjahr kam ich in das Admin Team.",[439,21492,21493],{},"Bei synyx wird Wert darauf gelegt, dass ein Azubi auch mal die andere Seite kennengelernt, in meinem Fall das Admin\nTeam.",[439,21495,21496],{},"Dabei kam ich viel mit der technischen Infrastruktur des Unternehmens in Berührung.",[439,21498,21499],{},"Durch die Zeit bei den Admins kam auch die Idee zu meinem Abschlussprojekt namens “Synlan”. Die Idee war es eine\nSoftware zu entwickeln, welche die Suche nach einem Netzwerkgerät vereinfachen soll.",[439,21501,21502],{},[2205,21503],{"alt":21504,"src":21505},"Software zur Suche nach Netzwerkgeräten","https://media.synyx.de/uploads/2018/05/Netzwekger%C3%A4tesuche-768x432.png",[439,21507,21508],{},"Durch die gute Vorbereitung auf die Prüfung und die gute Unterstützung meiner Arbeitskollegen schloss ich im Juni meine\nAusbildung erfolgreich ab.",{"title":469,"searchDepth":507,"depth":507,"links":21510},[21511,21512,21513],{"id":21430,"depth":507,"text":21431},{"id":21458,"depth":507,"text":21459},{"id":21486,"depth":507,"text":21487},[1031],"2018-05-04T10:14:04","Ich bin nun bereits seit fast einem Jahr ausgelernt und möchte euch ein bisschen über die drei jährige Ausbildung bei\\nsynyx berichten.","https://synyx.de/blog/meine-ausbildung-bei-synyx/",{},"/blog/meine-ausbildung-bei-synyx",{"title":21409,"description":21418},"blog/meine-ausbildung-bei-synyx",[],"Ich bin nun bereits seit fast einem Jahr ausgelernt und möchte euch ein bisschen über die drei jährige Ausbildung bei synyx berichten.","lB3mCCc6SVfIKT9pkyynhwApqUEo-W5QQuaWRcWPccQ",{"id":21526,"title":21527,"author":21528,"body":21529,"category":21691,"date":21692,"description":21693,"extension":1034,"link":21694,"meta":21695,"navigation":916,"path":21696,"seo":21697,"slug":21533,"stem":21698,"tags":21699,"teaser":21703,"__hash__":21704},"blog/blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like.md","Breakout Session – how to prototype your enterprise project hackathon-like",[97],{"type":432,"value":21530,"toc":21684},[21531,21534,21537,21543,21547,21554,21557,21561,21564,21567,21570,21573,21578,21581,21584,21587,21593,21597,21600,21606,21609,21612,21615,21618,21621,21624,21630,21634,21637,21657,21663,21668,21671,21674,21678,21681],[435,21532,21527],{"id":21533},"breakout-session-how-to-prototype-your-enterprise-project-hackathon-like",[439,21535,21536],{},"This is the story of my team creating something awesome within one day. It begins in November of 2017 at “Hack your\nOffice”, a 24-hour hackathon hosted in cooperation by my employer synyx and our customer dm-drogerie markt. Although\nit was an excellent hackathon, this is not the day I am refering to but it was on this day when the idea was born.\nSeveral of my team members from dm where participating in the hackathon, even Matthäus – one of our product owners –\njoined us. He didn’t contribute anything to the code but he was absorbed in the electrifying atmosphere of everybody\nbeing excited to hack something together. The amazing thing about hackathons is that everybody has the intrinsic\nmotivation to be creative and to work hard to produce something awesome in the short time that is available, while\nhaving fun! Matthäus formed the vision to experience something similar with our own, regular development team from the\ndm office.",[439,21538,21539],{},[2205,21540],{"alt":21541,"src":21542},"Logo Hack Your Office","https://media.synyx.de/uploads/2019/04/HACKYOUROFFICE-768x1024.jpg",[3938,21544,21546],{"id":21545},"the-vision","The Vision",[439,21548,21549,21550,21553],{},"The vision was plain and simple: Create a working POC-like product that ",[448,21551,21552],{},"adds real value and runs in production","\nwithin a single day while working in a hackathon-like atmosphere.",[439,21555,21556],{},"On this day we would not work in our usual office but together in a remote location, focussing as a team on this one\ngoal alone. We called it “breakout session”. Luckily we had a suitable use case in the pipeline. The digital receipt\nsystem of dm (“e-Bon”), that was reaching its end-of-life in 2018 for several reasons, had to be reimplemented. We\ndefined the ambitious goal that within a day we should be able to walk downstairs to the real dm store with our phone,\nactually buy something and see the genuine receipt created by the check-out counter displayed in our dm customer\naccount on our phone. Everything with production systems and production data.",[3938,21558,21560],{"id":21559},"preparation-for-bonbon","Preparation for BonBon",[439,21562,21563],{},"Although it was intended as a one-day thing we didn’t want to go in blindly. Some preparation was necessary to reduce\nthe risk of failure in our one-day adventure.",[439,21565,21566],{},"First we made up a small concept about basic things like where the data had to come from, what system our product will\nbe running on, which systems would have to talk to each other. The actual business use case was worked out in more\ndetail by our product owners.",[439,21568,21569],{},"We knew we would be developing a new microservice so we needed a system to run it on. At dm there is a provisioning\nplatform in place that can pull up a fully configured virtual machine cluster on multiple stages including firewall\nrules, load balancing and everything within half an hour. However we did this beforehand because experience tells us\nthat this really cool technology doesn’t always run smoothly on first try due to its massive complexity.",[439,21571,21572],{},"The receipt data from the ~9000 checkout counters of all dm stores in europe is stored in real time on some central\nbackend system so we prepared a route piping the data in real time into a topic on one of our Apache Kafka clusters. We\nlimited the data to the last two days, which should be more than enough for our POC.",[439,21574,21575],{},[2205,21576],{"alt":18099,"src":21577},"https://media.synyx.de/uploads/2019/04/json.jpg",[439,21579,21580],{},"We assembled the team for our breakout session having cross-functionality in mind as we had to master tasks in backend\ndevelopment, data processing, operations and mobile development on that day. Our regular development team (usually in\ncharge of the customer backend of dm) formed the backbone of the breakout team. We already incorporate backend expertise\nand extensive knowledge in operations and many areas of data processing thanks to our devops culture. Unfortunately we\nare a bit thin on frontend and mobile development so we asked colleagues from the mobile team to join us. One guy from\nanalytics helped us setting up Kafka and the server operations team was (as always) on stand by in the office and\ninformed about our endeavour in case anything complicated would go wrong with the infrastructure.",[439,21582,21583],{},"Finally we needed a location to retreat to. One of my teammates arranged three rooms at the office of his employer\ndiva-e Netpioneer, which is beautifully located at the city park in Karlsruhe – quickly accessible by bike, train or\ncar for all participants. He also made reservations for lunch at the nearby pizza place – one more thing of importance\nthat we would not have to bother about on the breakout day.",[439,21585,21586],{},"The discussion about the upcoming solution’s name was held some day during lunch. We called it BonBon.",[439,21588,21589],{},[2205,21590],{"alt":21591,"src":21592},"Bonbon","https://media.synyx.de/uploads/2019/04/bonbon-768x512.jpg",[3938,21594,21596],{"id":21595},"the-session","The Session",[439,21598,21599],{},"Finally the day arrived on a Wednesday in March. Everbody was hyped about it, though some of us a bit more sceptically\nhyped. Ten developers, two product owners, two interested guests from the business department and one scrum master were\ngathering in the kitchen at diva-e, having the first of countless coffees and enjoying breakfast pretzels eager to\nabuse our keyboards for some dirty hacking. Our product owners started the session at 9:00 with a short kick-off\nintroducing the details of the use case and we quickly forged the requirements into small user stories that would\neventually add up to the desired POC.",[439,21601,21602],{},[2205,21603],{"alt":21604,"src":21605},"Whiteboard mit Post-its","https://media.synyx.de/uploads/2019/04/board_gimp2.jpg",[439,21607,21608],{},"Twenty minutes in the user stories were ready and the signal to commence the hacking was given. Suddenly the excitement\nunloaded in a stream of productivity! Small teams were vibrantly working to put the pieces together. Creating\nrepositories, initializing a Spring Boot app, testing out infrastructure all around within the first few minutes! Thanks\nto modern development tools the results came in quickly. Within one hour the first API was useable in a freshly created\nBackend Service. Shortly after the new born iPhone app was capable to authenticate users at the checkout counter via\nQR-code. Before lunch the Kafka data trickeled in at the backend. A rudimentary build and release process was in place.\nThis didn’t feel like work, it was the hackathon atmosphere transferred to our everyday activity.",[439,21610,21611],{},"The mode we were working in was mainly in small teams of 2-3 developers tackling the different tasks in pair\nprogramming. Every hour we did a quick stand up to sync our progress. Small successes were celebrated, problems\nannounced to find a fast solution using the creative power of the whole team. The product owners where always within\nreach, researching business details, answering arising questions and giving feedback on the progress. Our scrum master\nwas scurrying around the team during development enabling communication, removing impediments, moderating stand-ups,\ngiving impulses.",[439,21613,21614],{},"Unfortunately the lunch break did not work out that well. Visiting a pizza place with a group of 15 unexpectedly turned\nout to take a lot longer than anticipated. A huge chunk of our precious time and focus was lost to some delicious slices\nof Italian culinary art.",[439,21616,21617],{},"During the time between lunch and the contemplated end at 17:00 we managed to develop almost everything that was\nnecessary to complete our goal. There was one problem left with processing the huge amount of real time data and one\nconnectivity problem between the app and the prod backend. Some team members committed to two more hours of problem\nsolving which payed off big time at the end of the day!",[439,21619,21620],{},"Around 19:00 a colleague went downstairs to the local dm store and got some apple-cinnamon cereal. He went to the\ncounter and held his phone to the QR Code scanner to let the cash identify his account. After payment he opened the\nBonBon app and the receipt of the purchase was displayed with article description, product pictures, prices and\neverything. Achievement unlocked!",[439,21622,21623],{},"Unfortunately we missed the opportunity to take a picture of this moment but the receipt in the final app prototype\nlooked like this:",[439,21625,21626],{},[2205,21627],{"alt":21628,"src":21629},"App-Beispiel","https://media.synyx.de/uploads/2018/04/app.jpg",[3938,21631,21633],{"id":21632},"hacking-around-problems","Hacking around problems",[439,21635,21636],{},"To enable this quick success we had to make our hands a little dirty by cutting corners on otherwise indispensable\npractices of our everyday work. (Except security, of course. Access to the APIs and the data was secured the whole\ntime.) The whole day we were running in full-on prototype-POC mode. We agreed in advance that all produced code had to\nwork quickly and didn’t have to be beautiful or readable at all. Tests were omitted from the beginning. Copy-paste from\nother projects or Stackoverflow for quick results was encouraged. Upcoming obstacles were circumnavigated with the\nquickest workaround instead of looking for the “correct” solution:",[994,21638,21639,21642,21645,21648,21651,21654],{},[997,21640,21641],{},"Connectivity from the app to the BonBon production system was created last minute with some obscure temporary routing\nthrough the release gateway into the DMZ.",[997,21643,21644],{},"Deployment was done by copying the jar artifact to the production vm “on foot”.",[997,21646,21647],{},"The receipt data was stored in memory – without backup. We just left out the persistence layer to save time.",[997,21649,21650],{},"The huge amount of data accumulated in 2 days hit us unexpectedly. We just shrugged it off and hoped the system\nperformance would endure the load.",[997,21652,21653],{},"Debugging problems by frantically adding new logs on the new production system was common practice",[997,21655,21656],{},"… and some more",[439,21658,21659],{},[2205,21660],{"alt":21661,"src":21662},"Teamarbeit","https://media.synyx.de/uploads/2018/04/team.jpg",[439,21664,21665],{},[2205,21666],{"alt":469,"src":21667},"https://synyx.de/blog/2018-04-17-breakout-session/team.jpg",[439,21669,21670],{},"Operating this way we ended up with a working proof of concept covering everything we aimed for on this day, which was a\nhuge success! But to be clear: The thing we produced is obviously far from being actually production ready. Some parts\nof the code need refactoring to meet our quality standards. Test coverage has to be provided everywhere on all layers of\nthe test pyramid. Data has to be persisted and resilience measures have to be taken to achieve acceptable robustness of\nthe solution. A proper deployment process has to be established, maybe with containerization. Continous integration and\ncontinous delivery have to be provided. The app has to be refurbished, approved and published. And so on.",[439,21672,21673],{},"Some aspects of the electronic receipt use case were not possible to implement on the breakout day – like the original\nidea to avoid paper waste for the receipt. To stop the checkout counter from printing the receipt we would have to\nchange and deploy the software running on it, which is maintained by another team, rolled out countrywide and is not\nintegrated into a CD lifecycle – impossible to achieve in one day.",[3938,21675,21677],{"id":21676},"the-aftermath","The aftermath",[439,21679,21680],{},"Ultimately the day turned out to be exceptionally valuable both as team building excercise and as an effective method of\nstarting a project. We proved our idea to be feasable in a real production example. We have a working code base, working\nartifacts and infrastructure, that can be built upon and expanded. This will be pursued in the form of normal backlog\nitems during our usual sprint-to-sprint routine. The team gained a decent boost of motivation and team spirit – not\nonly the developers but also the product owners, who worked in support of the team and the use case the whole day, and\nour scrum master, who enabled focus and kept our spirits high with his subtle (*cough*) positive nature.",[439,21682,21683],{},"We perceived the breakout session experiment as a successful, wholesome experience and can recommend it as method for\nPOCs, project starts, feature kick-offs to every team that wants to try something new.",{"title":469,"searchDepth":507,"depth":507,"links":21685},[21686,21687,21688,21689,21690],{"id":21545,"depth":507,"text":21546},{"id":21559,"depth":507,"text":21560},{"id":21595,"depth":507,"text":21596},{"id":21632,"depth":507,"text":21633},{"id":21676,"depth":507,"text":21677},[9546,1030,1031],"2018-04-17T11:22:24","This is the story of my team creating something awesome within one day. It begins in November of 2017 at “Hack your\\nOffice”, a 24-hour hackathon hosted in cooperation by my employer synyx and our customer dm-drogerie markt. Although\\nit was an excellent hackathon, this is not the day I am refering to but it was on this day when the idea was born.\\nSeveral of my team members from dm where participating in the hackathon, even Matthäus – one of our product owners –\\njoined us. He didn’t contribute anything to the code but he was absorbed in the electrifying atmosphere of everybody\\nbeing excited to hack something together. The amazing thing about hackathons is that everybody has the intrinsic\\nmotivation to be creative and to work hard to produce something awesome in the short time that is available, while\\nhaving fun! Matthäus formed the vision to experience something similar with our own, regular development team from the\\ndm office.","https://synyx.de/blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like/",{},"/blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like",{"title":21527,"description":21536},"blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like",[9546,21700,21701,21702],"hackathon","poc","prototyping","This is the story of my team creating something awesome within one day. It begins in November of 2017 at 'Hack your Office', a 24-hour hackathon hosted in cooperation by my employer synyx and our customer dm-drogerie markt. Although it was an excellent hackathon, this is not the day I am refering to but it was on this day when the idea was born. Several of my team members from dm where participating in the hackathon, even Matthäus - one of our product owners - joined us.","9IujWA-Ig9hNhZ4uAQCIwhvcPtb6hXENLyA351ke03g",{"id":21706,"title":21707,"author":21708,"body":21709,"category":22152,"date":22153,"description":22154,"extension":1034,"link":22155,"meta":22156,"navigation":916,"path":22157,"seo":22158,"slug":21713,"stem":22160,"tags":22161,"teaser":22164,"__hash__":22165},"blog/blog/an-image-slideshow-shortcode-for-hugo.md","An Image Slideshow Shortcode For Hugo",[338],{"type":432,"value":21710,"toc":22150},[21711,21714,21723,21726,21729,21732,21737,21740,21743,21787,21790,21953,21956,21959,21962,21965,22098,22101,22107,22113,22119,22125,22131,22137,22143,22148],[435,21712,21707],{"id":21713},"an-image-slideshow-shortcode-for-hugo",[439,21715,21716,21717,21722],{},"Creating static web sites with ",[1002,21718,21721],{"href":21719,"rel":21720},"http://gohugo.io/",[1006],"Hugo"," is fun and fast but providing a convenient shortcode to\nsmoothly cross-fade an unknown number of images in a blogpost gets a bit tricky… So let’s go!",[439,21724,21725],{},"Shortcodes are Hugo template snippets which can be used inside a markdown document with optional named or unamed\nparameters.",[439,21727,21728],{},"The snippet is for example called slide.html and has to be placed in a folder called shortcodes in the layout directory\nof the Hugo site.",[439,21730,21731],{},"To slide some images we need to know the folder’s location. So the shortcode in the markdown document will look like\nthis:",[439,21733,21734],{},[471,21735,21736],{},"{{``\u003C slide \"/assets/img/\">``}}",[439,21738,21739],{},"The shortcode itself starts with reading out the parameter and 2 constants defining the fade-in time and the number of\nseconds for how long an image is visible. The localFolder may require some string manipulations to match the local\ndirectory.",[439,21741,21742],{},"Read the folder, count the number of files in it and calculate the duration of the whole css animation.",[464,21744,21746],{"className":709,"code":21745,"language":711,"meta":469,"style":469},"{{ $fadein := 2 }}\n{{ $visible := 4 }}\n{{ $param := .Get 0 }}\n{{ $localFolder := printf \"/static%s/\" $param }}\n\n{{ $files := sort (readDir $localFolder) }}\n{{ $numberOfFiles := len $files }}\n{{ $animationDuration := mul (add $fadein $visible) $numberOfFiles }}\n",[471,21747,21748,21753,21758,21763,21768,21772,21777,21782],{"__ignoreMap":469},[474,21749,21750],{"class":476,"line":477},[474,21751,21752],{},"{{ $fadein := 2 }}\n",[474,21754,21755],{"class":476,"line":507},[474,21756,21757],{},"{{ $visible := 4 }}\n",[474,21759,21760],{"class":476,"line":547},[474,21761,21762],{},"{{ $param := .Get 0 }}\n",[474,21764,21765],{"class":476,"line":584},[474,21766,21767],{},"{{ $localFolder := printf \"/static%s/\" $param }}\n",[474,21769,21770],{"class":476,"line":607},[474,21771,917],{"emptyLinePlaceholder":916},[474,21773,21774],{"class":476,"line":642},[474,21775,21776],{},"{{ $files := sort (readDir $localFolder) }}\n",[474,21778,21779],{"class":476,"line":663},[474,21780,21781],{},"{{ $numberOfFiles := len $files }}\n",[474,21783,21784],{"class":476,"line":694},[474,21785,21786],{},"{{ $animationDuration := mul (add $fadein $visible) $numberOfFiles }}\n",[439,21788,21789],{},"Now create the slider div, iterate through the files and generate an img tag for each file.",[464,21791,21793],{"className":709,"code":21792,"language":711,"meta":469,"style":469}," \u003Cstyle>\n .slider {\n padding-bottom: 70%;\n width: 100%;\n height: 0;\n position: relative;\n }\n .slider img {\n width: 100%;\n height: auto;\n position: absolute;\n opacity: 0;\n animation: slide infinite {{$animationDuration}}s;\n }\n\n {{ $x := div 100.0 $animationDuration }}\n {{ $p0 := 0 }}\n {{ $p1 := mul $x $fadein }}\n {{ $p2 := mul $x (add $fadein $visible) }}\n {{ $p3 := mul $x (add (add $fadein $visible) $fadein) }}\n\n @keyframes slide {\n {{ $p0 }}% { opacity: 0; }\n {{ $p1 }}% { opacity: 1; }\n {{ $p2 }}% { opacity: 1; }\n {{ $p3 }}% { opacity: 0; }\n }\n\n {{ range $index, $value := $files }}\n {{ $delay := mul (add $fadein $visible) $index }}\n .slider img:nth-child({{add $index 1}}){animation-delay:{{$delay}}s;}\n {{ end }}\n\u003C/style>\n",[471,21794,21795,21800,21805,21810,21815,21820,21825,21829,21834,21838,21843,21848,21853,21858,21862,21866,21871,21876,21881,21886,21891,21895,21900,21905,21910,21915,21920,21924,21928,21933,21938,21943,21948],{"__ignoreMap":469},[474,21796,21797],{"class":476,"line":477},[474,21798,21799],{}," \u003Cstyle>\n",[474,21801,21802],{"class":476,"line":507},[474,21803,21804],{}," .slider {\n",[474,21806,21807],{"class":476,"line":547},[474,21808,21809],{}," padding-bottom: 70%;\n",[474,21811,21812],{"class":476,"line":584},[474,21813,21814],{}," width: 100%;\n",[474,21816,21817],{"class":476,"line":607},[474,21818,21819],{}," height: 0;\n",[474,21821,21822],{"class":476,"line":642},[474,21823,21824],{}," position: relative;\n",[474,21826,21827],{"class":476,"line":663},[474,21828,1276],{},[474,21830,21831],{"class":476,"line":694},[474,21832,21833],{}," .slider img {\n",[474,21835,21836],{"class":476,"line":700},[474,21837,21814],{},[474,21839,21840],{"class":476,"line":913},[474,21841,21842],{}," height: auto;\n",[474,21844,21845],{"class":476,"line":920},[474,21846,21847],{}," position: absolute;\n",[474,21849,21850],{"class":476,"line":926},[474,21851,21852],{}," opacity: 0;\n",[474,21854,21855],{"class":476,"line":932},[474,21856,21857],{}," animation: slide infinite {{$animationDuration}}s;\n",[474,21859,21860],{"class":476,"line":938},[474,21861,1276],{},[474,21863,21864],{"class":476,"line":944},[474,21865,917],{"emptyLinePlaceholder":916},[474,21867,21868],{"class":476,"line":950},[474,21869,21870],{}," {{ $x := div 100.0 $animationDuration }}\n",[474,21872,21873],{"class":476,"line":956},[474,21874,21875],{}," {{ $p0 := 0 }}\n",[474,21877,21878],{"class":476,"line":962},[474,21879,21880],{}," {{ $p1 := mul $x $fadein }}\n",[474,21882,21883],{"class":476,"line":4876},[474,21884,21885],{}," {{ $p2 := mul $x (add $fadein $visible) }}\n",[474,21887,21888],{"class":476,"line":4888},[474,21889,21890],{}," {{ $p3 := mul $x (add (add $fadein $visible) $fadein) }}\n",[474,21892,21893],{"class":476,"line":4900},[474,21894,917],{"emptyLinePlaceholder":916},[474,21896,21897],{"class":476,"line":4913},[474,21898,21899],{}," @keyframes slide {\n",[474,21901,21902],{"class":476,"line":4921},[474,21903,21904],{}," {{ $p0 }}% { opacity: 0; }\n",[474,21906,21907],{"class":476,"line":4932},[474,21908,21909],{}," {{ $p1 }}% { opacity: 1; }\n",[474,21911,21912],{"class":476,"line":4938},[474,21913,21914],{}," {{ $p2 }}% { opacity: 1; }\n",[474,21916,21917],{"class":476,"line":4946},[474,21918,21919],{}," {{ $p3 }}% { opacity: 0; }\n",[474,21921,21922],{"class":476,"line":4952},[474,21923,1276],{},[474,21925,21926],{"class":476,"line":4957},[474,21927,917],{"emptyLinePlaceholder":916},[474,21929,21930],{"class":476,"line":4969},[474,21931,21932],{}," {{ range $index, $value := $files }}\n",[474,21934,21935],{"class":476,"line":4990},[474,21936,21937],{}," {{ $delay := mul (add $fadein $visible) $index }}\n",[474,21939,21940],{"class":476,"line":5001},[474,21941,21942],{}," .slider img:nth-child({{add $index 1}}){animation-delay:{{$delay}}s;}\n",[474,21944,21945],{"class":476,"line":5013},[474,21946,21947],{}," {{ end }}\n",[474,21949,21950],{"class":476,"line":5024},[474,21951,21952],{},"\u003C/style>\n",[439,21954,21955],{},"The last part is the css animation.",[439,21957,21958],{},"All images will get an opacity of 0 and an infinite animation with a keyframe rule binding.",[439,21960,21961],{},"Before we define the keyframe rule we calculate some percentages for it. If you are not familiar with these keyframe\ncalculations the code may not explain everything but at least may give you a hint on how the percentages are calculated.",[439,21963,21964],{},"The individual animation delay for every image is the last step to fade one image into the next one.",[464,21966,21968],{"className":709,"code":21967,"language":711,"meta":469,"style":469},"\n .slider {\n padding-bottom: 70%;\n width: 100%;\n height: 0;\n position: relative;\n }\n .slider img {\n width: 100%;\n height: auto;\n position: absolute;\n opacity: 0;\n animation: slide infinite {{$animationDuration}}s;\n }\n\n {{ $x := div 100.0 $animationDuration }}\n {{ $p0 := 0 }}\n {{ $p1 := mul $x $fadein }}\n {{ $p2 := mul $x (add $fadein $visible) }}\n {{ $p3 := mul $x (add (add $fadein $visible) $fadein) }}\n\n @keyframes slide {\n {{ $p0 }}% { opacity: 0; }\n {{ $p1 }}% { opacity: 1; }\n {{ $p2 }}% { opacity: 1; }\n {{ $p3 }}% { opacity: 0; }\n }\n\n {{ range $index, $value := $files }}\n {{ $delay := mul (add $fadein $visible) $index }}\n .slider img:nth-child({{add $index 1}}){animation-delay:{{$delay}}s;}\n {{ end }}\n\n",[471,21969,21970,21974,21978,21982,21986,21990,21994,21998,22002,22006,22010,22014,22018,22022,22026,22030,22034,22038,22042,22046,22050,22054,22058,22062,22066,22070,22074,22078,22082,22086,22090,22094],{"__ignoreMap":469},[474,21971,21972],{"class":476,"line":477},[474,21973,917],{"emptyLinePlaceholder":916},[474,21975,21976],{"class":476,"line":507},[474,21977,21804],{},[474,21979,21980],{"class":476,"line":547},[474,21981,21809],{},[474,21983,21984],{"class":476,"line":584},[474,21985,21814],{},[474,21987,21988],{"class":476,"line":607},[474,21989,21819],{},[474,21991,21992],{"class":476,"line":642},[474,21993,21824],{},[474,21995,21996],{"class":476,"line":663},[474,21997,1276],{},[474,21999,22000],{"class":476,"line":694},[474,22001,21833],{},[474,22003,22004],{"class":476,"line":700},[474,22005,21814],{},[474,22007,22008],{"class":476,"line":913},[474,22009,21842],{},[474,22011,22012],{"class":476,"line":920},[474,22013,21847],{},[474,22015,22016],{"class":476,"line":926},[474,22017,21852],{},[474,22019,22020],{"class":476,"line":932},[474,22021,21857],{},[474,22023,22024],{"class":476,"line":938},[474,22025,1276],{},[474,22027,22028],{"class":476,"line":944},[474,22029,917],{"emptyLinePlaceholder":916},[474,22031,22032],{"class":476,"line":950},[474,22033,21870],{},[474,22035,22036],{"class":476,"line":956},[474,22037,21875],{},[474,22039,22040],{"class":476,"line":962},[474,22041,21880],{},[474,22043,22044],{"class":476,"line":4876},[474,22045,21885],{},[474,22047,22048],{"class":476,"line":4888},[474,22049,21890],{},[474,22051,22052],{"class":476,"line":4900},[474,22053,917],{"emptyLinePlaceholder":916},[474,22055,22056],{"class":476,"line":4913},[474,22057,21899],{},[474,22059,22060],{"class":476,"line":4921},[474,22061,21904],{},[474,22063,22064],{"class":476,"line":4932},[474,22065,21909],{},[474,22067,22068],{"class":476,"line":4938},[474,22069,21914],{},[474,22071,22072],{"class":476,"line":4946},[474,22073,21919],{},[474,22075,22076],{"class":476,"line":4952},[474,22077,1276],{},[474,22079,22080],{"class":476,"line":4957},[474,22081,917],{"emptyLinePlaceholder":916},[474,22083,22084],{"class":476,"line":4969},[474,22085,21932],{},[474,22087,22088],{"class":476,"line":4990},[474,22089,21937],{},[474,22091,22092],{"class":476,"line":5001},[474,22093,21942],{},[474,22095,22096],{"class":476,"line":5013},[474,22097,21947],{},[439,22099,22100],{},"The following shortcode in action has an additional JavaScript navigation and some prefixed css properties to support a\nwider range of browsers.",[439,22102,22103],{},[2205,22104],{"alt":22105,"src":22106},"Synema Logo Leinwand","https://media.synyx.de/uploads/2019/03/01_synema-768x512.jpg",[439,22108,22109],{},[2205,22110],{"alt":22111,"src":22112},"Synema Buffet","https://media.synyx.de/uploads/2019/03/IMG_5784-768x512.jpg",[439,22114,22115],{},[2205,22116],{"alt":22117,"src":22118},"Snapschot vom Synema event","https://media.synyx.de/uploads/2019/03/IMG_5834-768x512.jpg",[439,22120,22121],{},[2205,22122],{"alt":22123,"src":22124},"Synema Kino eingang","https://media.synyx.de/uploads/2019/03/IMG_5835-768x512.jpg",[439,22126,22127],{},[2205,22128],{"alt":22129,"src":22130},"Synema Star Wars Impression","https://media.synyx.de/uploads/2019/03/IMG_5862-768x512.jpg",[439,22132,22133],{},[2205,22134],{"alt":22135,"src":22136},"Publikum beim Synema event","https://media.synyx.de/uploads/2019/03/IMG_5847-768x512.jpg",[439,22138,22139],{},[2205,22140],{"alt":22141,"src":22142},"Thomas Kraft beim Synema event","https://media.synyx.de/uploads/2019/03/IMG_5954-768x512.jpg",[439,22144,22145],{},[2205,22146],{"alt":469,"src":22147},"https://media.synyx.de/uploads/2019/03/IMG_6016-768x512.jpg",[1024,22149,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":22151},[],[1030,1031],"2018-04-12T11:45:46","Creating static web sites with Hugo is fun and fast but providing a convenient shortcode to\\nsmoothly cross-fade an unknown number of images in a blogpost gets a bit tricky… So let’s go!","https://synyx.de/blog/an-image-slideshow-shortcode-for-hugo/",{},"/blog/an-image-slideshow-shortcode-for-hugo",{"title":21707,"description":22159},"Creating static web sites with Hugo is fun and fast but providing a convenient shortcode to\nsmoothly cross-fade an unknown number of images in a blogpost gets a bit tricky… So let’s go!","blog/an-image-slideshow-shortcode-for-hugo",[22162,22163],"css","hugo","Creating static web sites with Hugo is fun and fast but providing a convenient shortcode to smoothly cross-fade an unknown number of images in a blogpost gets a bit tricky… So let’s go!","-zIyT5c6EfwcvonPd1c3lRBKIjuDg5Z-B0BHOQAqfzg",{"id":22167,"title":22168,"author":22169,"body":22170,"category":22977,"date":22978,"description":22979,"extension":1034,"link":22980,"meta":22981,"navigation":916,"path":22982,"seo":22983,"slug":22174,"stem":22984,"tags":22985,"teaser":22991,"__hash__":22992},"blog/blog/using-travis-ci-to-deploy-to-maven-repositories-and-github-releases.md","Using Travis CI to deploy to Maven repositories and GitHub Releases",[214],{"type":432,"value":22171,"toc":22966},[22172,22175,22178,22182,22195,22220,22226,22256,22281,22458,22462,22469,22523,22527,22538,22542,22570,22662,22666,22673,22720,22739,22752,22756,22762,22773,22786,22871,22875,22878,22884,22918,22925,22928,22931,22964],[435,22173,22168],{"id":22174},"using-travis-ci-to-deploy-to-maven-repositories-and-github-releases",[439,22176,22177],{},"This post outlines the steps needed to simultaneously deploy to Maven repositories and to GitHub Releases. Every time a\ntagged commit is pushed, a Travis CI build will be triggered automatically and start the release process. This blog post\nuses Sonatype Nexus as an example for a Maven repository manager.",[3938,22179,22181],{"id":22180},"preparing-github-releases","Preparing GitHub Releases",[439,22183,22184,22185,22190,22191,22194],{},"Sergey Mashkov has written a ",[1002,22186,22189],{"href":22187,"rel":22188},"https://github.com/cy6erGn0m/github-release-plugin",[1006],"Maven plugin"," that allows us to create\na new release on our project’s releases page and upload our build artifacts to a release. The following sections\ndescribe how we need to configure our ",[471,22192,22193],{},"pom.xml"," in order to use this plugin.",[439,22196,22197,22198,22201,22202,22205,22206,22209,22210,22213,22214,22219],{},"The plugin uses the ",[471,22199,22200],{},"scm"," settings to find out for which project the new release should be created. Right now, there’s\nstill a bug in the plugin which restricts the format for our git URIs. The only working format is\n",[471,22203,22204],{},"scm:git:git@github.com:...",". Neither ",[471,22207,22208],{},"scm:git:https://github.com/...","nor ",[471,22211,22212],{},"scm:git:ssh://git@github.com/...","work, but\na ",[1002,22215,22218],{"href":22216,"rel":22217},"https://github.com/cy6erGn0m/github-release-plugin/pull/2",[1006],"pull request"," has been created that adds this\nfunctionality.",[439,22221,22222,22223,22225],{},"So add an ",[471,22224,22200],{},"section to your pom that looks like this:",[464,22227,22229],{"className":6253,"code":22228,"language":6255,"meta":469,"style":469},"\u003Cscm>\n \u003Curl>https://github.com/example/project\u003C/url>\n \u003Cconnection>scm:git:git@github.com:example/project.git\u003C/connection>\n \u003CdeveloperConnection>scm:git:git@github.com:example/project.git\u003C/developerConnection>\n\u003C/scm>\n\n\n",[471,22230,22231,22236,22241,22246,22251],{"__ignoreMap":469},[474,22232,22233],{"class":476,"line":477},[474,22234,22235],{},"\u003Cscm>\n",[474,22237,22238],{"class":476,"line":507},[474,22239,22240],{}," \u003Curl>https://github.com/example/project\u003C/url>\n",[474,22242,22243],{"class":476,"line":547},[474,22244,22245],{}," \u003Cconnection>scm:git:git@github.com:example/project.git\u003C/connection>\n",[474,22247,22248],{"class":476,"line":584},[474,22249,22250],{}," \u003CdeveloperConnection>scm:git:git@github.com:example/project.git\u003C/developerConnection>\n",[474,22252,22253],{"class":476,"line":607},[474,22254,22255],{},"\u003C/scm>\n",[439,22257,22258,22259,22264,22265,22268,22269,22274,22275,22280],{},"The second step is to include the plugin in our pom. Right now the plugin is only available\nfrom ",[1002,22260,22263],{"href":22261,"rel":22262},"http://dl.bintray.com/cy6ergn0m/maven",[1006],"bintray.com"," so we need to add it as a plugin repository. We only want to\ncreate a new release on GitHub when we are building a new release. Hence we configure the plugin in an extra release\nprofile section. This leads to the plugin being executed only if Maven is started with ",[471,22266,22267],{},"-Prelease","and only if the deploy\ngoal is invoked. For more information on how to configure the plugin options please refer to\nits ",[1002,22270,22273],{"href":22271,"rel":22272},"https://github.com/cy6erGn0m/github-release-plugin#plugin-configuration-options",[1006],"documentation"," and\nthe ",[1002,22276,22279],{"href":22277,"rel":22278},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#pitfalls",[1006],"pitfalls"," below.",[464,22282,22284],{"className":6253,"code":22283,"language":6255,"meta":469,"style":469},"\u003Cprofiles>\n ...\n \u003Cprofile>\n \u003Cid>release\u003C/id>\n \u003CpluginRepositories>\n \u003CpluginRepository>\n \u003Cid>bintray-cy6ergn0m-maven\u003C/id>\n \u003Cname>bintray-plugins\u003C/name>\n \u003Curl>http://dl.bintray.com/cy6ergn0m/maven\u003C/url>\n \u003C/pluginRepository>\n \u003C/pluginRepositories>\n \u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>cy.github\u003C/groupId>\n \u003CartifactId>github-release-plugin\u003C/artifactId>\n \u003Cversion>0.5.1\u003C/version>\n \u003Cconfiguration>\n \u003CtagName>${project.version}\u003C/tagName>\n \u003CreleaseTitle>${project.artifactId}-${project.version}\u003C/releaseTitle>\n \u003CserverId>github\u003C/serverId>\n \u003C/configuration>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cgoals>\n \u003Cgoal>gh-upload\u003C/goal>\n \u003C/goals>\n \u003Cphase>deploy\u003C/phase>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003C/plugins>\n \u003C/build>\n \u003C/profile>\n\u003C/profiles>\n",[471,22285,22286,22291,22295,22300,22305,22310,22315,22320,22325,22330,22335,22340,22345,22350,22355,22360,22365,22370,22374,22379,22384,22389,22393,22398,22403,22408,22413,22418,22423,22428,22433,22438,22443,22448,22453],{"__ignoreMap":469},[474,22287,22288],{"class":476,"line":477},[474,22289,22290],{},"\u003Cprofiles>\n",[474,22292,22293],{"class":476,"line":507},[474,22294,743],{},[474,22296,22297],{"class":476,"line":547},[474,22298,22299],{}," \u003Cprofile>\n",[474,22301,22302],{"class":476,"line":584},[474,22303,22304],{}," \u003Cid>release\u003C/id>\n",[474,22306,22307],{"class":476,"line":607},[474,22308,22309],{}," \u003CpluginRepositories>\n",[474,22311,22312],{"class":476,"line":642},[474,22313,22314],{}," \u003CpluginRepository>\n",[474,22316,22317],{"class":476,"line":663},[474,22318,22319],{}," \u003Cid>bintray-cy6ergn0m-maven\u003C/id>\n",[474,22321,22322],{"class":476,"line":694},[474,22323,22324],{}," \u003Cname>bintray-plugins\u003C/name>\n",[474,22326,22327],{"class":476,"line":700},[474,22328,22329],{}," \u003Curl>http://dl.bintray.com/cy6ergn0m/maven\u003C/url>\n",[474,22331,22332],{"class":476,"line":913},[474,22333,22334],{}," \u003C/pluginRepository>\n",[474,22336,22337],{"class":476,"line":920},[474,22338,22339],{}," \u003C/pluginRepositories>\n",[474,22341,22342],{"class":476,"line":926},[474,22343,22344],{}," \u003Cbuild>\n",[474,22346,22347],{"class":476,"line":932},[474,22348,22349],{}," \u003Cplugins>\n",[474,22351,22352],{"class":476,"line":938},[474,22353,22354],{}," \u003Cplugin>\n",[474,22356,22357],{"class":476,"line":944},[474,22358,22359],{}," \u003CgroupId>cy.github\u003C/groupId>\n",[474,22361,22362],{"class":476,"line":950},[474,22363,22364],{}," \u003CartifactId>github-release-plugin\u003C/artifactId>\n",[474,22366,22367],{"class":476,"line":956},[474,22368,22369],{}," \u003Cversion>0.5.1\u003C/version>\n",[474,22371,22372],{"class":476,"line":962},[474,22373,16996],{},[474,22375,22376],{"class":476,"line":4876},[474,22377,22378],{}," \u003CtagName>${project.version}\u003C/tagName>\n",[474,22380,22381],{"class":476,"line":4888},[474,22382,22383],{}," \u003CreleaseTitle>${project.artifactId}-${project.version}\u003C/releaseTitle>\n",[474,22385,22386],{"class":476,"line":4900},[474,22387,22388],{}," \u003CserverId>github\u003C/serverId>\n",[474,22390,22391],{"class":476,"line":4913},[474,22392,17046],{},[474,22394,22395],{"class":476,"line":4921},[474,22396,22397],{}," \u003Cexecutions>\n",[474,22399,22400],{"class":476,"line":4932},[474,22401,22402],{}," \u003Cexecution>\n",[474,22404,22405],{"class":476,"line":4938},[474,22406,22407],{}," \u003Cgoals>\n",[474,22409,22410],{"class":476,"line":4946},[474,22411,22412],{}," \u003Cgoal>gh-upload\u003C/goal>\n",[474,22414,22415],{"class":476,"line":4952},[474,22416,22417],{}," \u003C/goals>\n",[474,22419,22420],{"class":476,"line":4957},[474,22421,22422],{}," \u003Cphase>deploy\u003C/phase>\n",[474,22424,22425],{"class":476,"line":4969},[474,22426,22427],{}," \u003C/execution>\n",[474,22429,22430],{"class":476,"line":4990},[474,22431,22432],{}," \u003C/executions>\n",[474,22434,22435],{"class":476,"line":5001},[474,22436,22437],{}," \u003C/plugin>\n",[474,22439,22440],{"class":476,"line":5013},[474,22441,22442],{}," \u003C/plugins>\n",[474,22444,22445],{"class":476,"line":5024},[474,22446,22447],{}," \u003C/build>\n",[474,22449,22450],{"class":476,"line":5035},[474,22451,22452],{}," \u003C/profile>\n",[474,22454,22455],{"class":476,"line":5047},[474,22456,22457],{},"\u003C/profiles>\n",[3938,22459,22461],{"id":22460},"preparing-for-maven-releases","Preparing for Maven releases",[439,22463,22464,22465,22468],{},"If you don’t already have a repository where you want to deploy to, you need to create a release and a snapshot\nrepository and add them to ",[471,22466,22467],{},"distributionManagement",".You might also want to create a separate user that has access only\nto your target repositories. This user will be used to upload the releases.",[464,22470,22472],{"className":6253,"code":22471,"language":6255,"meta":469,"style":469},"\u003CdistributionManagement>\n \u003Crepository>\n \u003Cid>oss\u003C/id>\n \u003Curl>https://nexus.example.com/content/repositories/oss-releases\u003C/url>\n \u003C/repository>\n \u003CsnapshotRepository>\n \u003Cid>oss\u003C/id>\n \u003Curl>https://nexus.example.com/content/repositories/oss-snapshots\u003C/url>\n \u003C/snapshotRepository>\n\u003C/distributionManagement>\n",[471,22473,22474,22479,22484,22489,22494,22499,22504,22508,22513,22518],{"__ignoreMap":469},[474,22475,22476],{"class":476,"line":477},[474,22477,22478],{},"\u003CdistributionManagement>\n",[474,22480,22481],{"class":476,"line":507},[474,22482,22483],{}," \u003Crepository>\n",[474,22485,22486],{"class":476,"line":547},[474,22487,22488],{}," \u003Cid>oss\u003C/id>\n",[474,22490,22491],{"class":476,"line":584},[474,22492,22493],{}," \u003Curl>https://nexus.example.com/content/repositories/oss-releases\u003C/url>\n",[474,22495,22496],{"class":476,"line":607},[474,22497,22498],{}," \u003C/repository>\n",[474,22500,22501],{"class":476,"line":642},[474,22502,22503],{}," \u003CsnapshotRepository>\n",[474,22505,22506],{"class":476,"line":663},[474,22507,22488],{},[474,22509,22510],{"class":476,"line":694},[474,22511,22512],{}," \u003Curl>https://nexus.example.com/content/repositories/oss-snapshots\u003C/url>\n",[474,22514,22515],{"class":476,"line":700},[474,22516,22517],{}," \u003C/snapshotRepository>\n",[474,22519,22520],{"class":476,"line":913},[474,22521,22522],{},"\u003C/distributionManagement>\n",[3938,22524,22526],{"id":22525},"putting-it-all-together","Putting it all together",[439,22528,22529,22530,22533,22534,22537],{},"So far we have configured the GitHub release plugin to deploy our artifacts to the GitHub Releases page and setup Maven\nreleases. Now it’s time to glue the parts together. In order to do this we have to create a ",[471,22531,22532],{},"settings.xml","for use with\nMaven, a ",[471,22535,22536],{},".travis.yml"," that manages our Travis CI builds and we have to configure some environment variables in Travis\nCI itself. Furthermore we need a small shell script that orchestrates our release.",[1065,22539,22541],{"id":22540},"maven-settings","Maven settings",[439,22543,22544,22545,22547,22548,22551,22552,22555,22556,22559,22560,22563,22564,22569],{},"Create a new ",[471,22546,22532],{},"file in your repository, e.g in a ",[471,22549,22550],{},".travis/","directory. The content of this file should look\nlike the following snippet. The ",[471,22553,22554],{},"\u003Cserver>","ids have to match the ids in ",[471,22557,22558],{},"\u003CdistributionManagement>","and the ",[471,22561,22562],{},"\u003CserverId>","of\nthe GitHub release plugin exactly. Do not use static credentials here! You don’t want everyone who stumbles upon your\nrepository on GitHub to have write access to your Nexus/Artifactory and GitHub. We will use Travis CI’s capability to\ninject environment variables into builds;\nthe ",[1002,22565,22568],{"href":22566,"rel":22567},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#configuring-travis-ci-itself",[1006],"environment variables","\nwill be configured soon.",[464,22571,22573],{"className":6253,"code":22572,"language":6255,"meta":469,"style":469},"\u003Csettings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n \u003Cservers>\n \u003Cserver>\n \u003Cid>oss\u003C/id>\n \u003Cusername>${env.NEXUS_USERNAME}\u003C/username>\n \u003Cpassword>${env.NEXUS_PASSWORD}\u003C/password>\n \u003C/server>\n \u003Cserver>\n \u003Cid>github\u003C/id>\n \u003Cusername>${env.GITHUB_USERNAME}\u003C/username>\n \u003Cpassword>${env.GITHUB_TOKEN}\u003C/password>\n \u003C/server>\n \u003C/servers>\n\n\u003C/settings>\n",[471,22574,22575,22580,22585,22590,22595,22600,22605,22610,22615,22620,22625,22629,22634,22639,22644,22648,22653,22657],{"__ignoreMap":469},[474,22576,22577],{"class":476,"line":477},[474,22578,22579],{},"\u003Csettings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n",[474,22581,22582],{"class":476,"line":507},[474,22583,22584],{}," xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[474,22586,22587],{"class":476,"line":547},[474,22588,22589],{}," xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n",[474,22591,22592],{"class":476,"line":584},[474,22593,22594],{}," http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n",[474,22596,22597],{"class":476,"line":607},[474,22598,22599],{}," \u003Cservers>\n",[474,22601,22602],{"class":476,"line":642},[474,22603,22604],{}," \u003Cserver>\n",[474,22606,22607],{"class":476,"line":663},[474,22608,22609],{}," \u003Cid>oss\u003C/id>\n",[474,22611,22612],{"class":476,"line":694},[474,22613,22614],{}," \u003Cusername>${env.NEXUS_USERNAME}\u003C/username>\n",[474,22616,22617],{"class":476,"line":700},[474,22618,22619],{}," \u003Cpassword>${env.NEXUS_PASSWORD}\u003C/password>\n",[474,22621,22622],{"class":476,"line":913},[474,22623,22624],{}," \u003C/server>\n",[474,22626,22627],{"class":476,"line":920},[474,22628,22604],{},[474,22630,22631],{"class":476,"line":926},[474,22632,22633],{}," \u003Cid>github\u003C/id>\n",[474,22635,22636],{"class":476,"line":932},[474,22637,22638],{}," \u003Cusername>${env.GITHUB_USERNAME}\u003C/username>\n",[474,22640,22641],{"class":476,"line":938},[474,22642,22643],{}," \u003Cpassword>${env.GITHUB_TOKEN}\u003C/password>\n",[474,22645,22646],{"class":476,"line":944},[474,22647,22624],{},[474,22649,22650],{"class":476,"line":950},[474,22651,22652],{}," \u003C/servers>\n",[474,22654,22655],{"class":476,"line":956},[474,22656,917],{"emptyLinePlaceholder":916},[474,22658,22659],{"class":476,"line":962},[474,22660,22661],{},"\u003C/settings>\n",[1065,22663,22665],{"id":22664},"release-script","Release script",[439,22667,22668,22669,22672],{},"We need a small shell script that orchestrates our releases. This script sets the correct version, creates a release and\nuploads it to our Maven repository and to GitHub. To release the correct version the",[471,22670,22671],{},"TRAVIS_TAG"," environment variable\nwill be used. Travis CI uses this variable to inject the value of the git tag into the build.",[464,22674,22676],{"className":6253,"code":22675,"language":6255,"meta":469,"style":469},"#!/usr/bin/env bash\n\nset -e\n\necho \"Ensuring that pom matches $TRAVIS_TAG\"\n./mvnw org.codehaus.mojo:versions-maven-plugin:2.5:set -DnewVersion=$TRAVIS_TAG\n\necho \"Uploading to oss repo and GitHub\"\n./mvnw deploy --settings .travis/settings.xml -DskipTests=true --batch-mode --update-snapshots -Prelease\n",[471,22677,22678,22683,22687,22692,22696,22701,22706,22710,22715],{"__ignoreMap":469},[474,22679,22680],{"class":476,"line":477},[474,22681,22682],{},"#!/usr/bin/env bash\n",[474,22684,22685],{"class":476,"line":507},[474,22686,917],{"emptyLinePlaceholder":916},[474,22688,22689],{"class":476,"line":547},[474,22690,22691],{},"set -e\n",[474,22693,22694],{"class":476,"line":584},[474,22695,917],{"emptyLinePlaceholder":916},[474,22697,22698],{"class":476,"line":607},[474,22699,22700],{},"echo \"Ensuring that pom matches $TRAVIS_TAG\"\n",[474,22702,22703],{"class":476,"line":642},[474,22704,22705],{},"./mvnw org.codehaus.mojo:versions-maven-plugin:2.5:set -DnewVersion=$TRAVIS_TAG\n",[474,22707,22708],{"class":476,"line":663},[474,22709,917],{"emptyLinePlaceholder":916},[474,22711,22712],{"class":476,"line":694},[474,22713,22714],{},"echo \"Uploading to oss repo and GitHub\"\n",[474,22716,22717],{"class":476,"line":700},[474,22718,22719],{},"./mvnw deploy --settings .travis/settings.xml -DskipTests=true --batch-mode --update-snapshots -Prelease\n",[439,22721,22722,22723,22726,22727,22730,22731,22734,22735,22738],{},"The script first sets the ",[471,22724,22725],{},"\u003Cversion>","in the pom exactly to our git tag’s value. So your tag always matches the version\nyou want to release, e.g. ",[471,22728,22729],{},"1.0","or ",[471,22732,22733],{},"1.5.1",". The second part creates the release. We need to reference the Maven settings\nin our repository here so that Travis CI has access rights to the Maven repositories and GitHub Releases. The important\npart here is to activate the",[471,22736,22737],{},"release","profile. This tells Maven to not only create and upload a Maven release but also to\ncreate a new GitHub Release.",[439,22740,22741,22742,22745,22746,22748,22749,11288],{},"Name this script ",[471,22743,22744],{},"release.sh",", put it inside the ",[471,22747,22550],{},"directory and make it executable (",[471,22750,22751],{},"chmod +x",[1065,22753,22755],{"id":22754},"build-configuration","Build configuration",[439,22757,22758,22759,22761],{},"Travis CI uses a file named ",[471,22760,22536],{},"at the root of a GitHub repository. The snipped contains the necessary steps.",[439,22763,22764,22765,22768,22769,22772],{},"In a normal build we just want to execute a simple ",[471,22766,22767],{},"clean verify",". The ",[471,22770,22771],{},"verify","goal will execute unit and integration\ntests. To make subsequent builds faster, we want to cache the m2 repositories during builds.",[439,22774,22775,22776,22778,22779,22782,22783,1402],{},"The most important part is the deploy section. Here we configure Travis CI to run the ",[471,22777,22744],{},"script if and only if\na tag has been pushed (",[471,22780,22781],{},"tags: true",") on the repo ",[471,22784,22785],{},"example/project",[464,22787,22789],{"className":6253,"code":22788,"language":6255,"meta":469,"style":469},"sudo: false\nlanguage: java\njdk:\n - oraclejdk8\nscript: ./mvnw clean verify\ncache:\n directories:\n - $HOME/.m2\ndeploy:\n provider: script\n script: .travis/release.sh\n skip_cleanup: true\n on:\n repo: example/project\n tags: true\n jdk: oraclejdk8\n",[471,22790,22791,22796,22801,22806,22811,22816,22821,22826,22831,22836,22841,22846,22851,22856,22861,22866],{"__ignoreMap":469},[474,22792,22793],{"class":476,"line":477},[474,22794,22795],{},"sudo: false\n",[474,22797,22798],{"class":476,"line":507},[474,22799,22800],{},"language: java\n",[474,22802,22803],{"class":476,"line":547},[474,22804,22805],{},"jdk:\n",[474,22807,22808],{"class":476,"line":584},[474,22809,22810],{}," - oraclejdk8\n",[474,22812,22813],{"class":476,"line":607},[474,22814,22815],{},"script: ./mvnw clean verify\n",[474,22817,22818],{"class":476,"line":642},[474,22819,22820],{},"cache:\n",[474,22822,22823],{"class":476,"line":663},[474,22824,22825],{}," directories:\n",[474,22827,22828],{"class":476,"line":694},[474,22829,22830],{}," - $HOME/.m2\n",[474,22832,22833],{"class":476,"line":700},[474,22834,22835],{},"deploy:\n",[474,22837,22838],{"class":476,"line":913},[474,22839,22840],{}," provider: script\n",[474,22842,22843],{"class":476,"line":920},[474,22844,22845],{}," script: .travis/release.sh\n",[474,22847,22848],{"class":476,"line":926},[474,22849,22850],{}," skip_cleanup: true\n",[474,22852,22853],{"class":476,"line":932},[474,22854,22855],{}," on:\n",[474,22857,22858],{"class":476,"line":938},[474,22859,22860],{}," repo: example/project\n",[474,22862,22863],{"class":476,"line":944},[474,22864,22865],{}," tags: true\n",[474,22867,22868],{"class":476,"line":950},[474,22869,22870],{}," jdk: oraclejdk8\n",[1065,22872,22874],{"id":22873},"configuring-travis-ci-itself","Configuring Travis CI itself",[439,22876,22877],{},"Now we need to teach Travis CI the values of the environment variables used in our Maven settings. To do this navigate\nto the Travis CI settings for your project:",[439,22879,22880],{},[2205,22881],{"alt":22882,"src":22883},"travis environment","https://media.synyx.de/uploads/2018/01/travis-environment-768x282.png",[439,22885,22886,22887,22890,22891,22894,22895,22900,22901,22890,22904,22907,22908,22911,22912,22917],{},"Add ",[471,22888,22889],{},"NEXUS_USER","and ",[471,22892,22893],{},"NEXUS_PASSWORD","for your\nnewly ",[1002,22896,22899],{"href":22897,"rel":22898},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#preparing-maven-releases",[1006],"created user",". You\nalso need to configure ",[471,22902,22903],{},"GITHUB_USERNAME",[471,22905,22906],{},"GITHUB_TOKEN",". But even though the field is called password, what your\nreally want to configure here is your ",[448,22909,22910],{},"GitHub API token",". Otherwise, the GitHub release plugin will not be able to\nupload artifacts. You can obtain your personal access token ",[1002,22913,22916],{"href":22914,"rel":22915},"https://github.com/settings/tokens",[1006],"here",". The token needs\naccess to the repo scope.",[439,22919,22920,22921,22924],{},"And that’s it. Now you can simply create a new tag in your repository (",[471,22922,22923],{},"git tag -a 1.1 -m \"Release 1.1\"","), push it to\nGitHub and Travis CI will trigger the release process. Happy releasing!",[3938,22926,22927],{"id":22279},"Pitfalls",[439,22929,22930],{},"Some advice so that you do not encounter the same problems we did:",[994,22932,22933,22942,22954,22957],{},[997,22934,22935,22936,22938,22939],{},"Do not forget to make ",[471,22937,22744],{},"executable otherwise the Travis CI build will fail with a rather unhelpful message:\n",[471,22940,22941],{},"Script failed with status 127.",[997,22943,22944,22945,22948,22949,22951,22952,1402],{},"The ",[471,22946,22947],{},"serverId","in the GitHub release plugin’s configuration refers to a ",[471,22950,22554],{},"entry in Maven’s ",[471,22953,22532],{},[997,22955,22956],{},"Do not be tempted to use your GitHub account password to configure the server settings for the GitHub release plugin.\nEven though the field is called password it’s really an API token that is needed here.",[997,22958,22959,22960,22963],{},"Do not change the value of ",[471,22961,22962],{},"\u003CtagName>","when configuring the GitHub release plugin. Using anything different from the\nproject’s version (i.e. the git tag’s value), will lead to recursive release builds. This happens because the release\nplugin will create and push a new tag if it does not already exist. Pushing a tag invokes another Travis CI build\nwhich will create a new tag and so on.",[1024,22965,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":22967},[22968,22969,22970,22976],{"id":22180,"depth":507,"text":22181},{"id":22460,"depth":507,"text":22461},{"id":22525,"depth":507,"text":22526,"children":22971},[22972,22973,22974,22975],{"id":22540,"depth":547,"text":22541},{"id":22664,"depth":547,"text":22665},{"id":22754,"depth":547,"text":22755},{"id":22873,"depth":547,"text":22874},{"id":22279,"depth":507,"text":22927},[1030,1031],"2018-01-24T12:05:58","This post outlines the steps needed to simultaneously deploy to Maven repositories and to GitHub Releases. Every time a\\ntagged commit is pushed, a Travis CI build will be triggered automatically and start the release process. This blog post\\nuses Sonatype Nexus as an example for a Maven repository manager.","https://synyx.de/blog/using-travis-ci-to-deploy-to-maven-repositories-and-github-releases/",{},"/blog/using-travis-ci-to-deploy-to-maven-repositories-and-github-releases",{"title":22168,"description":22177},"blog/using-travis-ci-to-deploy-to-maven-repositories-and-github-releases",[22986,22987,711,22988,22989,22990],"artifactory","github","maven","nexus","travisci","This post outlines the steps needed to simultaneously deploy to Maven repositories and to GitHub Releases. Every time a tagged commit is pushed, a Travis CI build will be triggered automatically and start the release process. This blog post uses Sonatype Nexus as an example for a Maven repository manager. Preparing GitHub Releases Sergey Mashkov has written a Maven plugin that allows us to create a new release on our project’s releases page and upload our build artifacts to a release.","2TPRgpLddHBxZy1b9W1YaqOnatHf0HzZXEjowW2rwB4",{"id":22994,"title":22995,"author":22996,"body":22997,"category":24358,"date":24359,"description":24360,"extension":1034,"link":24361,"meta":24362,"navigation":916,"path":24363,"seo":24364,"slug":23001,"stem":24365,"tags":24366,"teaser":24367,"__hash__":24368},"blog/blog/implementing-a-waiting-component-with-user-experience-in-mind.md","Implementing a waiting component with user experience in mind",[335],{"type":432,"value":22998,"toc":24353},[22999,23002,23005,23011,23029,23054,23062,23066,23069,23140,23154,23318,23333,23339,23343,23346,23352,23357,23360,23382,23585,23588,23613,23685,23700,23756,23774,23860,23888,23892,23895,23934,23937,23962,23972,24027,24036,24042,24050,24072,24173,24185,24350],[435,23000,22995],{"id":23001},"implementing-a-waiting-component-with-user-experience-in-mind",[439,23003,23004],{},"Giving fast feedback to users has been improved by single page applications over the request response cycle. However,\nthere is one serious downside with this approach. Elements are popping out of the wild on various sections everytime.\nParticular data loading indicated by a waiting animation is affected with this phenomenon. In this blog I’d like to\npresent you our solution of a UI component that takes care about delaying the rendering of the animation.",[439,23006,23007,23010],{},[448,23008,23009],{},"Disclaimer:"," we’re using React in our frontend (without server side rendering). In case you don’t know React: React\nprovides lifecycle hooks for UI components like",[994,23012,23013,23018,23023],{},[997,23014,23015],{},[471,23016,23017],{},"render",[997,23019,23020],{},[471,23021,23022],{},"willUpdate",[997,23024,23025,23026],{},"or .",[471,23027,23028],{},"didUpdate",[439,23030,23031,23032,23035,23036,23039,23040,23042,23043,21201,23045,23047,23048,23053],{},"These hooks can be used to do internal stuff your component requires to be rendered correctly. React components can\neither be updated with changing ",[471,23033,23034],{},"properties","or updating ",[471,23037,23038],{},"state",". Properties are actually the public API of the\ncomponent. The",[471,23041,23038],{},", however, is the antagonist which can only be updated by the component itself. Changing\n",[471,23044,23034],{},[471,23046,23038],{}," triggers specific lifecycle hooks and finally a rerendering of the component. Don’t hesitate to\nread the ",[1002,23049,23052],{"href":23050,"rel":23051},"https://reactjs.org/docs/rendering-elements.html",[1006],"react docs"," for more detail.",[439,23055,23056,23057,1402],{},"tl;dr source code is available ",[1002,23058,23061],{"href":23059,"rel":23060},"https://github.com/bseber/waiting",[1006],"on github",[3938,23063,23065],{"id":23064},"loading-or-not-loading","loading or not loading",[439,23067,23068],{},"At first we have to satisfy the basic need. The user must get feedback whether we’re loading data currently or not. The\nmost simple component takes a boolean property that reflects the current state.",[464,23070,23072],{"className":15199,"code":23071,"language":15201,"meta":469,"style":469},"class Waiting extends React.Component {\n render() {\n return this.props.loading ? \u003Cdiv>loading...\u003C/div> : null;\n }\n}\n",[471,23073,23074,23093,23100,23132,23136],{"__ignoreMap":469},[474,23075,23076,23078,23081,23083,23086,23088,23091],{"class":476,"line":477},[474,23077,15208],{"class":810},[474,23079,23080],{"class":480}," Waiting",[474,23082,15214],{"class":810},[474,23084,23085],{"class":480}," React",[474,23087,1402],{"class":503},[474,23089,23090],{"class":480},"Component",[474,23092,14027],{"class":503},[474,23094,23095,23098],{"class":476,"line":507},[474,23096,23097],{"class":480}," render",[474,23099,15227],{"class":503},[474,23101,23102,23105,23107,23110,23113,23116,23118,23121,23123,23125,23127,23130],{"class":476,"line":547},[474,23103,23104],{"class":810}," return",[474,23106,2596],{"class":510},[474,23108,23109],{"class":503},".props.loading ",[474,23111,23112],{"class":810},"?",[474,23114,23115],{"class":503}," \u003C",[474,23117,15027],{"class":15051},[474,23119,23120],{"class":503},">loading...\u003C/",[474,23122,15027],{"class":15051},[474,23124,9010],{"class":503},[474,23126,6562],{"class":810},[474,23128,23129],{"class":510}," null",[474,23131,2139],{"class":503},[474,23133,23134],{"class":476,"line":584},[474,23135,15319],{"class":503},[474,23137,23138],{"class":476,"line":607},[474,23139,703],{"class":503},[439,23141,23142,23143,23146,23147,23149,23150,23153],{},"This component can now be used in our App. The loading info is visible as long as the",[471,23144,23145],{},"loading"," flag is set to ",[471,23148,829],{}," and\nhidden as soon as the flag is toggled.",[471,23151,23152],{},"MyDataView"," is just another component that takes care about rendering the data.",[464,23155,23157],{"className":15199,"code":23156,"language":15201,"meta":469,"style":469},"class MyApp extends React.Component {\n // initialState\n // no data existent and we're loading currently\n state = {\n data: null,\n loading: true\n };\n\n renderData() {\n return this.state.data ? : null;\n }\n\n render() {\n return (\n \u003Cdiv>\n\n {this.renderData()}\n \u003C/div>\n );\n }\n}\n",[471,23158,23159,23176,23181,23186,23195,23205,23213,23218,23222,23229,23247,23251,23255,23261,23268,23277,23281,23296,23305,23310,23314],{"__ignoreMap":469},[474,23160,23161,23163,23166,23168,23170,23172,23174],{"class":476,"line":477},[474,23162,15208],{"class":810},[474,23164,23165],{"class":480}," MyApp",[474,23167,15214],{"class":810},[474,23169,23085],{"class":480},[474,23171,1402],{"class":503},[474,23173,23090],{"class":480},[474,23175,14027],{"class":503},[474,23177,23178],{"class":476,"line":507},[474,23179,23180],{"class":2277}," // initialState\n",[474,23182,23183],{"class":476,"line":547},[474,23184,23185],{"class":2277}," // no data existent and we're loading currently\n",[474,23187,23188,23191,23193],{"class":476,"line":584},[474,23189,23190],{"class":14037}," state",[474,23192,1661],{"class":810},[474,23194,14027],{"class":503},[474,23196,23197,23200,23203],{"class":476,"line":607},[474,23198,23199],{"class":503}," data: ",[474,23201,23202],{"class":510},"null",[474,23204,4715],{"class":503},[474,23206,23207,23210],{"class":476,"line":642},[474,23208,23209],{"class":503}," loading: ",[474,23211,23212],{"class":510},"true\n",[474,23214,23215],{"class":476,"line":663},[474,23216,23217],{"class":503}," };\n",[474,23219,23220],{"class":476,"line":694},[474,23221,917],{"emptyLinePlaceholder":916},[474,23223,23224,23227],{"class":476,"line":700},[474,23225,23226],{"class":480}," renderData",[474,23228,15227],{"class":503},[474,23230,23231,23233,23235,23238,23240,23243,23245],{"class":476,"line":913},[474,23232,23104],{"class":810},[474,23234,2596],{"class":510},[474,23236,23237],{"class":503},".state.data ",[474,23239,23112],{"class":810},[474,23241,23242],{"class":810}," :",[474,23244,23129],{"class":510},[474,23246,2139],{"class":503},[474,23248,23249],{"class":476,"line":920},[474,23250,15319],{"class":503},[474,23252,23253],{"class":476,"line":926},[474,23254,917],{"emptyLinePlaceholder":916},[474,23256,23257,23259],{"class":476,"line":932},[474,23258,23097],{"class":480},[474,23260,15227],{"class":503},[474,23262,23263,23265],{"class":476,"line":938},[474,23264,23104],{"class":810},[474,23266,23267],{"class":503}," (\n",[474,23269,23270,23273,23275],{"class":476,"line":944},[474,23271,23272],{"class":503}," \u003C",[474,23274,15027],{"class":15051},[474,23276,15070],{"class":503},[474,23278,23279],{"class":476,"line":950},[474,23280,917],{"emptyLinePlaceholder":916},[474,23282,23283,23286,23288,23290,23293],{"class":476,"line":956},[474,23284,23285],{"class":503}," {",[474,23287,8394],{"class":510},[474,23289,1402],{"class":503},[474,23291,23292],{"class":480},"renderData",[474,23294,23295],{"class":503},"()}\n",[474,23297,23298,23301,23303],{"class":476,"line":962},[474,23299,23300],{"class":503}," \u003C/",[474,23302,15027],{"class":15051},[474,23304,15070],{"class":503},[474,23306,23307],{"class":476,"line":4876},[474,23308,23309],{"class":503}," );\n",[474,23311,23312],{"class":476,"line":4888},[474,23313,15319],{"class":503},[474,23315,23316],{"class":476,"line":4900},[474,23317,703],{"class":503},[439,23319,23320,23321,23326,23327,23329,23330,23332],{},"One benefit of this solution is that we now have a reusable component. We don’t have to care about the visualisation\nstuff anymore at every place. It could render the div element with a static text or it could render some more advanced\ncss animation. For instance we could change the loading animation to use this\nawesome ",[1002,23322,23325],{"href":23323,"rel":23324},"https://codepen.io/dissimulate/pen/vlfyA",[1006],"codepen"," with refactoring the",[471,23328,13362],{}," component implementation only.\nConsumers of the ",[471,23331,13362],{}," component wouldn’t have to be touched.",[439,23334,23335,23336,23338],{},"A second benefit is the really simple implementation of the ",[471,23337,13362],{}," component. Even without knowing React or\nJavaScript in detail you quickly see that a div or nothing is rendered.",[3938,23340,23342],{"id":23341},"pretend-not-loading-when-its-fast","pretend not loading when it’s fast",[439,23344,23345],{},"The next step is user experience improvement. We don’t want to render the loading text",[439,23347,23348,23349,23351],{},"when the ",[471,23350,23145],{}," flag is toggled back to false within 100ms.",[11947,23353,23354],{},[439,23355,23356],{},"0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no\nspecial feedback is necessary except to display the result.",[439,23358,23359],{},"Jakob Nielsen",[439,23361,23362,23363,23365,23366,23369,23370,23373,23374,23377,23378,23381],{},"To keep changes small let us first map the loading property value to the internal component state. React takes care\nabout calling ",[471,23364,23017],{}," when either new properties are given or state is changed with ",[471,23367,23368],{},"setState",". So in the constructor\nwe’re mapping the original loading flag to render the initially intended state. Let’s say the ",[990,23371,23372],{},"yep, we’re currently\nloading"," state. Soon afterwards the property will eventually swap to ",[990,23375,23376],{},"nop, we’re finished loading",". This can be\nintercepted by the ",[471,23379,23380],{},"componentWillReceiveProps"," lifecycle hook . Just like in the constructor we’re mapping the property\nto the internal state.",[464,23383,23385],{"className":15199,"code":23384,"language":15201,"meta":469,"style":469},"class Waiting extends React.Component {\n+ constructor(props) {\n+ super();\n+ this.state = { loading: props.loading };\n+ }\n+\n+ componentWillReceiveProps(nextProps) {\n+ if (nextProps.loading !== this.props.loading) {\n+ this.setState({ loading: nextProps.loading });\n+ }\n+ }\n+\n render() {\n- return this.props.loading ? \u003Cdiv>loading...\u003C/div> : null;\n+ return this.state.loading ? \u003Cdiv>loading...\u003C/div> : null;\n }\n}\n",[471,23386,23387,23403,23419,23428,23442,23448,23453,23467,23484,23498,23504,23510,23514,23520,23548,23577,23581],{"__ignoreMap":469},[474,23388,23389,23391,23393,23395,23397,23399,23401],{"class":476,"line":477},[474,23390,15208],{"class":810},[474,23392,23080],{"class":480},[474,23394,15214],{"class":810},[474,23396,23085],{"class":480},[474,23398,1402],{"class":503},[474,23400,23090],{"class":480},[474,23402,14027],{"class":503},[474,23404,23405,23408,23411,23413,23416],{"class":476,"line":507},[474,23406,23407],{"class":810},"+",[474,23409,23410],{"class":810}," constructor",[474,23412,1483],{"class":503},[474,23414,23415],{"class":14037},"props",[474,23417,23418],{"class":503},") {\n",[474,23420,23421,23423,23426],{"class":476,"line":547},[474,23422,23407],{"class":810},[474,23424,23425],{"class":510}," super",[474,23427,15271],{"class":503},[474,23429,23430,23432,23434,23437,23439],{"class":476,"line":584},[474,23431,23407],{"class":810},[474,23433,15232],{"class":510},[474,23435,23436],{"class":503},".state ",[474,23438,811],{"class":810},[474,23440,23441],{"class":503}," { loading: props.loading };\n",[474,23443,23444,23446],{"class":476,"line":607},[474,23445,23407],{"class":810},[474,23447,15319],{"class":503},[474,23449,23450],{"class":476,"line":642},[474,23451,23452],{"class":810},"+\n",[474,23454,23455,23457,23460,23462,23465],{"class":476,"line":663},[474,23456,23407],{"class":810},[474,23458,23459],{"class":480}," componentWillReceiveProps",[474,23461,1483],{"class":503},[474,23463,23464],{"class":14037},"nextProps",[474,23466,23418],{"class":503},[474,23468,23469,23471,23473,23476,23479,23481],{"class":476,"line":694},[474,23470,23407],{"class":810},[474,23472,14333],{"class":810},[474,23474,23475],{"class":503}," (nextProps.loading ",[474,23477,23478],{"class":810},"!==",[474,23480,2596],{"class":510},[474,23482,23483],{"class":503},".props.loading) {\n",[474,23485,23486,23488,23491,23493,23495],{"class":476,"line":700},[474,23487,23407],{"class":810},[474,23489,23490],{"class":510}," this",[474,23492,1402],{"class":503},[474,23494,23368],{"class":480},[474,23496,23497],{"class":503},"({ loading: nextProps.loading });\n",[474,23499,23500,23502],{"class":476,"line":913},[474,23501,23407],{"class":810},[474,23503,1276],{"class":503},[474,23505,23506,23508],{"class":476,"line":920},[474,23507,23407],{"class":810},[474,23509,15319],{"class":503},[474,23511,23512],{"class":476,"line":926},[474,23513,23452],{"class":810},[474,23515,23516,23518],{"class":476,"line":932},[474,23517,23097],{"class":480},[474,23519,15227],{"class":503},[474,23521,23522,23524,23526,23528,23530,23532,23534,23536,23538,23540,23542,23544,23546],{"class":476,"line":938},[474,23523,10143],{"class":810},[474,23525,23104],{"class":810},[474,23527,2596],{"class":510},[474,23529,23109],{"class":503},[474,23531,23112],{"class":810},[474,23533,23115],{"class":503},[474,23535,15027],{"class":15051},[474,23537,23120],{"class":503},[474,23539,15027],{"class":15051},[474,23541,9010],{"class":503},[474,23543,6562],{"class":810},[474,23545,23129],{"class":510},[474,23547,2139],{"class":503},[474,23549,23550,23552,23554,23556,23559,23561,23563,23565,23567,23569,23571,23573,23575],{"class":476,"line":944},[474,23551,23407],{"class":810},[474,23553,23104],{"class":810},[474,23555,2596],{"class":510},[474,23557,23558],{"class":503},".state.loading ",[474,23560,23112],{"class":810},[474,23562,23115],{"class":503},[474,23564,15027],{"class":15051},[474,23566,23120],{"class":503},[474,23568,15027],{"class":15051},[474,23570,9010],{"class":503},[474,23572,6562],{"class":810},[474,23574,23129],{"class":510},[474,23576,2139],{"class":503},[474,23578,23579],{"class":476,"line":950},[474,23580,15319],{"class":503},[474,23582,23583],{"class":476,"line":956},[474,23584,703],{"class":503},[439,23586,23587],{},"So far we’ve gained nothing but complexity /o\\",[439,23589,23590,23591,23593,23594,23596,23597,23599,23600,23603,23604,23606,23607,23609,23610,1402],{},"Now to the interesting part. As soon as the",[471,23592,13362],{}," component receives new properties we’re starting a timeout to\nupdate the internal state with a delay of 100ms. Remember react calls",[471,23595,23017],{},"on property changes as well as on state\nchanges. So",[471,23598,23017],{}," is called two times now actually. The first time it renders the same as previously ",[990,23601,23602],{},"nop, we’re not\nloading",". After 100ms",[471,23605,23368],{}," is called which triggers the second ",[471,23608,23017],{},"cycle ",[990,23611,23612],{},"yep, we’re loading",[464,23614,23616],{"className":709,"code":23615,"language":711,"meta":469,"style":469},"class Waiting extends React.Component {\n constructor() { ... }\n\n componentWillReceiveProps(nextProps) {\n if (nextProps.loading !== this.props.loading) {\n+ window.clearTimeout(this._loadingTimeout);\n+ this._loadingTimeout = window.setTimeout(() => {\n this.setState({ loading: nextProps.loading });\n+ }, 100);\n }\n\n render() { ... }\n }\n}\n",[471,23617,23618,23623,23628,23632,23637,23642,23647,23652,23657,23662,23667,23671,23676,23681],{"__ignoreMap":469},[474,23619,23620],{"class":476,"line":477},[474,23621,23622],{},"class Waiting extends React.Component {\n",[474,23624,23625],{"class":476,"line":507},[474,23626,23627],{}," constructor() { ... }\n",[474,23629,23630],{"class":476,"line":547},[474,23631,917],{"emptyLinePlaceholder":916},[474,23633,23634],{"class":476,"line":584},[474,23635,23636],{}," componentWillReceiveProps(nextProps) {\n",[474,23638,23639],{"class":476,"line":607},[474,23640,23641],{}," if (nextProps.loading !== this.props.loading) {\n",[474,23643,23644],{"class":476,"line":642},[474,23645,23646],{},"+ window.clearTimeout(this._loadingTimeout);\n",[474,23648,23649],{"class":476,"line":663},[474,23650,23651],{},"+ this._loadingTimeout = window.setTimeout(() => {\n",[474,23653,23654],{"class":476,"line":694},[474,23655,23656],{}," this.setState({ loading: nextProps.loading });\n",[474,23658,23659],{"class":476,"line":700},[474,23660,23661],{},"+ }, 100);\n",[474,23663,23664],{"class":476,"line":913},[474,23665,23666],{}," }\n",[474,23668,23669],{"class":476,"line":920},[474,23670,917],{"emptyLinePlaceholder":916},[474,23672,23673],{"class":476,"line":926},[474,23674,23675],{}," render() { ... }\n",[474,23677,23678],{"class":476,"line":932},[474,23679,23680],{}," }\n",[474,23682,23683],{"class":476,"line":938},[474,23684,703],{},[439,23686,23687,23688,23691,23692,23695,23696,23699],{},"But wait, what’s happening now when the loading property is swapped the other way around from ",[990,23689,23690],{},"yep"," to ",[990,23693,23694],{},"nop","? Remember\nthe implementation of ",[471,23697,23698],{},"MyApp"," from above?",[464,23701,23703],{"className":709,"code":23702,"language":711,"meta":469,"style":469},"class MyApp extends React.Component {\n // ...\n render() {\n return (\n \u003Cdiv>\n\n {this.renderData()}\n \u003C/div>\n );\n }\n}\n",[471,23704,23705,23710,23715,23720,23725,23730,23734,23739,23744,23748,23752],{"__ignoreMap":469},[474,23706,23707],{"class":476,"line":477},[474,23708,23709],{},"class MyApp extends React.Component {\n",[474,23711,23712],{"class":476,"line":507},[474,23713,23714],{}," // ...\n",[474,23716,23717],{"class":476,"line":547},[474,23718,23719],{}," render() {\n",[474,23721,23722],{"class":476,"line":584},[474,23723,23724],{}," return (\n",[474,23726,23727],{"class":476,"line":607},[474,23728,23729],{}," \u003Cdiv>\n",[474,23731,23732],{"class":476,"line":642},[474,23733,917],{"emptyLinePlaceholder":916},[474,23735,23736],{"class":476,"line":663},[474,23737,23738],{}," {this.renderData()}\n",[474,23740,23741],{"class":476,"line":694},[474,23742,23743],{}," \u003C/div>\n",[474,23745,23746],{"class":476,"line":700},[474,23747,23309],{},[474,23749,23750],{"class":476,"line":913},[474,23751,15319],{},[474,23753,23754],{"class":476,"line":920},[474,23755,703],{},[439,23757,23758,23759,23761,23762,23764,23765,23768,23769,23771,23772,1402],{},"The",[471,23760,13362],{}," component receives the updated loading flag ",[990,23763,4768],{}," and delays it’s internal rendering while\n",[471,23766,23767],{},"this.renderData()"," renders the actual data. So the loading info is shortly visible amongst the data. Fortunately this\ncan be fixed easily. We just have to update immediately when the ",[471,23770,23145],{}," property is set to ",[990,23773,4768],{},[464,23775,23777],{"className":709,"code":23776,"language":711,"meta":469,"style":469},"class Waiting extends React.Component {\n constructor() { ... }\n\n componentWillReceiveProps(nextProps) {\n if (nextProps.loading !== this.props.loading) {\n window.clearTimeout(this._loadingTimeout);\n+ if (nextProps.loading) {\n this._loadingTimeout = window.setTimeout(() => {\n this.setState({ loading: nextProps.loading });\n }, 100);\n+ } else {\n+ this.setState({ loading: false });\n+ }\n }\n }\n\n render() { ... }\n}\n",[471,23778,23779,23783,23787,23791,23795,23799,23804,23809,23814,23819,23824,23829,23834,23839,23843,23847,23851,23856],{"__ignoreMap":469},[474,23780,23781],{"class":476,"line":477},[474,23782,23622],{},[474,23784,23785],{"class":476,"line":507},[474,23786,23627],{},[474,23788,23789],{"class":476,"line":547},[474,23790,917],{"emptyLinePlaceholder":916},[474,23792,23793],{"class":476,"line":584},[474,23794,23636],{},[474,23796,23797],{"class":476,"line":607},[474,23798,23641],{},[474,23800,23801],{"class":476,"line":642},[474,23802,23803],{}," window.clearTimeout(this._loadingTimeout);\n",[474,23805,23806],{"class":476,"line":663},[474,23807,23808],{},"+ if (nextProps.loading) {\n",[474,23810,23811],{"class":476,"line":694},[474,23812,23813],{}," this._loadingTimeout = window.setTimeout(() => {\n",[474,23815,23816],{"class":476,"line":700},[474,23817,23818],{}," this.setState({ loading: nextProps.loading });\n",[474,23820,23821],{"class":476,"line":913},[474,23822,23823],{}," }, 100);\n",[474,23825,23826],{"class":476,"line":920},[474,23827,23828],{},"+ } else {\n",[474,23830,23831],{"class":476,"line":926},[474,23832,23833],{},"+ this.setState({ loading: false });\n",[474,23835,23836],{"class":476,"line":932},[474,23837,23838],{},"+ }\n",[474,23840,23841],{"class":476,"line":938},[474,23842,1276],{},[474,23844,23845],{"class":476,"line":944},[474,23846,15319],{},[474,23848,23849],{"class":476,"line":950},[474,23850,917],{"emptyLinePlaceholder":916},[474,23852,23853],{"class":476,"line":956},[474,23854,23855],{}," render() { ... }\n",[474,23857,23858],{"class":476,"line":962},[474,23859,703],{},[439,23861,23862,23863,23866,23867,23869,23870,23872,23873,23875,23876,23881,23882,18175,23885,17548],{},"Now we’ve gained a good user experience by not displaying the loading info if the loading property is toggled from ",[990,23864,23865],{},"yay","\nback to ",[990,23868,23694],{}," within 100ms. There is no flickering anymore \\o/ However, we’ve payed with some complexity in the\n",[471,23871,13362],{}," component and even have async stuff happening there. So testing consumers of the ",[471,23874,13362],{}," component could be\nconfusing. But in my opinion the better user experience is worth the complexity and tests should be fine as long\nas ",[1002,23877,23880],{"href":23878,"rel":23879},"https://reactjs.org/docs/shallow-renderer.html",[1006],"shallowRendering"," is used. Otherwise we have to use the timemachine\nfeature of the testing library (e.g. jest provides ",[471,23883,23884],{},"jest.useFakeTimers()",[471,23886,23887],{},"jest.runTimersToTime(100)",[3938,23889,23891],{"id":23890},"improved-handling-of-data-rendering","improved handling of data rendering",[439,23893,23894],{},"Currently we have a waiting component that takes care about delaying the loading info. But the consumer is still\nresponsible to check itself whether the data is available and should be rendered or not.",[464,23896,23898],{"className":15199,"code":23897,"language":15201,"meta":469,"style":469},"renderData() {\n return this.state.data\n ?\n : null;\n}\n",[471,23899,23900,23906,23916,23921,23930],{"__ignoreMap":469},[474,23901,23902,23904],{"class":476,"line":477},[474,23903,23292],{"class":480},[474,23905,15227],{"class":503},[474,23907,23908,23911,23913],{"class":476,"line":507},[474,23909,23910],{"class":810}," return",[474,23912,2596],{"class":510},[474,23914,23915],{"class":503},".state.data\n",[474,23917,23918],{"class":476,"line":547},[474,23919,23920],{"class":810}," ?\n",[474,23922,23923,23926,23928],{"class":476,"line":584},[474,23924,23925],{"class":810}," :",[474,23927,23129],{"class":510},[474,23929,2139],{"class":503},[474,23931,23932],{"class":476,"line":607},[474,23933,703],{"class":503},[439,23935,23936],{},"However, my collegues and my humble self could live with this redundancy actually. It is explicit and the waiting\ncomponent wouldn’t be bloated with more features and complexity. But in our project we had the following issue (amongst\nsome others…)",[439,23938,23939,23940,23942,23943,23946,23947,23950,23951,23954,23955,23957,23958,23961],{},"Given",[471,23941,23152],{},"renders a list of items with a headline and other eye candy stuff. It takes care about rendering a ",[990,23944,23945],{},"no\ndata"," info banner when the given list is empty. The default ",[471,23948,23949],{},"this.state.data"," value is an empty array instead of\nundefined or null to avoid the notorious ",[471,23952,23953],{},"Cannot read property XXX of undefined",". Then the code snippet above results in\nalways rendering ",[471,23956,23152],{}," and therefore the ",[990,23959,23960],{},"no data"," info banner (empty array is a truthy expression).",[439,23963,23964,23965,23967,23968,23971],{},"The unwanted ",[990,23966,23960],{}," info banner could be avoided by adding the ",[471,23969,23970],{},"this.state.loading"," flag to the condition. But that’s\nnot really satisfying since this adds more complexity which even will be copied and pasted into other components.",[464,23973,23975],{"className":15199,"code":23974,"language":15201,"meta":469,"style":469},"renderData() {\n return (this.state.data && !this.state.loading)\n ?\n : null;\n}\n",[471,23976,23977,23983,24011,24015,24023],{"__ignoreMap":469},[474,23978,23979,23981],{"class":476,"line":477},[474,23980,23292],{"class":480},[474,23982,15227],{"class":503},[474,23984,23985,23987,23989,23991,23993,23996,23999,24001,24004,24006,24008],{"class":476,"line":507},[474,23986,23910],{"class":810},[474,23988,15250],{"class":503},[474,23990,8394],{"class":510},[474,23992,23237],{"class":503},[474,23994,23995],{"class":810},"&",[474,23997,23998],{"class":503},"amp;",[474,24000,23995],{"class":810},[474,24002,24003],{"class":503},"amp; ",[474,24005,18773],{"class":810},[474,24007,8394],{"class":510},[474,24009,24010],{"class":503},".state.loading)\n",[474,24012,24013],{"class":476,"line":547},[474,24014,23920],{"class":810},[474,24016,24017,24019,24021],{"class":476,"line":584},[474,24018,23925],{"class":810},[474,24020,23129],{"class":510},[474,24022,2139],{"class":503},[474,24024,24025],{"class":476,"line":607},[474,24026,703],{"class":503},[439,24028,24029,24030,24032,24033,24035],{},"Furthermore… remember the actual challenge we tried to solve with the ",[471,24031,13362],{},"component which delays the rendering of\nthe loading info? Exactly, we wanted to avoid flickering and displaying the loading info when the data is received\nwithin 100ms. Now we’ve added this again for ",[471,24034,23152],{},". The component will be unmounted and mounted within 42ms for\ninstance. The new data is visible but all eye candy around the data list (like the headline) is gone and rerendered\nwithin one blink of an eye.",[439,24037,24038,24039,24041],{},"So let’s improve the ",[471,24040,13362],{},"component to handle the rendering of it’s children. We have two react techniques to\nimplement this:",[994,24043,24044,24047],{},[997,24045,24046],{},"render props",[997,24048,24049],{},"function as child",[439,24051,24052,24053,24056,24057,24059,24060,24063,24064,18175,24066,24068,24069,24071],{},"Both are the same actually. The ",[990,24054,24055],{},"render prop"," pattern uses a function passed as component property to render something.\nThe ",[990,24058,24049],{}," pattern is… well… the same. ",[471,24061,24062],{},"children"," is just an additional property of a React component. The\ndifference between ",[990,24065,24046],{},[990,24067,24049],{}," is the syntax. Personally I prefer ",[990,24070,24046],{}," since this\nis more explicit and doesn’t leave room of misconception for people not knowing React and JSX in detail.",[464,24073,24075],{"className":15199,"code":24074,"language":15201,"meta":469,"style":469},"class RenderProps extends React.Component {\n render() {\n return this.renderData()} />;\n }\n}\n\nclass FunctionAsChild extends React.Component {\n render() {\n return {() => this.renderData()};\n }\n}\n",[471,24076,24077,24094,24100,24123,24127,24131,24135,24152,24158,24165,24169],{"__ignoreMap":469},[474,24078,24079,24081,24084,24086,24088,24090,24092],{"class":476,"line":477},[474,24080,15208],{"class":810},[474,24082,24083],{"class":480}," RenderProps",[474,24085,15214],{"class":810},[474,24087,23085],{"class":480},[474,24089,1402],{"class":503},[474,24091,23090],{"class":480},[474,24093,14027],{"class":503},[474,24095,24096,24098],{"class":476,"line":507},[474,24097,23097],{"class":480},[474,24099,15227],{"class":503},[474,24101,24102,24104,24107,24109,24111,24114,24117,24120],{"class":476,"line":547},[474,24103,23104],{"class":810},[474,24105,24106],{"class":510}," this",[474,24108,1402],{"class":503},[474,24110,23292],{"class":480},[474,24112,24113],{"class":503},"()} ",[474,24115,24116],{"class":810},"/&",[474,24118,24119],{"class":14037},"gt",[474,24121,24122],{"class":503},";;\n",[474,24124,24125],{"class":476,"line":584},[474,24126,15319],{"class":503},[474,24128,24129],{"class":476,"line":607},[474,24130,703],{"class":503},[474,24132,24133],{"class":476,"line":642},[474,24134,917],{"emptyLinePlaceholder":916},[474,24136,24137,24139,24142,24144,24146,24148,24150],{"class":476,"line":663},[474,24138,15208],{"class":810},[474,24140,24141],{"class":480}," FunctionAsChild",[474,24143,15214],{"class":810},[474,24145,23085],{"class":480},[474,24147,1402],{"class":503},[474,24149,23090],{"class":480},[474,24151,14027],{"class":503},[474,24153,24154,24156],{"class":476,"line":694},[474,24155,23097],{"class":480},[474,24157,15227],{"class":503},[474,24159,24160,24162],{"class":476,"line":700},[474,24161,23104],{"class":810},[474,24163,24164],{"class":503}," {() => this.renderData()};\n",[474,24166,24167],{"class":476,"line":913},[474,24168,15319],{"class":503},[474,24170,24171],{"class":476,"line":920},[474,24172,703],{"class":503},[439,24174,24175,24176,24178,24179,24181,24182,1402],{},"The first step is to extend the ",[471,24177,13362],{},"component with a render property. Instead of returning ",[471,24180,23202],{},"when data is not\nloading we have to call ",[471,24183,24184],{},"this.props.render",[464,24186,24188],{"className":15199,"code":24187,"language":15201,"meta":469,"style":469},"class Waiting extends React.Component {\n constructor() { ... }\n\n componentWillReceiveProps(nextProps) { ... }\n\n+ renderContent() {\n+ return this.state.loading ? \u003Cdiv>loading...\u003C/div> : this.props.render();\n+ }\n+\n render() {\n- return this.state.loading ? \u003Cdiv>loading...\u003C/div> : null;\n+ return this.renderContent();\n }\n}\n",[471,24189,24190,24206,24218,24222,24237,24241,24250,24283,24289,24293,24299,24327,24342,24346],{"__ignoreMap":469},[474,24191,24192,24194,24196,24198,24200,24202,24204],{"class":476,"line":477},[474,24193,15208],{"class":810},[474,24195,23080],{"class":480},[474,24197,15214],{"class":810},[474,24199,23085],{"class":480},[474,24201,1402],{"class":503},[474,24203,23090],{"class":480},[474,24205,14027],{"class":503},[474,24207,24208,24210,24213,24216],{"class":476,"line":507},[474,24209,23410],{"class":810},[474,24211,24212],{"class":503},"() { ",[474,24214,24215],{"class":810},"...",[474,24217,23680],{"class":503},[474,24219,24220],{"class":476,"line":547},[474,24221,917],{"emptyLinePlaceholder":916},[474,24223,24224,24226,24228,24230,24233,24235],{"class":476,"line":584},[474,24225,23459],{"class":480},[474,24227,1483],{"class":503},[474,24229,23464],{"class":14037},[474,24231,24232],{"class":503},") { ",[474,24234,24215],{"class":810},[474,24236,23680],{"class":503},[474,24238,24239],{"class":476,"line":607},[474,24240,917],{"emptyLinePlaceholder":916},[474,24242,24243,24245,24248],{"class":476,"line":642},[474,24244,23407],{"class":810},[474,24246,24247],{"class":480}," renderContent",[474,24249,15227],{"class":503},[474,24251,24252,24254,24256,24258,24260,24262,24264,24266,24268,24270,24272,24274,24276,24279,24281],{"class":476,"line":663},[474,24253,23407],{"class":810},[474,24255,23104],{"class":810},[474,24257,2596],{"class":510},[474,24259,23558],{"class":503},[474,24261,23112],{"class":810},[474,24263,23115],{"class":503},[474,24265,15027],{"class":15051},[474,24267,23120],{"class":503},[474,24269,15027],{"class":15051},[474,24271,9010],{"class":503},[474,24273,6562],{"class":810},[474,24275,2596],{"class":510},[474,24277,24278],{"class":503},".props.",[474,24280,23017],{"class":480},[474,24282,15271],{"class":503},[474,24284,24285,24287],{"class":476,"line":694},[474,24286,23407],{"class":810},[474,24288,15319],{"class":503},[474,24290,24291],{"class":476,"line":700},[474,24292,23452],{"class":810},[474,24294,24295,24297],{"class":476,"line":913},[474,24296,23097],{"class":480},[474,24298,15227],{"class":503},[474,24300,24301,24303,24305,24307,24309,24311,24313,24315,24317,24319,24321,24323,24325],{"class":476,"line":920},[474,24302,10143],{"class":810},[474,24304,23104],{"class":810},[474,24306,2596],{"class":510},[474,24308,23558],{"class":503},[474,24310,23112],{"class":810},[474,24312,23115],{"class":503},[474,24314,15027],{"class":15051},[474,24316,23120],{"class":503},[474,24318,15027],{"class":15051},[474,24320,9010],{"class":503},[474,24322,6562],{"class":810},[474,24324,23129],{"class":510},[474,24326,2139],{"class":503},[474,24328,24329,24331,24333,24335,24337,24340],{"class":476,"line":926},[474,24330,23407],{"class":810},[474,24332,23104],{"class":810},[474,24334,2596],{"class":510},[474,24336,1402],{"class":503},[474,24338,24339],{"class":480},"renderContent",[474,24341,15271],{"class":503},[474,24343,24344],{"class":476,"line":932},[474,24345,15319],{"class":503},[474,24347,24348],{"class":476,"line":938},[474,24349,703],{"class":503},[1024,24351,24352],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":469,"searchDepth":507,"depth":507,"links":24354},[24355,24356,24357],{"id":23064,"depth":507,"text":23065},{"id":23341,"depth":507,"text":23342},{"id":23890,"depth":507,"text":23891},[1030,1031],"2017-12-14T12:28:51","Giving fast feedback to users has been improved by single page applications over the request response cycle. However,\\nthere is one serious downside with this approach. Elements are popping out of the wild on various sections everytime.\\nParticular data loading indicated by a waiting animation is affected with this phenomenon. In this blog I’d like to\\npresent you our solution of a UI component that takes care about delaying the rendering of the animation.","https://synyx.de/blog/implementing-a-waiting-component-with-user-experience-in-mind/",{},"/blog/implementing-a-waiting-component-with-user-experience-in-mind",{"title":22995,"description":23004},"blog/implementing-a-waiting-component-with-user-experience-in-mind",[],"Giving fast feedback to users has been improved by single page applications over the request response cycle. However, there is one serious downside with this approach. Elements are popping out of the wild on various sections everytime. Particular data loading indicated by a waiting animation is affected with this phenomenon. In this blog I’d like to present you our solution of a UI component that takes care about delaying the rendering of the animation.","Pi6q7zwo_sMrYVeVQ_ABM1ZbdLVghYcFYUOKXE1oxvI",{"id":24370,"title":24371,"author":24372,"body":24373,"category":24449,"date":24450,"description":24451,"extension":1034,"link":24452,"meta":24453,"navigation":916,"path":24454,"seo":24455,"slug":24377,"stem":24456,"tags":24457,"teaser":24459,"__hash__":24460},"blog/blog/die-unrealistische-vision-die-dann-doch-irgendwie-funktioniert.md","Die unrealistische Vision – die dann doch irgendwie funktioniert",[88],{"type":432,"value":24374,"toc":24447},[24375,24378,24381,24384,24387,24390,24410,24413,24416,24419,24435,24438,24441,24444],[435,24376,24371],{"id":24377},"die-unrealistische-vision-die-dann-doch-irgendwie-funktioniert",[439,24379,24380],{},"Ich kann mich nicht mehr so 100% erinnern – aber irgendwann vor 2014 planten wir den Umzug in neue Räumlichkeiten. Raus\naus dem Altbau – rein in ein Bürogebäude – Neues Netzwerk, mehr Internet als wir jemals bräuchten.",[439,24382,24383],{},"Natürlich stellte sich auch die Frage, wie wir unsere neuen Arbeitsplätze ausstatten. Auf jeden Fall irgendwie “agil”\nwie man es heute nennen würde – War-Rooms müssten sein, und am besten können sich jederzeit Gruppen zusammensetzen um\nzusammen schnell was zu hacken.",[439,24385,24386],{},"Doch etwas später holte uns dann doch die Realität ein…",[439,24388,24389],{},"Die Planungen waren vielversprechend:",[994,24391,24392,24395,24398,24401,24404,24407],{},[997,24393,24394],{},"Telefonanlage -> kann man sich mit seiner Nummer anmelden. ✔",[997,24396,24397],{},"Die Telefonanlage kann auch VoIP für das Handy anbieten ✔",[997,24399,24400],{},"Dockingstationen -> wenigstens für die Windows/Linux User gleich ✔",[997,24402,24403],{},"Wi-Fi haben wir ja schon. ✔",[997,24405,24406],{},"Für den persönlichen Krempel (eigene Tastatur etc.) wird es einen kleinen Spind geben (der ja eh vorgeschrieben ist).\n✔",[997,24408,24409],{},"Für Wired-Ethernet gibt es IEEE 802.1X ✔",[439,24411,24412],{},"Läuft also? Weit gefehlt.",[439,24414,24415],{},"Kurz vor dem Umzug hat $Laptop_hersteller neue Dockingstationen herausgebracht, verschiedene User haben einen Monitor\nhochkant, manche haben ein Standtelefon – die Mobile Entwickler haben mehrere Handys – andere Teams haben\nSpezialhardware vom Kunden die sich nicht “einfach mal so” auf- und abbauen lässt – und IEEE 802.1X hat genug “Stoff\ninne” um mehrere Jahre mit einem Standup-Comedyprogramm durch die Veranstaltungshallen der Nation zu tingeln. Und das\nWi-Fi war den Anforderungen natürlich auch nicht gewachsen.",[439,24417,24418],{},"Das Ergebnis:",[994,24420,24421,24429,24432],{},[997,24422,24423,24424],{},"Jeder kann sich überall hinsetzen und Arbeiten ✖\n",[994,24425,24426],{},[997,24427,24428],{},"Schnelle “agile” Teambildung ✖",[997,24430,24431],{},"Per Wi-Fi kann man bequem mit den Laptops arbeiten ✖",[997,24433,24434],{},"Das Wi-Fi reicht zum Telefonieren ✖",[439,24436,24437],{},"Ein paar (3) Wi-Fi Projekte später und weiterer Fragmentierung der Laptop-Infrastruktur (jeder darf sich aussuchen\nwas er will) kommen wir dennoch langsam der ursprünglichen Idee näher.",[439,24439,24440],{},"Wie? Wir haben einfach angefangen Rollen an die Schreibtische zu schrauben. So hat zwar jeder noch “seinen”\nSchreibtisch – mit all der nötigen und unnötigen Hardware die sich darauf so mit der Zeit ansammelt – aber wenn man mal\nschnell Umziehen will, oder einen Tag mit einem Team zusammen sitzen will – dann rollt man einfach rüber.",[439,24442,24443],{},"Vielleicht war die Planung der ursprünglichen Vision für die “variablen Arbeitsplätze” zu technisch und nicht\npragmatisch genug. Aber alleine dadurch das man die Vision doch im Hinterkopf hatte, hat sich im Endeffekt eine Lösung\nergeben an die am Anfang einfach niemand gedacht hat.",[439,24445,24446],{},"Was ich versuche für mich mitzunehmen – es lohnt sich an eine Vision zu glauben – auch wenn diese unerreichbar\nerscheint.",{"title":469,"searchDepth":507,"depth":507,"links":24448},[],[1031],"2017-11-17T14:40:12","Ich kann mich nicht mehr so 100% erinnern – aber irgendwann vor 2014 planten wir den Umzug in neue Räumlichkeiten. Raus\\naus dem Altbau – rein in ein Bürogebäude – Neues Netzwerk, mehr Internet als wir jemals bräuchten.","https://synyx.de/blog/die-unrealistische-vision-die-dann-doch-irgendwie-funktioniert/",{},"/blog/die-unrealistische-vision-die-dann-doch-irgendwie-funktioniert",{"title":24371,"description":24380},"blog/die-unrealistische-vision-die-dann-doch-irgendwie-funktioniert",[9546,389,24458],"vision","Ich kann mich nicht mehr so 100% erinnern - aber irgendwann vor 2014 planten wir den Umzug in neue Räumlichkeiten. Raus aus dem Altbau - rein in ein Bürogebäude - Neues Netzwerk, mehr Internet als wir jemals bräuchten.","h4zsI9rULxXOa91wTO4Kcmy185QCU5z8mYDX2zxqajI",{"id":24462,"title":24463,"author":24464,"body":24465,"category":24594,"date":24595,"description":24596,"extension":1034,"link":24597,"meta":24598,"navigation":916,"path":24599,"seo":24600,"slug":24469,"stem":24602,"tags":24603,"teaser":24604,"__hash__":24605},"blog/blog/das-barcamp-pforzheim-2017.md","Das Barcamp Pforzheim 2017",[238,412],{"type":432,"value":24466,"toc":24586},[24467,24470,24483,24489,24494,24503,24506,24510,24516,24519,24525,24531,24534,24538,24552,24556,24559,24563,24566,24570,24573,24577,24580],[435,24468,24463],{"id":24469},"das-barcamp-pforzheim-2017",[439,24471,24472,24473,24478,24479,24482],{},"Am 03.11. und 04.11.2017 fand das erste Barcamp in Pforzheim statt. Veranstaltungsort war\ndas ",[1002,24474,24477],{"href":24475,"rel":24476},"http://www.emma-pf.de/",[1006],"Kreativzentrum Emma",". Das Emma bietet viel Platz und angenehme Räumlichkeiten für ein\nOpenspace Event. Die große Aula hat ausreichend Platz für alle Teilnehmer zur Begrüßung, und eine große Leinwand für die\n",[448,24480,24481],{},"#bcpf2017"," – Twitterwall. Ein angeschlossener Raum für Frühstück und Mittagessen ist ein idealer Ort für ausgeprägte\nKommunikation. Und natürlich lassen wir uns als synyxer solch ein Ereignis in unmittelbaren Nähe nicht entgehen. Ein\nkleine Delegation bestehend aus Stephan Ulrich, Frederick Meseck und Christian Mennerich hat sich als aufgemacht, um\nzumindest einen Tag lang dabei sein zu können. Wir haben dabei auch die Gelegenheit genutzt, eine eigene Session\nanzubieten.",[439,24484,24485],{},[2205,24486],{"alt":24487,"src":24488},"Präsentation Barcamp Pforzheim","https://media.synyx.de/uploads/2019/04/pforzheim-Barcamp2017-1-768x432.jpg",[439,24490,24491],{},[2205,24492],{"alt":24487,"src":24493},"https://media.synyx.de/uploads/2019/04/pforzheim-Barcamp2017-2-768x432.jpg",[439,24495,24496,24497,24502],{},"Die Sessions fanden in fünf weiteren gut ausgestatteten Meetingräumen statt. Spannend war, dass unter den circa 100\nTeilnehmern sehr viele Barcamp-Neulinge zugegen waren. Nach der obligatorischen Vorstellungsrunde und den\nSessionangeboten ging es (fast) pünktlich los. Trotz des hohen Anteils an Barcamp-Newbies waren alle Slots ausgefüllt\nund die ",[1002,24498,24501],{"href":24499,"rel":24500},"https://app.konferenz.guide/index.html?seite=sessionList&event=bcpf17",[1006],"Sessions des ersten Tages"," waren\nvielfältig und thematisch breit gestreut . An dieser Stelle vorab ein Dankeschön für die tolle Organisation und die\nvielen großartigen Diskussionen!",[439,24504,24505],{},"Exemplarisch berichten wir hier kurz und knapp (in chronologischer Reihenfolge) über die Sitzungen, an denen wir\nteilnehmen konnten.",[3938,24507,24509],{"id":24508},"sessionslot-1-fachwissen-verdient-farbe","Sessionslot 1 – Fachwissen verdient Farbe",[439,24511,24512],{},[2205,24513],{"alt":24514,"src":24515},"No SQL Plakat","https://media.synyx.de/uploads/2019/04/no_sql_plakat-768x1087.jpg",[439,24517,24518],{},"Unser Session-Angebot “Fachwissen verdient Farbe – Illustrierte Poster-Stories statt langweiliger Vorträge” fand viel\nAnklang bei den Anwesenden. Wir haben unsere Idee präsentiert, wie wir bei synyx seit einiger Zeit versuchen, komplexe\nund komplizierte Inhalte aufzubereiten. Statt der üblichen Slidedecks mit vielen Aufzählungen setzen wir auf die\nErstellung eines Plakats, das eine Geschichte erzählt. Mittels einer Javascript Bibliothek werden im einen Browser dann\nZoomstufen festgelegt, und die so festgelegten Ausschnitte bilden die Folien des Vortrags. Wir entwickeln diese Art der\nVortragspräsentation in enger Zusammenarbeit zwischen Designer und dem Vortragendem. Diese Art der Vortragsgestaltung\nstößt auch auf großen IT-Tagungen wie der JAX, den Frankfurter IT-Tagen oder dem JavaForumNord auf reges Interesse.\nStephan hat die Herausforderungen aus Sicht des Designers herausgestellt, während Freddy und Christian bereits\nVortragserfahrung mit dieser Technik haben und aus der Sicht des Vortragenden berichteten. Exemplarisch haben wir\ngezeigt, wie viel Aufwand es ist, bis eine Szene des Vortrags seine endgültige Gestalt hat.",[439,24520,24521],{},[2205,24522],{"alt":24523,"src":24524},"resilience Plakat","https://media.synyx.de/uploads/2019/04/resilience_small-768x1086.jpg",[439,24526,24527],{},[2205,24528],{"alt":24529,"src":24530},"scribble","https://media.synyx.de/uploads/2019/04/scribble1-768x543.jpg",[439,24532,24533],{},"Insbesondere die gestalterischen Herausforderungen wurden mit anwesenden Designern ausgiebig besprochen. Einige\nTeilnehmer konnten sich direkt vorstellen, wie ein Plakat im eigenen Unternehmen einen Mehrwert bietet kann gegenüber\nder bisherigen Art, Inhalte intern und extern zu kommunizieren. In Zukunft soll bei synyx ein Tooling entstehen und\nunter einer Open Source Lizenz veröffentlicht werden, dass die Entwicklung von Vortragspostern gut unterstützt. Die\nDiskussion war spannend, und für uns war es interessant und lehrreich, unsere Ideen mit nicht-ITlern zu teilen.",[3938,24535,24537],{"id":24536},"sessionslot-2-design-von-der-stange","Sessionslot 2 – Design von der Stange",[439,24539,24540,24541,520,24546,24551],{},"Jochen Baumann hat eine Sitzung angeboten zu Design von der Stange, um die Nutzung von und\nErfahrungen ",[1002,24542,24545],{"href":24543,"rel":24544},"https://99designs.de",[1006],"99design",[1002,24547,24550],{"href":24548,"rel":24549},"https://www.designcrowd.com/",[1006],"Design Crowd"," und ähnlichen Portalen zu\ndiskutieren. Trotz unterschiedlicher Erfahrungen herrschte doch Einigkeit darüber, dass sich Design nicht einfach\noutsourcen lässt. Gute und vor allem homogene Designkonzepte leben von der Interaktion zwischen Designer und Kunde, und\nindividuelle Beratung wird auch in Zukunft ein Schlüssel sein für erfolgreiche Konzepte. Hier spielt auch eine große\nRolle, wie gut ein Designer ein Gespür entwickeln kann für die Bedürfnisse seiner Kunden, eine Qualifikation, die in der\nAusbildung der Designer an den Hochschulen oftmals noch zu kurz kommt.",[3938,24553,24555],{"id":24554},"sessionslot-3-künstliche-intelligenz-einführung-ausblick","Sessionslot 3 – Künstliche Intelligenz – Einführung & Ausblick",[439,24557,24558],{},"Carsten Kraus hat eine sehr schöne und gut allgemein verständliche Einführung in die Welt der künstlichen Intelligenz\ngehalten. Die harte Statistik hat er minimal gehalten und gut motiviert die Funktionsweise und Interpretiertbarkeit\nneuronaler Netze erklärt. Als bekennender Go-Spieler hat er großen Respekt vor der Leistungsfähigkeit heutiger Rechner\ngezeigt, nachdem der erste Computer Go erlernt und mehrere Großmeister geschlagen hat. Die Diskussionen in dieser\nSitzung sind dem Thema angebracht auch philosophisch geworden, wobei Carsten sich als jemand bekannt hat, der den\nMaschinen “echte” Intelligenz zugesteht. Aufgrund des exponentiellen Lernverhaltens von Computern hat er die Vermutung,\ndass Computer bereits in wenigen Jahrzehnten intelligenter sein können als Menschen. Was allerdings Intelligenz und vor\nallem “intelligenter sein” genau bedeuten, dass war ein Teil der Diskussion, über den natürlich kein Konsens gefunden\nwerden konnte. Aber sehr spannend ist das Thema allemal, und es wird uns auch in Zukunft alle berühren.",[3938,24560,24562],{"id":24561},"sessionslot-3-wie-kann-pforzheim-schöner-werden","Sessionslot 3 – Wie kann Pforzheim schöner werden?",[439,24564,24565],{},"In einer etwas kleineren Runde kamen etwa 10 Teilnehmer zusammen um darüber zu sprechen, was es wohl braucht um das\nPforzheimer Stadtbild anschaulicher zu machen. Interessant waren bereits die Erfahrungsberichte der Diskutierenden. Von\nalt-eingesessenen bis hin zu frisch-zugezogenen schilderten die Teilnehmer nicht nur ihre eigenen Eindrücke bezüglich\nPforzheim und Umgebung: “Hässlich”, “monoton” oder “veraltet” sind Worte die der Goldstadt oftmals entgegenschlagen. Wer\njetzt glaubt diese Session sei in Schwarzmalerei verfallen, irrt sich. Die barcamp-Runde war sich schnell einig: Es\ngibt sie, die schönen Ecken im Pforzheimer Raum, sowohl in der Stadt selbst als auch im grünen Umland. Doch was können\nwir tun um diese Perlen herauszuarbeiten oder generell ersichtlicher zu machen? Um sich dieser Frage zu nähern, bat\nSession-Leiterin Stefanie alle Teilnehmer, stichwortartig Verbesserungsvorschläge auf Post-Its zu schreiben, welche im\nAnschluss besprochen wurden. Trockene Theorie ohne greifbare Ergebnisse? Weit gefehlt! Die Session hat Menschen\nzusammengeführt die sich bereits für eine Aufwertung des Stadtbilds engagieren oder dies zukünftig tun wollen. Es wurden\nTelefonnummern und Kontakte getauscht, Ideen zusammengeführt und Pläne geschmiedet – für ein schöneres Pforzelona!",[3938,24567,24569],{"id":24568},"sessionslot-4-agilität-in-der-praxis-bei-komplexen-produkten","Sessionslot 4 – Agilität in der Praxis bei komplexen Produkten",[439,24571,24572],{},"In kleiner Runde hat Margit darüber gesprochen, wie die agile Vorgehensweise bei der Produktion von komplexen Produkten\nzu verstehen ist. Als Beispiel diente die Herstellung eines Autos, mit allen Facetten der Produktionslinie, von der Idee\nbis hin zur tatsächlichen Produktion und allen Beteiligten, von Designer und Ingenieuren über Teilzuliefereren bis hin\nzum Vertrieb. In der Diskussion wurde klar, dass die in der agilen Welt verwendeten Begriffe, insbesondere der der\nVision, nicht immer gleich interpretiert werden, und zu allen Diskussionen immer auch Begriffsklärung gehört. Weiter\nstellt der Umgang mit komplexen Strukturen eine Herausforderung dar, insbesondere starre Hierarchien erschweren die\nkonsequente Anwendung agiler Methoden. Zur Zeit scheint es so, so wurde in der kleinen Runde festgehalten, dass es keine\nallgemein anerkannten Techniken gibt, diese Komplexität in den Griff zu bekommen. Die Sinnhaftigkeit, auch diese\nkomplexen Probleme agil anzugehen, wurde aber generell bejaht, und wir hoffen, hier in Zukunft viele positive Beispiele\nzu finden.",[3938,24574,24576],{"id":24575},"bis-zum-nächsten-mal","Bis zum nächsten Mal",[439,24578,24579],{},"Abschließend ein herzliches Dankeschön an alle Teilnehmer für das tolle Event, und das Orgateam für die gute Arbeit. Wir\nvon synyx werden nächstes Jahr sicherlich wieder dabei sein, und können uns auch gut vorstellen uns als Sponsoren zu\nbeteiligen.",[439,24581,24582],{},[2205,24583],{"alt":24584,"src":24585},"Drei Synyxer","https://media.synyx.de/uploads/2017/11/drei-synyxer-768x1044.jpg",{"title":469,"searchDepth":507,"depth":507,"links":24587},[24588,24589,24590,24591,24592,24593],{"id":24508,"depth":507,"text":24509},{"id":24536,"depth":507,"text":24537},{"id":24554,"depth":507,"text":24555},{"id":24561,"depth":507,"text":24562},{"id":24568,"depth":507,"text":24569},{"id":24575,"depth":507,"text":24576},[17502,1031],"2017-11-16T15:30:46","Am 03.11. und 04.11.2017 fand das erste Barcamp in Pforzheim statt. Veranstaltungsort war\\ndas Kreativzentrum Emma. Das Emma bietet viel Platz und angenehme Räumlichkeiten für ein\\nOpenspace Event. Die große Aula hat ausreichend Platz für alle Teilnehmer zur Begrüßung, und eine große Leinwand für die\\n#bcpf2017 – Twitterwall. Ein angeschlossener Raum für Frühstück und Mittagessen ist ein idealer Ort für ausgeprägte\\nKommunikation. Und natürlich lassen wir uns als synyxer solch ein Ereignis in unmittelbaren Nähe nicht entgehen. Ein\\nkleine Delegation bestehend aus Stephan Ulrich, Frederick Meseck und Christian Mennerich hat sich als aufgemacht, um\\nzumindest einen Tag lang dabei sein zu können. Wir haben dabei auch die Gelegenheit genutzt, eine eigene Session\\nanzubieten.","https://synyx.de/blog/das-barcamp-pforzheim-2017/",{},"/blog/das-barcamp-pforzheim-2017",{"title":24463,"description":24601},"Am 03.11. und 04.11.2017 fand das erste Barcamp in Pforzheim statt. Veranstaltungsort war\ndas Kreativzentrum Emma. Das Emma bietet viel Platz und angenehme Räumlichkeiten für ein\nOpenspace Event. Die große Aula hat ausreichend Platz für alle Teilnehmer zur Begrüßung, und eine große Leinwand für die\n#bcpf2017 – Twitterwall. Ein angeschlossener Raum für Frühstück und Mittagessen ist ein idealer Ort für ausgeprägte\nKommunikation. Und natürlich lassen wir uns als synyxer solch ein Ereignis in unmittelbaren Nähe nicht entgehen. Ein\nkleine Delegation bestehend aus Stephan Ulrich, Frederick Meseck und Christian Mennerich hat sich als aufgemacht, um\nzumindest einen Tag lang dabei sein zu können. Wir haben dabei auch die Gelegenheit genutzt, eine eigene Session\nanzubieten.","blog/das-barcamp-pforzheim-2017",[],"Am 03.11. und 04.11.2017 fand das erste Barcamp in Pforzheim statt. Veranstaltungsort war das Kreativzentrum Emma. Das Emma bietet viel Platz und angenehme Räumlichkeiten für ein Openspace Event. Die große Aula hat ausreichend Platz für alle Teilnehmer zur Begrüßung, und eine große Leinwand für die #bcpf2017 – Twitterwall. Ein angeschlossener Raum für Frühstück und Mittagessen ist ein idealer Ort für ausgeprägte Kommunikation. Und natürlich lassen wir uns als synyxer solch ein Ereignis in unmittelbaren Nähe nicht entgehen.","5856c5g7cJuuOplcCw76cS5ZX00SzfwYT-X9qdHrSWQ",{"id":24607,"title":24608,"author":24609,"body":24610,"category":24709,"date":24710,"description":24711,"extension":1034,"link":24712,"meta":24713,"navigation":916,"path":24714,"seo":24715,"slug":24614,"stem":24717,"tags":24718,"teaser":24719,"__hash__":24720},"blog/blog/devoxx4kids-oktober-2017.md","Devoxx4Kids Oktober 2017",[142,332],{"type":432,"value":24611,"toc":24703},[24612,24615,24622,24625,24628,24631,24634,24637,24645,24651,24654,24663,24669,24673,24681,24686],[435,24613,24608],{"id":24614},"devoxx4kids-oktober-2017",[439,24616,24617,24618,24621],{},"Am 21.10.2017 fand die siebte Devoxx4Kids in der ",[1002,24619,18688],{"href":18686,"rel":24620},[1006]," in Karlsruhe statt,\nwelche bei Groß und Klein für sehr viel Spaß sorgte. Auch dieses Jahr waren wir mit 30 Kindern und 19 Mentoren voll\nbesetzt. Hierbei waren 11 Mädchen mit dabei, was uns sehr gefreut hat.",[3938,24623,4154],{"id":24624},"workshops",[439,24626,24627],{},"Wie auch bereits bei den letzten zwei Devoxx4Kids, haben wir uns wieder für drei Workshops mit je 90 Minuten\nentschlossen. Die Kinder hatten dadurch mehr Zeit pro Workshop und wurden nicht schon nach einer Stunde aus ihrer\nAufgabe gerissen.",[439,24629,24630],{},"Um für ein wenig mehr Abwechslung in den Workshops zu sorgen, haben wir gleich zwei neue Workshops für die Kinder\nvorbereitet. In einem der neuen Workshops konnten die Kinder mit der Open Source Musiksoftware ‘Sonic Pi’ ihren\nRaspberry Pi in ein DJ Pult verwandeln. Beim zweiten neuen Workshop wurden die Kinder zu kleinen Spieleentwicklern und\nkonnten mit ‘Scratch’ ein eigenes Halloweenspiel programmieren.",[3938,24632,12213],{"id":24633},"sonic-pi",[439,24635,24636],{},"Zu Beginn des Workshops haben wir die Grundlagen erlernt. Einzelne Töne, Tonfolgen und Schleifen mit kraftvollen Beats\nwurden ausprobiert. Zum Schluss durften die Kids reihum ihre Musik der Gruppe vorspielen. Es hat den Kids und uns sehr\nviel Spaß gemacht, deshalb wird dieser Workshop mit den anderen zwei vermutlich im nächsten Frühjahr wieder auf dem Plan\nstehen.",[439,24638,24639,24640,24644],{},"Wer nun selbst Musik programmieren möchte, kann sich ",[1002,24641,12213],{"href":24642,"rel":24643},"http://sonic-pi.net",[1006]," kostenfrei herunterladen.",[439,24646,24647],{},[2205,24648],{"alt":24649,"src":24650},"devoxx4kids 2017","https://media.synyx.de/uploads/2019/04/devoxx4kids2017nov-768x512.jpg",[3938,24652,12189],{"id":24653},"scratch",[439,24655,3786,24656,24659,24660,18781],{},[1002,24657,12189],{"href":4032,"rel":24658},[1006]," Workshop gingen die Kinder auf eine Schatzsuche im Halloweengewand. Die Kinder\nentwickelten von der Geschichte des Spiels, über dessen Leveldesign bis hin zum Finale ihr ganz eigenes Spiel. Wie alle\nunsere Workshops ist auch dieser auf ",[1002,24661,4042],{"href":12198,"rel":24662},[1006],[439,24664,24665],{},[2205,24666],{"alt":24667,"src":24668},"devoxx4kids Scratch 2017","https://media.synyx.de/uploads/2019/04/devoxx4kids2017_nov_scratch-768x512.jpg",[3938,24670,24672],{"id":24671},"mindstorms","Mindstorms",[439,24674,24675,24676,24680],{},"Ein altbekannter Workshop, welcher uns die letzten Jahre begleitet und immer weiter entwickelt wurde ist\nunser ",[1002,24677,24679],{"href":4040,"rel":24678},[1006],"Lego Mindstorms Workshop",". Bei diesem Workshop\ndurften die Kinder ihrem Roboter Leben einhauchen. Den Kindern wurden mehrere Aufgaben gestellt, welche sie mit der\nProgrammierung ihres Roboters intelligent lösen mussten. Zum Beispiel mussten sie das Verhalten eines\nRoboterstaubsaugers nachbilden und auf bestimmte äußere Merkmale im Raum reagieren.",[439,24682,24683],{},[2205,24684],{"alt":24679,"src":24685},"https://media.synyx.de/uploads/2019/04/devoxx4kids_nov_2017_Mindstorm-768x512.jpg",[439,24687,24688,24689,24692,24693,24698,24699,24702],{},"Vielen Dank an unsere Mentoren, den Kindern, der ",[1002,24690,18688],{"href":18686,"rel":24691},[1006]," zum Bereitstellen der\nRäumlichkeiten sowie\nder ",[1002,24694,24697],{"href":24695,"rel":24696},"https://www.dm.de/arbeiten-und-lernen/arbeiten-bei-dm/filiadata-c534052.html",[1006],"IT-Tochter Filiadata"," von DM\nund ",[1002,24700,389],{"href":18760,"rel":24701},[1006]," für ihr Sponsoring.",{"title":469,"searchDepth":507,"depth":507,"links":24704},[24705,24706,24707,24708],{"id":24624,"depth":507,"text":4154},{"id":24633,"depth":507,"text":12213},{"id":24653,"depth":507,"text":12189},{"id":24671,"depth":507,"text":24672},[4216,1031],"2017-11-02T14:02:50","Am 21.10.2017 fand die siebte Devoxx4Kids in der Karlshochschule in Karlsruhe statt,\\nwelche bei Groß und Klein für sehr viel Spaß sorgte. Auch dieses Jahr waren wir mit 30 Kindern und 19 Mentoren voll\\nbesetzt. Hierbei waren 11 Mädchen mit dabei, was uns sehr gefreut hat.","https://synyx.de/blog/devoxx4kids-oktober-2017/",{},"/blog/devoxx4kids-oktober-2017",{"title":24608,"description":24716},"Am 21.10.2017 fand die siebte Devoxx4Kids in der Karlshochschule in Karlsruhe statt,\nwelche bei Groß und Klein für sehr viel Spaß sorgte. Auch dieses Jahr waren wir mit 30 Kindern und 19 Mentoren voll\nbesetzt. Hierbei waren 11 Mädchen mit dabei, was uns sehr gefreut hat.","blog/devoxx4kids-oktober-2017",[4216],"Am 21.10.2017 fand die siebte Devoxx4Kids in der Karlshochschule in Karlsruhe statt, welche bei Groß und Klein für sehr viel Spaß sorgte. Auch dieses Jahr waren wir mit 30 Kindern und 19 Mentoren voll besetzt. Hierbei waren 11 Mädchen mit dabei, was uns sehr gefreut hat. Workshops Wie auch bereits bei den letzten zwei Devoxx4Kids, haben wir uns wieder für drei Workshops mit je 90 Minuten entschlossen.","0HMMrv1kjTc3sQg4bG6Qlg9nPrgZqXFNkXCjrU5GBT8",{"id":24722,"title":24723,"author":24724,"body":24725,"category":24877,"date":24878,"description":24879,"extension":1034,"link":24880,"meta":24881,"navigation":916,"path":24882,"seo":24883,"slug":24729,"stem":24884,"tags":24885,"teaser":24890,"__hash__":24891},"blog/blog/how-we-ended-up-using-bdd.md","How we ended up using BDD",[418],{"type":432,"value":24726,"toc":24870},[24727,24730,24733,24737,24740,24744,24747,24751,24754,24758,24798,24804,24810,24816,24822,24824,24827,24836,24838],[435,24728,24723],{"id":24729},"how-we-ended-up-using-bdd",[439,24731,24732],{},"It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding\nand optimizing our agile software development process we recognized that we established the building blocks of BDD. It\nworks quite well and offers a lot of space and flexibility for our future plans, switching our architecture to\nmicroservices.",[1065,24734,24736],{"id":24735},"the-project-setup","The project setup",[439,24738,24739],{},"But let us start at the beginning of the project. We started on a green field project but had to embed in a system\ncontext with established interfaces and many third party services as we had to replace a legacy system. Also as a newly\nformed team consisting of internal and external employees we had to find our team spirit and embed into the existing\norganizational structure. With at least one dedicated Product Owner (PO) and a dedicated Scrum Master we started as a\nteam of 7 people. We are one of multiple Scrum teams in an organization sector. The operations team is (still) in\nanother sector and the domain experts or Functional Owner (FO) even in another building. On the one hand it makes\ncommunication on domain topics harder, on the other hand with this setting we had a lot of liberties to build, release\nand deploy whenever we want. Noticing that other teams organized their cyclic release as part of their scrum process\nevery two weeks our way to deploy after feature complete, seemed to be unconventional to others.",[1065,24741,24743],{"id":24742},"trust-as-prerequisite-to-keep-liberties-in-development-process","Trust as prerequisite to keep liberties in development process",[439,24745,24746],{},"As time went by we had our first deployment to production. We have been consuming other services and vice versa. While\nmore features were added, the complexity and the amount of deployments to production increased. Our POs’ confidence in\nour unconventional release and deploy process decreased, perhaps feeling that the project got out of hand. Of course we\nsent announcement E-Mails to relevant stakeholders, but somehow we had not built up enough trust yet and our POs still\nhad the desire to make manual tests on the release stage. So we started to create charts of all processes together with\nour POs as part of our development process just before talking about the user story in our refinement. It was and still\nis helpful for two reasons. On the one hand it was creating a format the whole team understands as it was simplifying\nthe process and serving as documentation. On the other hand it was serving as kind of a contract, which we as devs and\nthe POs were committed to. Also we arranged that our POs wrote acceptance tests for the use case. With those newly\nintroduced methods in our development process, confidence and trust increased and we kept our way of releasing and\ndeploying after the feature is “Definition of Done” ready during the sprint.",[1065,24748,24750],{"id":24749},"tests-turn-from-verification-to-specification","Tests turn from verification to specification",[439,24752,24753],{},"Again time went by and as the FOs are sitting in another building and in contrast to our POs are feeling the need of\nmanual tests, again the question for a fixed deployment-plan arose. Newly implemented features and more dependencies to\nother teams strengthened the need. To hold on to our flexibility and to keep deploying during our sprint we introduced a\nprocess called “3 Amigo Testing”. Before a story accomplishes the “Definition of Ready” and is thereby ready for our\nrefinement the tests have to be written together with our FOs, our POs and us (one of the devs). Thereby tests turn from\nverification to specification no matter at which level: unit, integration or system integration! In addition the\ndiscussion not only improves the communication between the business and the technical team, it also helps to understand\nthe complexity and the pitfalls early which results in a better estimation.",[1065,24755,24757],{"id":24756},"time-to-grasp-the-nettle","Time to grasp the nettle",[439,24759,24760,24761,24766,24767,24772,24773,8351,24778,8351,24783,8351,24788,8351,24793,24797],{},"Technically that means you need an abstraction on top of your automated tests. Behaviour tests should always be written\nfrom a customer’s point of view and describe the acceptance criteria of a use case. Important is, that you use a\nbusiness readable language like ",[1002,24762,24765],{"href":24763,"rel":24764},"https://github.com/cucumber/cucumber/wiki/Gherkin",[1006],"gherkin",". It describes a test in the\nform of an initial state (“given”), an action (“when”) and a final state (“then”) which is common in the BDD context. By\nthat you have a description of a finite state machine which gives you the ability to describe a feature integrally. It\ndoes not matter at which level of the testing pyramide the actual implementation of the test is settled. A defined\nrequirement may be implemented at unit level as well as at system integration level. We\nuse ",[1002,24768,24771],{"href":24769,"rel":24770},"https://cucumber.io/",[1006],"cucumber"," because it meets the aforementioned requirements and because of existing knowledge\nand easy integration in our ecosystem. It is easy to use with the spring framework and gives us pretty jenkins\nreportings loved by our POs. But it is\nonly ",[1002,24774,24777],{"href":24775,"rel":24776},"http://lettuce.it/",[1006],"one",[1002,24779,24782],{"href":24780,"rel":24781},"http://spockframework.org/",[1006],"of",[1002,24784,24787],{"href":24785,"rel":24786},"http://robotframework.org/",[1006],"many",[1002,24789,24792],{"href":24790,"rel":24791},"http://jbehave.org/",[1006],"possible",[1002,24794,13977],{"href":24795,"rel":24796},"http://www.thucydides.info/#/",[1006],".\nFinally we recognized, that we established the building blocks of BDD:",[439,24799,24800,24803],{},[448,24801,24802],{},"Define tests beforehand"," – The specification of tests takes place before talking about the story in the refinement.",[439,24805,24806,24809],{},[448,24807,24808],{},"Use ubiquitous language"," – Defining the behaviour together with our POs and FOs we use a common language to avoid\nmisunderstandings.",[439,24811,24812,24815],{},[448,24813,24814],{},"Express all requirements in high-level business terms from the customer point of view"," – The tests serve as the\nacceptance criteria of the story describing the next business value to deliver. Therefore it is defined from the\ncustomer’s perspective.",[439,24817,24818,24821],{},[448,24819,24820],{},"Accessible to all stakeholders"," – The tests, which now serve as documentation, are accessible to all stakeholders via\na reporting plugin provided by our continuous integration tool.",[1065,24823,9392],{"id":9391},[439,24825,24826],{},"With that said we built up not only the technical prerequisite to eliminate all manual tests but also gained trust and\nare now ready for continuous deployment. In addition our testsuite turns out to be a helpful backbone for our ongoing\nproject of splitting a monolith into microservices.",[439,24828,24829,24830,24835],{},"If u want to know more about the project join us for our ",[1002,24831,24834],{"href":24832,"rel":24833},"https://synyx.de/events/17_08_jugka_microservices_andi/",[1006],"talk","\nat Java User Group Karlsruhe!",[439,24837,9418],{},[994,24839,24840,24846,24852,24858,24864],{},[997,24841,24842],{},[1002,24843,24844],{"href":24844,"rel":24845},"https://www.agilealliance.org/glossary/bdd",[1006],[997,24847,24848],{},[1002,24849,24850],{"href":24850,"rel":24851},"https://dannorth.net/introducing-bdd/",[1006],[997,24853,24854],{},[1002,24855,24856],{"href":24856,"rel":24857},"https://martinfowler.com/bliki/GivenWhenThen.html",[1006],[997,24859,24860],{},[1002,24861,24862],{"href":24862,"rel":24863},"http://www.agiletestingframework.com/atf/testing/behavior-driven-development-bdd/",[1006],[997,24865,24866],{},[1002,24867,24868],{"href":24868,"rel":24869},"https://en.wikipedia.org/wiki/Behavior-driven_development",[1006],{"title":469,"searchDepth":507,"depth":507,"links":24871},[24872,24873,24874,24875,24876],{"id":24735,"depth":547,"text":24736},{"id":24742,"depth":547,"text":24743},{"id":24749,"depth":547,"text":24750},{"id":24756,"depth":547,"text":24757},{"id":9391,"depth":547,"text":9392},[1030,1031],"2017-07-31T14:13:17","It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding\\nand optimizing our agile software development process we recognized that we established the building blocks of BDD. It\\nworks quite well and offers a lot of space and flexibility for our future plans, switching our architecture to\\nmicroservices.","https://synyx.de/blog/how-we-ended-up-using-bdd/",{},"/blog/how-we-ended-up-using-bdd",{"title":24723,"description":24732},"blog/how-we-ended-up-using-bdd",[24886,24887,15546,24888,24889],"bdd","behaviour","driven","trust","It was not our primary goal to use Behaviour Driven Development (BDD) in the project at a customer, but while finding and optimizing our agile software development process we recognized that we established the building blocks of BDD. It works quite well and offers a lot of space and flexibility for our future plans, switching our architecture to microservices. The project setup But let us start at the beginning of the project.","yImgzxEzR7UHl6fIsRCGniHr2xISKHBeIlBhI5IqQOs",{"id":24893,"title":24894,"author":24895,"body":24896,"category":24985,"date":24986,"description":24987,"extension":1034,"link":24988,"meta":24989,"navigation":916,"path":24990,"seo":24991,"slug":24992,"stem":24993,"tags":24994,"teaser":24995,"__hash__":24996},"blog/blog/kommunikation-im-realitaetscheck.md","Kommunikation im Realitätscheck",[244],{"type":432,"value":24897,"toc":24980},[24898,24901,24904,24915,24918,24922,24925,24928,24931,24934,24938,24941,24944,24948,24951,24954],[435,24899,24894],{"id":24900},"kommunikation-im-realitätscheck",[439,24902,24903],{},"Kommunikation ist allgegenwärtig und findet zu jeder Zeit statt. Schon Paul Watzlawick formulierte 1969 das bekannte\nAxiom: “Man kann nicht nicht kommunizieren.” Er macht dadurch deutlich, dass sich die Kommunikation wie die Luft zum\nAtmen verhält – sie umgibt uns überall, ist unausweichlich und essentiell zum Leben. Jeden Tag aufs Neue, sowohl privat\nals auch beruflich, wird uns dieser Umstand bewusst. Doch was passiert mit der Kommunikation, wenn…",[8310,24905,24906,24909,24912],{},[997,24907,24908],{},"…sie nur noch als Mittel zum Zweck dient?",[997,24910,24911],{},"…die Beziehungsebene tabu ist und jegliche Interaktion zum alleinigen Austausch von Sachinformationen verkommt?",[997,24913,24914],{},"…für Konfliktpotentiale kein Raum zur Entfaltung bereit steht?",[439,24916,24917],{},"Eines ist sicher: dies hat Auswirkungen. Wie stark diese zum Teil sein können, soll das folgende Beispiel zeigen:",[3938,24919,24921],{"id":24920},"flug-173-von-portland-oregon","Flug 173 von Portland, Oregon",[439,24923,24924],{},"Melburn McBroom war ein launenhafter Chef. Seine Mitarbeiter fühlten sich durch ihn eingeschüchtert. Dieses angespannte\nVerhältnis zwischen Vorgesetzten und Mitarbeitern hätte in einem klassischen Betrieb wohl kaum spürbare Folgen.\nVermutlich wäre die Stimmung schlecht und die Produktivität würde leiden. Jedoch in der Situation von Melburn McBroom\nkam es 1978 in Portland, Oregon, zu einer Katastrophe. Denn McBroom war als einer der erfahrensten Flugkapitäne der\nUnited Airlines des Fluges 173 mit seiner ebenfalls erfahrenen Crew von New York nach Portland unterwegs. Zum Zeitpunkt\ndes Abflugs waren in der Douglas DC-8 189 Personen an Board. Beim Anflug des Zielflughafens in Oregon wurde im Flugzeug\nein lauter Schlag vernommen. Es folgten Vibrationen und Bewegungen um die vertikale Achse. Um dem Problem auf die Spur\ngehen zu können, wurde der Anflug vorerst abgebrochen. Die Crew fand derweilen bei der Ursachenforschung heraus, dass\nKomplikationen beim Ausfahren des rechten Fahrwerks aufgetreten waren. Denn eine grüne Kontrollleuchte hätte das\nkorrekte Ausfahren des rechten Fahrwerks im Cockpit bestätigen müssen. Während sich die Crew mit dem Tower darüber\nberaten hatte, welche Maßnahmen getroffen werden müssten, um das Problem mit dem Fahrwerk zu beheben, bemerkte die Crew,\ndass eine Landung wegen Treibstoffmangels unausweichlich werden würde. Daraufhin wurden die Passagiere auf eine\nNotlandung vorbereitet und der Landeanflug eingeleitet. Der Treibstoffmangel führte jedoch dazu, dass mehrere Turbinen\nausfielen und das Flugzeug nicht mehr in der Lage war die Landebahn zu erreichen. In letzter Konsequenz stürzte das\nFlugzeug in ein nahegelegenes Waldstück von Portland ab. Bei diesem Unglück starben 10 Menschen.",[439,24926,24927],{},"Im Anschluss an die Untersuchung des Unfallhergangs stellte sich heraus, dass der Mechanismus für das Einfahren des\nFahrwerks durch Korrosion beschädigt war. Aus Sicherheitsgründen wird bei solch einer Fehlfunktion ein Freifall des\nFahrwerks automatisch eingeleitet, um bei einer Landung zur Verfügung zu stehen. Dieser Freifall verursachte auch den\nlauten Schlag, der im Flugzeug gehört werden konnte. Unglücklicherweise ist dabei aber der Sensor beschädigt worden, der\nein grünes Aufleuchten der Kontrollleuchte zur Bestätigung des ausgefahrenen Fahrwerks im Cockpit dienen sollte. Die\nCrew ging somit davon aus, dass ihnen zur Landung das rechte Fahrwerk fehlen würde. Tatsächlich führte jedoch das\nausgefahrene Fahrwerk zu einem erhöhten Luftwiderstand, welcher den Treibstoffverbrauch stark ansteigen ließ. Weder das\nFahrwerk noch der Treibstoffverbrauch hätten zum Absturz führen müssen. Denn die Crew machte den Kapitän McBroom darauf\naufmerksam, dass der Treibstoff rapide abgenommen hatte. Dieser war jedoch so sehr vertieft, die Störung zu beheben,\ndass er auf die Ratschläge der Crew nicht reagierte. Die Crew konnte sich trotz erkanntem Risiko nicht gegen den\nlaunenhaften Kapitän durchsetzen.",[439,24929,24930],{},"Letztlich war der Absturz die Folge aus einer schlechten Zusammenarbeit, unter erschwerten Bedingungen, zwischen der\nCrew und dem Kapitän, nicht jedoch die technischen bzw. fliegerischen Fähigkeiten der Beteiligten. Die internationale\nLuftfahrt zog Konsequenzen aus diesem Unglück. Daraufhin wurde die Ausbildung für Flugkapitäne weitreichend verändert.\nEin neuer Standard, Crew Resource Management (CRM), wurde eingeführt. Hierbei liegt der Fokus auf die nicht-technischen\nFertigkeiten (Kooperation, Führungsverhalten, Entscheidungsfindung, Kommunikation) der gesamten Crew. Diese sollen\nverbessert werden, um Flugunfälle aufgrund menschlichen Versagens vorzubeugen.",[439,24932,24933],{},"Welche Lehren lassen sich aus diesem Beispiel für andere berufliche Kontexte ziehen?",[3938,24935,24937],{"id":24936},"lehren-aus-dem-flugzeugabsturz","Lehren aus dem Flugzeugabsturz",[439,24939,24940],{},"Aus beruflicher Sicht gehören Konflikte, Missverständnisse und Probleme zum normalen Alltag. Selten erfahren sie jedoch\nso eine dramatische Realitätsprüfung wie in dem obigen Beispiel. Die Auswirkung sind meist subtiler. Sie zeigen sich\nanfangs durch eine schlechte Stimmung oder fehlendes Engagement im Unternehmen. Mitarbeiter sind nicht mehr bereit\nzusätzliche Energie in die Arbeit zu investieren. Die Kompromissbereitschaft sinkt. Wird dafür kein Bewusstsein\ngeschaffen und in die Kommunikation gebracht, beginnt die Abwärtsspirale. Durch das fehlende Engagement geht die\nMotivation völlig verloren, es häufen sich Fehler und die Performance von Mitarbeitern und Teams nimmt ab. Werden auch\ndiese Warnsignale missachtet, könnten Kundenbeziehungen stark leiden, der Produktabsatz fallen und Mitarbeiter\nkündigen (Goleman 1997). Aber soweit muss es nicht kommen. Wie auch in der Geschichte vom Flug 173, lohnt es sich, wenn\ndie Kommunikation mehr in den Mittelpunkt rückt. Dies lässt sich zum Einen dadurch ermöglichen, dass Bewusstsein über\nKommunikation zu einem festen Bestandteil in Ausbildungen wird. Zum Anderen indem wichtige Aspekte der Kommunikation im\nberuflichen Alltag mehr Raum bekommen. Kommunikation lebt nicht nur durch den Austausch von Sachinformationen oder\nFakten. Jeder Kommunikation haftet eine Beziehungsebene an (Schulz von Thun 1981, Watzlawick 1969). Diese sagt etwas\nüber das Verhältnis zwischen zwei Menschen aus, wie sie zueinander stehen, was zwischen ihnen erlaubt ist und was nicht.\nIm täglichen Umgang steht jedoch der Sachinhalt im Vordergrund. Zahlreiche Meetings und Diskussionen finden über\nfachliche Themen statt. Die Beziehungsebene wird dabei oftmals ausgeblendet oder tabuisiert, obwohl sie immer Teil der\nKommunikation ist. Sie wird häufig als “der Elefant im Raum”, über den keiner sprechen will, wahrgenommen oder entlädt\nihre Energie in offenen Konflikten. Wird die Beziehungsebene jedoch als Teil der Kommunikation wertgeschätzt und in\ndiese integriert, kann dieser Zwickmühle begegnet werden. Am einfachsten, indem gezielt über die Beziehungsebene\ngesprochen wird. Hierfür kann ein regelmäßiges, moderiertes Meeting in Form von Metakommunikation oder einer\nRetrospektive nützlich sein. Die Sinnhaftigkeit ergibt sich aus den Inhalten: Konflikte klären und proaktiv die\nZusammenarbeit verbessern.",[439,24942,24943],{},"Dies ist eine Möglichkeit die Kommunikation im Unternehmen zu pflegen. Eine weitere ist mit einer kulturellen\nVeränderung verbunden. Hierfür bedarf es einer zusätzlichen Rolle im Unternehmen, die sich mit viel Begeisterung\nlangfristig für eine aktive Entwicklung zum offenen und ehrlichen Dialog einsetzt. Diese Rolle muss sich folglich um die\nMeetingkultur (respektvoller Umgang im gemeinsamen Dialog) kümmern, bei Konflikten unterstützen, ermutigen, Fehler als\nLernfelder wahrnehmen, Bewusstsein für unterschiedliche Bedürfnisse schaffen und ein Miteinander vorleben.",[3938,24945,24947],{"id":24946},"zu-guter-letzt","Zu guter Letzt…",[439,24949,24950],{},"Es gibt zahlreiche Wege dafür zu sorgen, dass Kommunikation nicht zum Mittel zum Zweck verkommt. Aus meiner Sicht führt\njedoch kein Weg daran vorbei, die Zusammenarbeit und Beziehungsebene regelmäßig zu reflektieren. Der kulturelle Wandel,\nder mit einer neuen Erfahrung durch ein offenes und ehrliches Miteinander einhergeht, benötigt seine Zeit. Während\ndieser Entwicklung empfehle ich, Mitarbeiter immer wieder einzuladen und zu ermutigen diese neue Erfahrung zu machen.\nDadurch kann es gelingen, dass neue Verhaltens- und Kommunikationsmuster entstehen, die ein sinnvolles Diskutieren auf\nder Beziehungsebene erlauben. Zum Abschluss möchte ich darauf hinweisen, dass keine dieser Möglichkeiten die Lösung\naller Probleme darstellt. Konflikte, Missverständnisse oder Probleme werden deswegen nicht ausbleiben. Aber der Umgang\nmiteinander wird menschlicher. Tief bleibende Verletzungen und innere Kündigungen können somit reduziert werden.",[439,24952,24953],{},"PS: Das CRM-Training ist mittlerweile gesetzlich vorgeschrieben und muss regelmäßig aufgefrischt werden. Das CRM kommt\nbereits auch außerhalb der Luftfahrt zum Einsatz, zum Beispiel in der Medizin um Behandlungsfehler zu vermeiden.\nBesonders betonen möchte ich den Umstand, dass bei der Mehrzahl der Unfalluntersuchungen von Flugzeugabstürzen\nfestgestellt wurde, dass meist nicht ein Mangel an fachlicher Kompetenz, sondern ein Mangel an sozialer Kompetenz\nvorherrschend war. Zitierte Quellen und weiterführende Literatur:",[994,24955,24956,24959,24967,24970,24973],{},[997,24957,24958],{},"Paul Watzlawick, Janet H. Beavin, Don D. Jackson: Menschliche Kommunikation – Formen, Störungen, Paradoxien. Huber,\nBern 1969 (12. unveränd. Aufl. 2011), ISBN 3456834578",[997,24960,24961,24962],{},"The Nation; When Moods Affect Safety: Communication in a Cockpit Means a Lot a Few Miles Up, The New York Times\n1994,",[1002,24963,24966],{"href":24964,"rel":24965},"http://www.nytimes.com/1994/06/26/weekinreview/nation-when-moods-affect-safety-communication-cockpit-means-lot-few-miles-up.html",[1006],"(http://www.nytimes.com/1994/06/26/weekinreview/nation-when-moods-affect-safety-communication-cockpit-means-lot-few-miles-up.html)",[997,24968,24969],{},"Daniel Goleman: EQ. Emotionale Intelligenz. Deutscher Taschenbuch Verlag 1997, ISBN 3-423-36020-8",[997,24971,24972],{},"Friedemann Schulz von Thun: Miteinander reden: Störungen und Klärungen. Psychologie der zwischenmenschlichen\nKommunikation. Rowohlt, Reinbek 1981, ISBN 3-499-17489-8.",[997,24974,24975,24976],{},"Informationen zum Crew Resource Management Training (CRM): ",[1002,24977,24978],{"href":24978,"rel":24979},"http://www.crewresourcemanagement.net",[1006],{"title":469,"searchDepth":507,"depth":507,"links":24981},[24982,24983,24984],{"id":24920,"depth":507,"text":24921},{"id":24936,"depth":507,"text":24937},{"id":24946,"depth":507,"text":24947},[1031],"2017-07-28T14:19:08","Kommunikation ist allgegenwärtig und findet zu jeder Zeit statt. Schon Paul Watzlawick formulierte 1969 das bekannte\\nAxiom: “Man kann nicht nicht kommunizieren.” Er macht dadurch deutlich, dass sich die Kommunikation wie die Luft zum\\nAtmen verhält – sie umgibt uns überall, ist unausweichlich und essentiell zum Leben. Jeden Tag aufs Neue, sowohl privat\\nals auch beruflich, wird uns dieser Umstand bewusst. Doch was passiert mit der Kommunikation, wenn…","https://synyx.de/blog/kommunikation-im-realitaetscheck/",{},"/blog/kommunikation-im-realitaetscheck",{"title":24894,"description":24903},"kommunikation-im-realitaetscheck","blog/kommunikation-im-realitaetscheck",[],"Kommunikation ist allgegenwärtig und findet zu jeder Zeit statt. Schon Paul Watzlawick formulierte 1969 das bekannte Axiom: “Man kann nicht nicht kommunizieren.” Er macht dadurch deutlich, dass sich die Kommunikation wie die Luft zum Atmen verhält - sie umgibt uns überall, ist unausweichlich und essentiell zum Leben. Jeden Tag aufs Neue, sowohl privat als auch beruflich, wird uns dieser Umstand bewusst. Doch was passiert mit der Kommunikation, wenn… …sie nur noch als Mittel zum Zweck dient?","WujAolrUeRFdVjQz791dfVh3bHlZE7FPBDvMAmXAZSE",{"id":24998,"title":24999,"author":25000,"body":25001,"category":25114,"date":25115,"description":25008,"extension":1034,"link":25116,"meta":25117,"navigation":916,"path":25118,"seo":25119,"slug":25005,"stem":25120,"tags":25121,"teaser":25122,"__hash__":25123},"blog/blog/decentralized-team-passwords-with-keepass-and-syncthing.md","Decentralized team passwords with Keepass and Syncthing",[208],{"type":432,"value":25002,"toc":25108},[25003,25006,25009,25012,25015,25018,25038,25041,25045,25052,25061,25065,25072,25075,25078,25082,25089,25092,25098,25102,25105],[435,25004,24999],{"id":25005},"decentralized-team-passwords-with-keepass-and-syncthing",[439,25007,25008],{},"At synyx we constantly try to improve the quality of the work of the Operations team.",[439,25010,25011],{},"We found we were missing certain security requirements. Although we were already using ssh-key-authentication for all\nour linux-servers, we still had some devices and assets lying around, that were (only) accessible with the\nvendor-assigned default credentials.",[439,25013,25014],{},"Sadly it’s impossible to use key-authentication for all devices and some appliances don’t allow for creation of\nmultiple personalized accounts with admin rights. Sometimes it can also come in handy to have a user/password fallback\nin case you can’t access the machine with your ssh keys. Therefore we decided we could improve our security by replacing\nthe default credentials with strong passwords that won’t be reused. Since those passwords would have to be known to 7\npeople, we had to find a way to share them without the need to manually update everytime someone provided or changed any\ndata.",[439,25016,25017],{},"We evaluated multiple tools under certain criteria:",[994,25019,25020,25023,25026,25029,25032,25035],{},[997,25021,25022],{},"encrypted password container",[997,25024,25025],{},"automatic sync on updates from others",[997,25027,25028],{},"simple configuration",[997,25030,25031],{},"decentralized sharing (no single point of failure)",[997,25033,25034],{},"Open Source software preferred",[997,25036,25037],{},"run on Linux, MacOS and probably Windows",[439,25039,25040],{},"We came up with a solution involving a keepass2 password container and Syncthing, a decentralized file sync framework.",[1065,25042,25044],{"id":25043},"keepass","Keepass",[439,25046,25047,25051],{},[1002,25048,25044],{"href":25049,"rel":25050},"http://keepass.info",[1006]," is an encrypted database for storing sensitive information like\nuser/password-credentials and notes.",[439,25053,25054,25055,25060],{},"We quickly figured that we’d have to use a keepass2-compatible container to allow usage on all required OSs. Debian and\nUbuntu offer official packages for keepass2, MacOS users can use ",[1002,25056,25059],{"href":25057,"rel":25058},"https://github.com/keepassx/keepassx",[1006],"keepassX",", a\nport also available for Debian/Ubuntu. Aside of the client choice we run a pretty “standard” container with a single\nmaster password known to all admins. As of today, keepass containers can be encrypted with a (single) key. Admins can be\ngiven this key instead or in addition to the master password. Make sure you never share the key with the container!",[1065,25062,25064],{"id":25063},"syncthing","Syncthing",[439,25066,25067,25071],{},[1002,25068,25064],{"href":25069,"rel":25070},"https://syncthing.net",[1006]," is a tool that allows you to share and synchronize files with multiple devices that\nform a cluster. All traffic between devices is encrypted with TLS1.2. It is typically installed as a service on every\ndevice. You can add other devices by specific ID. Once a device comes online it broadcasts a discovery announcement\npackage on the local network (multicast on ipv6). Devices that have been “introduced by Device-ID” authenticate to\neach other to allow index comparison and possible updates. To allow for synchronization outside of the local network,\nits is possible to set up global device search through a discovery server. To relay actual traffic (syncing files) you\ncan setup a syncthing relay-server or use existing servers. It is also possible to run and connect to other syncthing\ndevices through SOCKS5 proxies.",[439,25073,25074],{},"Syncthing also allows you to keep a history of changes, so you have a fallback if someone accidently breaks the\ncontainer.",[439,25076,25077],{},"Devices can be configured to be an “introducer”. New devices added to an introducer get shared to the whole group of\nlinked devices. This prevents you from adding all new Device-IDs manually.",[1065,25079,25081],{"id":25080},"our-setup","Our setup",[439,25083,25084,25085],{},"We manage syncthing and keepass installations through puppet, and also provision the admin cluster config with all the\nDevice-IDs automatically. There are two utility servers in the cluster that are always on to provide the share in case\nno admin laptop is reachable and someone misses updates. We also run an instance in the Datacenter in case an admin\nforgets his laptop ",[2205,25086],{"alt":25087,"src":25088},"trollface","https://synyx.de/assets/blog/trollface_logo.png",[439,25090,25091],{},"I tried to visualize the setup here, with every device being able to talk to every other device that has been added to\nthe cluster.",[439,25093,25094],{},[2205,25095],{"alt":25096,"src":25097},"relay and discovery servers Erklärung","https://media.synyx.de/uploads/2019/04/syncthing-768x407.png",[1065,25099,25101],{"id":25100},"conclusions","Conclusions",[439,25103,25104],{},"The setup is really easy to create and test. Syncthing works out of the box and brings an easy-to-use web-GUI in case\nyou just want to test and not do any fancy configmanagement. It’s not only useful to share the keepass container but can\nalso be used to share any file that you want “offline” on your computer, like contingency plan documentation, cheat\nsheets and a cat pictures.",[439,25106,25107],{},"You should run syncthing in the background so your container gets updated immediately after someone changes something or\nadds new information. Everyone’s keepass app should be kept up to date to avoid version mismatches. You don’t have time\nto fix that during emergency when you need the container.",{"title":469,"searchDepth":507,"depth":507,"links":25109},[25110,25111,25112,25113],{"id":25043,"depth":547,"text":25044},{"id":25063,"depth":547,"text":25064},{"id":25080,"depth":547,"text":25081},{"id":25100,"depth":547,"text":25101},[9045,1031],"2017-07-12T14:22:34","https://synyx.de/blog/decentralized-team-passwords-with-keepass-and-syncthing/",{},"/blog/decentralized-team-passwords-with-keepass-and-syncthing",{"title":24999,"description":25008},"blog/decentralized-team-passwords-with-keepass-and-syncthing",[],"At synyx we constantly try to improve the quality of the work of the Operations team. We found we were missing certain security requirements. Although we were already using ssh-key-authentication for all our linux-servers, we still had some devices and assets lying around, that were (only) accessible with the vendor-assigned default credentials. Sadly it’s impossible to use key-authentication for all devices and some appliances don’t allow for creation of multiple personalized accounts with admin rights.","nTgDYDlTDuKaa9v6JclHpy2buRG7rfQlVJteioy0LKs",{"id":25125,"title":25126,"author":25127,"body":25128,"category":25246,"date":25247,"description":25248,"extension":1034,"link":25249,"meta":25250,"navigation":916,"path":25251,"seo":25252,"slug":25132,"stem":25253,"tags":25254,"teaser":25255,"__hash__":25256},"blog/blog/software-zum-anfassen-ein-nachmittag-in-der-ideenschmiede.md","Software zum Anfassen – Ein Nachmittag in der Ideenschmiede",[202],{"type":432,"value":25129,"toc":25244},[25130,25133,25136,25139,25142,25148,25154,25160,25163,25166,25169,25175,25181,25186,25189,25192,25197,25203,25208,25213,25216,25219,25224,25229,25238,25241],[435,25131,25126],{"id":25132},"software-zum-anfassen-ein-nachmittag-in-der-ideenschmiede",[439,25134,25135],{},"Am Mittwoch den 22. März durften wir bei synyx einen ganzen Bus voller Kunden zu einer Art internen Hausmesse begrüßen.\nWir hatten uns für diesen Tag vorgenommen, einmal ganz plastisch und “begreifbar” die Software-Entwicklung und die von\nuns für den Kunden realisierten Projekte zu zeigen.",[439,25137,25138],{},"Seit über 6 Jahren arbeiten wir nun schon für die Contargo GmbH & Co. KG als IT-Dienstleister. In dieser Zeit sind\ngemeinsam jede Menge Tools und Softwareprojekte entstanden, welche die tägliche Arbeit auf einem Container Terminal\nunterstützen. Es war nun an der Zeit all das nun einmal im großen Zusammenspiel und wirklich zum Anfassen zu zeigen.",[439,25140,25141],{},"Aufgeteilt in handliche Gruppen und mit einem persönlichen Betreuer versehen wurde bei uns im synyx Büro einmal ein\nkompletter Überblick der Contargo Softwarelösungen vorgestellt, an denen synyx mitarbeiten durfte. In sechs\nverschiendenen Stationen wurde viel gelacht und viele wissenswerte Informationen an die Frau bzw. an den Mann gebracht.",[439,25143,25144],{},[2205,25145],{"alt":25146,"src":25147},"Software zum Anfassen","https://media.synyx.de/uploads/2019/04/SWZA1.jpg",[439,25149,25150],{},[2205,25151],{"alt":25152,"src":25153},"Unterhaltung im Synyx Wohnzimmer","https://media.synyx.de/uploads/2019/04/SWZA2.jpg",[439,25155,25156],{},[2205,25157],{"alt":25158,"src":25159},"Billardecke Heiterkeit","https://media.synyx.de/uploads/2019/04/SWZA3.jpg",[439,25161,25162],{},"Man konnte den Ehrgeiz unserer Teams spüren, mit welchem sie Tag für Tag an Software für die Contargo arbeiten und es\ngab auf Seiten unserer Besucher auch den ein oder anderen AHA Effekt. Dieser Nachmittag hat sichtlich dazu beigetragen,\nein umfassenderes gegenseitiges Verständnis zwischen Kunde und Dienstleister zu erzeugen.",[439,25164,25165],{},"Beginnend mit der großen Contargo-System-Vision, also dem “Wo wollen wir hin?” wurden anhand eines riesigen über 6m\nlangen Plots die einzelnen Subsysteme dargestellt und die Verbindungen und Schnittstellen anhand der Prozesse\nvisualisiert. So konnte ein umfassendes “Big Picture” etabliert werden, welches danach anhand einzelner ausgewählten\nSysteme verfeinert wurde.",[439,25167,25168],{},"Da wir Software ausschliesslich mit agilen Entwicklungsmethodiken erstellen, war der logische nächste Stop ein kurzer\nVortrag über Agilität, die verschiedenen Projektrollen und wie das alles zusammenspielt in der täglichen Arbeit für die\nContargo.",[439,25170,25171],{},[2205,25172],{"alt":25173,"src":25174},"Logistik Modell","https://media.synyx.de/uploads/2019/04/SWZA5.jpg",[439,25176,25177],{},[2205,25178],{"alt":25179,"src":25180},"Contargo Terminal","https://media.synyx.de/uploads/2019/04/SWZA6.jpg",[439,25182,25183],{},[2205,25184],{"alt":25146,"src":25185},"https://media.synyx.de/uploads/2019/04/SWZA4.jpg",[439,25187,25188],{},"Im der dritten Session gab es dann auch endlich richtig was zum Anfassen. Hier wurde die Ankunft an einem Terminal\nsimuliert und die dann anlaufenden Prozesse und Arbeitsschritte visualisiert. Hier bekamen die Teilnehmer dann auch\neinen richtigen Container (im H0 Format) in die Hand und durften diesen Container über ein Self Checkin Terminal selbst\nanmelden.",[439,25190,25191],{},"In der vierten Session war dann unser Sahnestückchen des Nachmittags versteckt, das detailgetreu nachgebaute Cosynux\nContainer Terminal mit Zug- Binnenschiff- und LKW-Anbindung. Hier wurde der komplette Prozess der Anlieferung eines\nContainers über die Abholung eines leeren Containers bis hin zur Verladung auf ein Binnenschiff von Hand nachgespielt\nund dabei die Unterstützung des gesamten Ablaufes durch die Software präsentiert. Das Containerstapeln hat allen\nBeteiligten sichtlich Spaß gemacht und hier wurde auch sehr viel gelacht.",[439,25193,25194],{},[2205,25195],{"alt":25173,"src":25196},"https://media.synyx.de/uploads/2019/04/SWZA7.jpg",[439,25198,25199],{},[2205,25200],{"alt":25201,"src":25202},"Begutachtung Logistikmodell","https://media.synyx.de/uploads/2019/04/SWZA8.jpg",[439,25204,25205],{},[2205,25206],{"alt":25173,"src":25207},"https://media.synyx.de/uploads/2019/04/SWZA9.jpg",[439,25209,25210],{},[2205,25211],{"alt":25146,"src":25212},"https://media.synyx.de/uploads/2019/04/SWZA10-768x512.jpg",[439,25214,25215],{},"Nach einem kurzen Stop an unserem Billard-Tisch und unserer Diner-Ecke wurde dann das Contargo Real-Time Monitoring\neinzelner Produktivsysteme gezeigt. Das beeindruckende 6-Monitor Setup gibt nicht nur einen guten Überblick über alle\nwichtigen Kennwerte in Echtzeit, sondern erlaubt es auch, frühzeitig auf entstehende Probleme einzugehen und diese zu\nbeseitigen, bevor sie zu ernsten Problemen werden.",[439,25217,25218],{},"Weil Software nur Spaß macht, wenn sie nicht nur gut aussieht, sondern sich auch innovativ und umkompliziert bedienen\nlässt, ist UX Driven Design eine tolle Sache. Warum das auch die Contargo so sieht und wie alle Tools über einen\ngemeinschaftlichen Anwendungs-Rahmen zusammen passen wurde von unserem User-Experience Evangelist sehr anschaulich\nerläutert.",[439,25220,25221],{},[2205,25222],{"alt":25146,"src":25223},"https://media.synyx.de/uploads/2019/04/SWZA11.jpg",[439,25225,25226],{},[2205,25227],{"alt":25146,"src":25228},"https://media.synyx.de/uploads/2019/04/SWZA12.jpg",[439,25230,25231,25232,25237],{},"Die letzte Station befasste sich mit Routings und Preisfindung. Das von Contargo frei zur Verfügung gestellte\nIRIS ",[1002,25233,25236],{"href":25234,"rel":25235},"https://github.com/Contargo/iris/",[1006],"(https://github.com/Contargo/iris)"," findet den besten Weg für einen Container\nzu seinem Zielort. Ein flexibles Preismodell rundet das ganze ab, so dass am Ende des Weges nicht nur der Container beim\nKunden steht, sondern auch eine optimale Abrechnung gewähleistet ist.",[439,25239,25240],{},"Da es viele interessierte Rückfragen von den Besuchern gab, ging die ganze Präsentation ein wenig länger als geplant und\nam Ende waren auch alle ganz schön geschafft, aber glücklich. Wir synyxer, dass wir es geschafft haben, einen gut\nverständlichen Überblick über unsere Arbeitsweise zu bieten und die Contargianer, dass sie die Möglichkeit hatten, mal\nganz tief in die Entstehung Ihrer Software rein zu schauen.",[439,25242,25243],{},"Als Fazit bleibt ganz viel gegenseitiges Verständnis und ein gutes Gefühl, einen verlässichen Partner an der Seite zu\nhaben. Danke liebe Contargo und auf die nächsten sechs Jahre!",{"title":469,"searchDepth":507,"depth":507,"links":25245},[],[1031],"2017-06-13T14:39:40","Am Mittwoch den 22. März durften wir bei synyx einen ganzen Bus voller Kunden zu einer Art internen Hausmesse begrüßen.\\nWir hatten uns für diesen Tag vorgenommen, einmal ganz plastisch und “begreifbar” die Software-Entwicklung und die von\\nuns für den Kunden realisierten Projekte zu zeigen.","https://synyx.de/blog/software-zum-anfassen-ein-nachmittag-in-der-ideenschmiede/",{},"/blog/software-zum-anfassen-ein-nachmittag-in-der-ideenschmiede",{"title":25126,"description":25135},"blog/software-zum-anfassen-ein-nachmittag-in-der-ideenschmiede",[],"Am Mittwoch den 22. März durften wir bei synyx einen ganzen Bus voller Kunden zu einer Art internen Hausmesse begrüßen. Wir hatten uns für diesen Tag vorgenommen, einmal ganz plastisch und “begreifbar” die Software-Entwicklung und die von uns für den Kunden realisierten Projekte zu zeigen.","NJYQCiEuvIJfcWnBSo5q11LVww-25N3MZgwhj4Vdvgs",{"id":25258,"title":25259,"author":25260,"body":25261,"category":25676,"date":25677,"description":25678,"extension":1034,"link":25679,"meta":25680,"navigation":916,"path":25681,"seo":25682,"slug":25265,"stem":25683,"tags":25684,"teaser":25689,"__hash__":25690},"blog/blog/the-struggle-with-hazelcast-queue-persistence.md","The struggle with Hazelcast queue persistence",[97],{"type":432,"value":25262,"toc":25663},[25263,25266,25269,25273,25276,25284,25287,25304,25319,25325,25329,25337,25340,25343,25347,25350,25353,25430,25434,25437,25440,25443,25446,25455,25459,25462,25473,25478,25481,25484,25488,25491,25577,25581,25584,25587,25590,25593,25597,25600,25603,25618,25621,25625,25628,25637,25641,25644,25647,25661],[435,25264,25259],{"id":25265},"the-struggle-with-hazelcast-queue-persistence",[439,25267,25268],{},"In this blog I will outline why we used Hazelcast for queueing messages in-memory distributed over a cluster and how we\nachieved higher resilience by persisting the queue’s content. I will explain the pitfalls and difficulties that we\nencountered and how I constantly switched between praising and condemning Hazelcast.",[3938,25270,25272],{"id":25271},"the-problem-to-solve","The problem to solve",[439,25274,25275],{},"I’m currently working in a project for a large customer data backend. The prod system consists of a load balanced\ncluster of five VMs each running two Tomcat instances hosting our application. The deployment process performs an A/B\nswitching between the Tomcats on each node to achieve zero downtime. The application has to handle a lot of incoming\ndata and updates and communicates with a lot of external services. At one point we felt the need for a queueing\nmechanism for two reasons:",[8310,25277,25278,25281],{},[997,25279,25280],{},"Enabling controlled asynchronous processing of tasks inside the application. Example: A synchronous user request\nqueues follow-up tasks to be processed later by another part of the application so the request can deliver the\nresponse quicker to the user.",[997,25282,25283],{},"Queueing and retrying failed calls to external systems for higher resilience",[439,25285,25286],{},"We gathered the following core requirements for the queueing mechanism:",[994,25288,25289,25292,25295,25298,25301],{},[997,25290,25291],{},"Embedded into the application. Using a potentially failing external system would defeat reason 2",[997,25293,25294],{},"Distributed over the cluster. Due to the nature of our data import mechanism one node creates a lot of tasks and the\ncluster should work together to process the tasks.",[997,25296,25297],{},"Resistant to system failure. The queued data is critical – so when one node or even the whole cluster goes down the\ndata should be preserved",[997,25299,25300],{},"Performance. Due to the amount of processed data the solution has to be fast.",[997,25302,25303],{},"Low complexity and easy maintainability. “Keep it simple” is a key ambition for everything that we use or build.",[439,25305,25306,25307,25312,25313,25318],{},"After a short evaluation phase these requirements led us to the conclusion that ",[1002,25308,25311],{"href":25309,"rel":25310},"https://hazelcast.org/",[1006],"Hazelcast","\nmight be the solution of our problem. It can be embedded as library, its core feature is distributed data structures\nlike maps and queues, it is known to be lightning fast and easy to use. Also it offers backup- and recovery mechanisms\nas well as the possibility to implement persistence for the data structures. And\nit’s ",[1002,25314,25317],{"href":25315,"rel":25316},"https://github.com/hazelcast/hazelcast",[1006],"open source",", yay!",[439,25320,25321],{},[2205,25322],{"alt":25323,"src":25324},"Logo hazelcast","https://media.synyx.de/uploads/2019/04/hazelcast_logo_small-768x185.png",[3938,25326,25328],{"id":25327},"the-easy-part-divide-and-queue","The easy part – divide and queue",[439,25330,25331,25332,25336],{},"The first implementation of the Hazelcast queue in our application was a piece of cake. Following\nthe ",[1002,25333,22273],{"href":25334,"rel":25335},"http://docs.hazelcast.org/docs/3.8.2/manual/html-single/index.html",[1006]," we only needed two dependencies\nin our pom.xml, some properties in our application config and one Spring config class and voilà: The distributed\nin-memory queue was ready to use in the code just like every other Java BlockingQueue implementation.",[439,25338,25339],{},"In the first tests we realized how great Hazelcast works. Every queued item was available on all nodes in an instant and\nwe could shut down and restart nodes at will without losing data. The only thing that was a bit trickier was to get\nHazelcast’s network configuration right so the cluster finds its nodes during an A/B deployment without adding nodes\nthat should NOT belong to the cluster.",[439,25341,25342],{},"I will not go into detail on this “easy part” because this blog post should concentrate on the difficulties. All in all\nwe were in awe of Hazelcast’s smoothness at this point.",[3938,25344,25346],{"id":25345},"the-hard-part-persist-the-shit-out-of-it","The hard part – persist the shit out of it",[439,25348,25349],{},"So far, so good. We already managed to meet 90% of our requirements. The last 10% should’t be that difficult, right?\nPfffff let’s just do it!",[439,25351,25352],{},"We wanted to make the data resilient against the improbable event of an outage of the whole cluster. So the data in the\ncluster should be backed up in some kind of persistence and be recovered when the cluster reboots. Hazelcast offers an\nabstract solution for this problem, namely the QueueStore interface. You can implement the interface with every\npersisting technology that you want, add some configuration and all queued data will be mirrored into the data store and\nbe recovered after an eventual downtime.",[464,25354,25356],{"className":709,"code":25355,"language":711,"meta":469,"style":469},"public interface QueueStore\u003CT> {\n\n void store(Long key, T value);\n\n void storeAll(Map\u003CLong, T> map);\n\n void delete(Long key);\n\n void deleteAll(Collection\u003CLong> keys);\n\n T load(Long key);\n\n Map\u003CLong, T> loadAll(Collection\u003CLong> keys);\n\n Set\u003CLong> loadAllKeys();\n}\n",[471,25357,25358,25363,25367,25372,25376,25381,25385,25390,25394,25399,25403,25408,25412,25417,25421,25426],{"__ignoreMap":469},[474,25359,25360],{"class":476,"line":477},[474,25361,25362],{},"public interface QueueStore\u003CT> {\n",[474,25364,25365],{"class":476,"line":507},[474,25366,917],{"emptyLinePlaceholder":916},[474,25368,25369],{"class":476,"line":547},[474,25370,25371],{}," void store(Long key, T value);\n",[474,25373,25374],{"class":476,"line":584},[474,25375,917],{"emptyLinePlaceholder":916},[474,25377,25378],{"class":476,"line":607},[474,25379,25380],{}," void storeAll(Map\u003CLong, T> map);\n",[474,25382,25383],{"class":476,"line":642},[474,25384,917],{"emptyLinePlaceholder":916},[474,25386,25387],{"class":476,"line":663},[474,25388,25389],{}," void delete(Long key);\n",[474,25391,25392],{"class":476,"line":694},[474,25393,917],{"emptyLinePlaceholder":916},[474,25395,25396],{"class":476,"line":700},[474,25397,25398],{}," void deleteAll(Collection\u003CLong> keys);\n",[474,25400,25401],{"class":476,"line":913},[474,25402,917],{"emptyLinePlaceholder":916},[474,25404,25405],{"class":476,"line":920},[474,25406,25407],{}," T load(Long key);\n",[474,25409,25410],{"class":476,"line":926},[474,25411,917],{"emptyLinePlaceholder":916},[474,25413,25414],{"class":476,"line":932},[474,25415,25416],{}," Map\u003CLong, T> loadAll(Collection\u003CLong> keys);\n",[474,25418,25419],{"class":476,"line":938},[474,25420,917],{"emptyLinePlaceholder":916},[474,25422,25423],{"class":476,"line":944},[474,25424,25425],{}," Set\u003CLong> loadAllKeys();\n",[474,25427,25428],{"class":476,"line":950},[474,25429,703],{},[1065,25431,25433],{"id":25432},"difficulty-1-how-to-persist","Difficulty #1: How to persist?",[439,25435,25436],{},"After the initial euphoria it began to dawn on us that implementing the Queue Store interface obviously meant choosing\nsome technology to persist data (d’uh). Unfortunately Hazelcast does not offer some kind of default implementation that\nyou just roll with if you want to try it out.",[439,25438,25439],{},"Well ok, how about our database? We did not want to do that at this time. We expected it to be slow and our project has\na history of database-managed queues that didn’t work that well.",[439,25441,25442],{},"The next thing that came in mind was the file system of our application servers. This actually seemed like a viable\nsolution as the queue entries passed to the QueueStore interface are in key-value format and there already are several\nlibraries providing a file-based key-value store.",[439,25444,25445],{},"So the evaluation train departed again and passed several solutions capable of storing key-value pairs in files like\nBerkeley DB, Map DB, Banana DB(!?) and some others.",[439,25447,25448,25449,25454],{},"In the end the train stopped at ",[1002,25450,25453],{"href":25451,"rel":25452},"https://github.com/OpenHFT/Chronicle-Map",[1006],"ChronicleMap",", an off-heap in-memory map\nthat is mirrored to a file and promises consistency and insane speed. The embedded library is developed by a team of\nprofessionals and supports file access by multiple JVM instances at the same time, which is crucial for our A/B\ndeployment. Long story short: We implemented the ChronicleMap QueueStore and the first local tests delivered the desired\nresults: A Hazelcast cluster with a huge amount of queued data got shutdown completely and restarted again and the data\nwas still there!",[1065,25456,25458],{"id":25457},"obstacle-2-i-am-the-persistence-master","Obstacle #2: I am the persistence Master!",[439,25460,25461],{},"The first test on a production-like system with a cluster of multiple VMs seemed promising. After every simulated\ncluster downtime the data was still there. But looking closely at the files written by ChronicleMap we noticed a strange\nthing. Only on one node of the cluster the file size changed, on the other nodes the files got created but stayed on the\nsame size of only a few KB. What was the meaning of this? Why are not all nodes backing up their data? And how was it\npossible for them to recover their data without the file backup?",[439,25463,25464,25465,25468,25469,25472],{},"After some more research we discovered a sentence in the Hazelcast documentation of the ",[448,25466,25467],{},"map"," persistence that was\nmissing in the documentation of the ",[448,25470,25471],{},"queue"," persistence. It says:",[11947,25474,25475],{},[439,25476,25477],{},"NOTE: Data store needs to be a centralized system that is accessible from all Hazelcast members. Persistence to a\nlocal file system is not supported.",[439,25479,25480],{},"Aaaahrgs! That explained the observed behavior! The cluster assumes that all nodes access the same data store and\ndetermines one node to be some kind of persistence master that writes and reads all data from the store for the whole\ncluster! Further tests showed that it seems pretty unpredictable which node becomes the persistence master. If the nodes\nare not restarted in exactly the same order after every deployment it could happen that a different node is assigned\npersistence master and does not recover the data from the previous persistence master. The data would be lost – even\nwithout a cluster downtime. To prevent this we had to provide a centralized data store!",[439,25482,25483],{},"The situation was not critical as the persistence implementation had not been merged to master yet – but admittedly we\nwere in some kind of frustration mode at that point and the first reflex was to centralize the ChroniclaMap file so we\ndid not have to change the implementation again. After hard negotiations our ops team grudgingly provided us with a test\nsystem of multiple VMs all accessing the same file on a NFS share. As expected it worked, but it didn’t feel right. We\ndecided to run a long term test with production-like data and to decide afterwards if the solution is good enough to be\nrolled out on production.",[1065,25485,25487],{"id":25486},"stumbling-block-3-configuring-the-queuestore","Stumbling block #3: Configuring the QueueStore",[439,25489,25490],{},"This was only a minor issue but it added up with the uneasy mood that we had about our solution at that point. The\nHazelcast queue is configured programmatically via a Spring Configuration Class. During our development we noticed that\nneither of our configuration changes to the queue persistence seemed to have any effect. It turned out that the\nQueueStore only accepts strings as configuration parameters which it not obvious at the first glance when using a\njava.util.Properties object to pass the properties, which accepts Object as type.",[464,25492,25494],{"className":709,"code":25493,"language":711,"meta":469,"style":469},"// no Strings - does not work\nQueueStoreConfig queueStoreConfig = new QueueStoreConfig();\nqueueStoreConfig.setEnabled(true);\nProperties properties = new Properties();\nproperties.put(\"binary\", true);\nproperties.put(\"memory-limit\", 0);\nproperties.put(\"bulk-load\", 4L);\nqueueStoreConfig.setProperties(properties);\nqueueStoreConfig.setStoreImplementation(new ChronicleMapQueueStore());\n\n// Strings - does work\nQueueStoreConfig queueStoreConfig = new QueueStoreConfig();\nqueueStoreConfig.setEnabled(true);\nqueueStoreConfig.setProperty(\"binary\", \"false\");\nqueueStoreConfig.setProperty(\"memory-limit\", \"0\");\nqueueStoreConfig.setProperty(\"bulk-load\", \"4\");\nqueueStoreConfig.setStoreImplementation(new ChronicleMapQueueStore());\n",[471,25495,25496,25501,25506,25511,25516,25521,25526,25531,25536,25541,25545,25550,25554,25558,25563,25568,25573],{"__ignoreMap":469},[474,25497,25498],{"class":476,"line":477},[474,25499,25500],{},"// no Strings - does not work\n",[474,25502,25503],{"class":476,"line":507},[474,25504,25505],{},"QueueStoreConfig queueStoreConfig = new QueueStoreConfig();\n",[474,25507,25508],{"class":476,"line":547},[474,25509,25510],{},"queueStoreConfig.setEnabled(true);\n",[474,25512,25513],{"class":476,"line":584},[474,25514,25515],{},"Properties properties = new Properties();\n",[474,25517,25518],{"class":476,"line":607},[474,25519,25520],{},"properties.put(\"binary\", true);\n",[474,25522,25523],{"class":476,"line":642},[474,25524,25525],{},"properties.put(\"memory-limit\", 0);\n",[474,25527,25528],{"class":476,"line":663},[474,25529,25530],{},"properties.put(\"bulk-load\", 4L);\n",[474,25532,25533],{"class":476,"line":694},[474,25534,25535],{},"queueStoreConfig.setProperties(properties);\n",[474,25537,25538],{"class":476,"line":700},[474,25539,25540],{},"queueStoreConfig.setStoreImplementation(new ChronicleMapQueueStore());\n",[474,25542,25543],{"class":476,"line":913},[474,25544,917],{"emptyLinePlaceholder":916},[474,25546,25547],{"class":476,"line":920},[474,25548,25549],{},"// Strings - does work\n",[474,25551,25552],{"class":476,"line":926},[474,25553,25505],{},[474,25555,25556],{"class":476,"line":932},[474,25557,25510],{},[474,25559,25560],{"class":476,"line":938},[474,25561,25562],{},"queueStoreConfig.setProperty(\"binary\", \"false\");\n",[474,25564,25565],{"class":476,"line":944},[474,25566,25567],{},"queueStoreConfig.setProperty(\"memory-limit\", \"0\");\n",[474,25569,25570],{"class":476,"line":950},[474,25571,25572],{},"queueStoreConfig.setProperty(\"bulk-load\", \"4\");\n",[474,25574,25575],{"class":476,"line":956},[474,25576,25540],{},[1065,25578,25580],{"id":25579},"anxiety-4-no-transactions-obviously","Anxiety #4: No transactions – obviously",[439,25582,25583],{},"After testing our solution for a while, more and more scenarios of potential data loss popped into our mind. We’ve been\naware that neither Hazelcast nor ChronicleMap offer some kind of real transactions when writing to the file.\nTheoretically the persistence master could be killed off during a write operation and the file could be left in an\ninconsistent state. We tested this scenario with a manually corrupted file and it resulted in the unpleasant situation\nthat the ChronicleMap Spring bean could not be initialized, preventing the creation of the Spring ApplicationContext and\nconsequentially stopping the application startup – not good.",[439,25585,25586],{},"To feel safe about our solution we needed a transactional, central data store. Captain Obvious knocked on the door and\nsaid “Helloooo? Database?”.",[439,25588,25589],{},"We reconsidered this option again. The database is transactional, it is centralized and it does not count as external\nsystem because it is so essential for our application that when the database is down, the application is down anyway. It\nstill wouldn’t be a database managed queue because the queue still lies in-memory and the queueing mechanism is managed\nby Hazelcast. The database would be just a backup. Of course performance would be a potential issue but we were ready to\ngive it a try.",[439,25591,25592],{},"So finally we decided to change the QueueStore implementation to persist to a key-value table into our database.",[1065,25594,25596],{"id":25595},"wtf-5-its-a-real-bug","WTF #5: It’s a real bug!",[439,25598,25599],{},"Feeling better with this persistence approach we pushed closer to a production release of the feature. But suddenly we\nhad massive performance difficulties with production-like data. Reading 1000 entries from the queue took several\nminutes! Was it the fault of the database? Was it really that slow?",[439,25601,25602],{},"After extensive analysis we found out that as a matter of fact it was a bug in the otherwise really robust and stable\nHazelcast. When using the drainTo(numberOfEntries) method to get data from the queue the data should be loaded from the\npersisted data in bulks of a configurable size calling the QueueStore.loadAll(listOfEntries) method once for every bulk.\nInstead loadAll() got called once for the first bulk and then once for every single following item, resulting in almost\nthe same number of database calls as the number of requested items.",[439,25604,25605,25606,25611,25612,25617],{},"I recently opened an ",[1002,25607,25610],{"href":25608,"rel":25609},"https://github.com/hazelcast/hazelcast/issues/10621",[1006],"issue"," for the bug including a\nsmall ",[1002,25613,25616],{"href":25614,"rel":25615},"https://github.com/indyarni/hazelcastbugdemo",[1006],"demo project",". The Hazelcast team reacted on the same day promising\nto fix it. One week later the issue was fixed and the fix to be released in the next version 3.9! Kudos to the\nHazelcast developers!",[439,25619,25620],{},"Until we are able to use 3.9 we solved the problem with a temporary workaround, setting the bulk size the same as the\nnumber of drained entries, resulting in only one bulk loaded at a time and only one database call per drain.",[1065,25622,25624],{"id":25623},"_6-going-prod-finally-successful","(╯°□°)╯︵ ┻━┻ #6: Going Prod – finally successful?",[439,25626,25627],{},"Having cleared this last issue and experiencing no further problems or data loss on the test system for weeks we felt\nconfident to merge the persistence solution to master and go live with it. After the release we were relieved to see\nthat it just worked! Seemingly no problems, no data loss, no performance problems – the application became a lot faster\nand more resilient.",[439,25629,25630,25631,25636],{},"And here comes the “but”: But we sometimes still observe some (5-10) lost items from the in-memory queue after\nperforming a deployment on the cluster. That means that Hazelcast unexpectedly is not always able to synchronize the\ncluster in time when our deployment performs the A/B switch on one node after another. It is not a critical problem\nbecause the lost items can be manually recovered from the database but obviously we still intend to fix this issue. The\ncause is possibly ",[1002,25632,25635],{"href":25633,"rel":25634},"https://github.com/hazelcast/hazelcast/issues/5444",[1006],"this issue"," that has been fixed in Hazelcast\n3.7. Problem is, we rolled out 3.6.3 on production which is incompatible with 3.7 and newer versions, which means\nto update Hazelcast we would need a cluster downtime…. AAAAAHRG – the story continues 🙂",[3938,25638,25640],{"id":25639},"final-words","Final words",[439,25642,25643],{},"Those were only the most dominant of the many challenges we encountered while implementing this solution. Others were\ne.g. problems with Spring transaction handling when Hazelcast internally opened new threads to call the persistence\ninterface, analyzing different causes of data loss, etc, etc….",[439,25645,25646],{},"After this Odyssey we can draw some conclusions:",[994,25648,25649,25652,25655,25658],{},[997,25650,25651],{},"Hazelcast is a great tool! It’s fun to work with, the core features work reaaaally well and I would use it again.",[997,25653,25654],{},"However the non-core functionalities (like queue persistence) require some effort to get them working in a complex\nenvironment.",[997,25656,25657],{},"When using a new tool you should read the documentation carefully and try to understand how it really works!",[997,25659,25660],{},"If you really want to understand how Hazelcast works it’s not enough to read the documentation carefully 😉",[1024,25662,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":25664},[25665,25666,25667,25675],{"id":25271,"depth":507,"text":25272},{"id":25327,"depth":507,"text":25328},{"id":25345,"depth":507,"text":25346,"children":25668},[25669,25670,25671,25672,25673,25674],{"id":25432,"depth":547,"text":25433},{"id":25457,"depth":547,"text":25458},{"id":25486,"depth":547,"text":25487},{"id":25579,"depth":547,"text":25580},{"id":25595,"depth":547,"text":25596},{"id":25623,"depth":547,"text":25624},{"id":25639,"depth":507,"text":25640},[1030,1031],"2017-06-09T14:50:15","In this blog I will outline why we used Hazelcast for queueing messages in-memory distributed over a cluster and how we\\nachieved higher resilience by persisting the queue’s content. I will explain the pitfalls and difficulties that we\\nencountered and how I constantly switched between praising and condemning Hazelcast.","https://synyx.de/blog/the-struggle-with-hazelcast-queue-persistence/",{},"/blog/the-struggle-with-hazelcast-queue-persistence",{"title":25259,"description":25268},"blog/the-struggle-with-hazelcast-queue-persistence",[25685,25686,25687,25688],"cluster","hazelcast","persistence","resilience","In this blog I will outline why we used Hazelcast for queueing messages in-memory distributed over a cluster and how we achieved higher resilience by persisting the queue’s content. I will explain the pitfalls and difficulties that we encountered and how I constantly switched between praising and condemning Hazelcast. The problem to solve I’m currently working in a project for a large customer data backend. The prod system consists of a load balanced cluster of five VMs each running two Tomcat instances hosting our application.","qoLs1ff6I5-z82eh57RtHIy3wXq0OEZ5qLCROZHwoyo",{"id":25692,"title":25693,"author":25694,"body":25695,"category":25767,"date":25768,"description":25769,"extension":1034,"link":25770,"meta":25771,"navigation":916,"path":25772,"seo":25773,"slug":25699,"stem":25774,"tags":25775,"teaser":25776,"__hash__":25777},"blog/blog/karlsruher-entwicklertage-2017-conference-day.md","Karlsruher Entwicklertage 2017 – Conference Day",[208],{"type":432,"value":25696,"toc":25758},[25697,25700,25703,25706,25710,25713,25717,25720,25724,25727,25731,25734,25738,25741,25745,25748,25752,25755],[435,25698,25693],{"id":25699},"karlsruher-entwicklertage-2017-conference-day",[439,25701,25702],{},"Am 22.5. habe ich zum ersten Mal die Karlsruher Entwicklertage besucht. Insgesamt war die Veranstaltung gut\norganisiert, man bekam schnell und unkompliziert seine Badge und die obligatorische Info-Tüte. Es gab eine große\nAuswahl an verschiedenen Talks, die auf 6 parallele Tracks aufgeteilt waren. Die einzelnen Tracks wurden unter anderem\nvon einigen Usergroups wie z.B. der Java Usergroup, der .NET-Usergroup und dem lokalen OWASP-Chapter organisiert.\nDanke an die ehrenamtlichen Helfer für das Engageme",[439,25704,25705],{},"Hier meine Eindrücke der Talks, die ich besucht habe:",[1065,25707,25709],{"id":25708},"sinn-und-nutzen-von-restful-hypermedia-apis","Sinn und Nutzen von RESTful-Hypermedia-APIs",[439,25711,25712],{},"Kai Tödter gab in seiner Keynote eine Einführung in REST und Hypermedia, und stellte verschiedene Frameworks vor, mit\nderen Hilfe entsprechende Schnittstellen in Java und .NET implementiert werden können. Die Ausrichtung war sehr\ndetailliert und technisch, mehr ein kompletter Vortrag als eine Keynote. Obwohl ich bei synyx schon mit Hypermedia und\nSpring HATEOAS in Berührung gekommen bin, war es interessant einige Grundlagen und Architektur-Überlegungen zu sehen.",[1065,25714,25716],{"id":25715},"ops-for-developers-monitoring-mit-prometheus-für-java-entwickler","Ops for Developers – Monitoring mit Prometheus für Java Entwickler",[439,25718,25719],{},"Trotz meiner Vorerfahrung mit InfluxDB, Prometheus und Grafana ist es für mich immer ein Zugewinn, neue Talks zum Thema\nMonitoring und Metriken zu hören. Nach einer kurzen Einführung zeigte Alexander Schwarz an einfachen Beispielen, wie man\nin verschiedenen Frameworks Metric-Endpoints bereitstellen kann, um sie mit Prometheus zu scrapen. Zusätzlich erklärte\ner wie man entsprechende Auswertungen in Prometheus bzw. passende Dashboards mit Grafana erzeugen kann, um Schlüsse über\nAuslastung und Laufzeitdaten aus seiner Anwendung zu ziehen.",[1065,25721,25723],{"id":25722},"security-requirements-im-software-development-lifecycle","Security Requirements im Software Development Lifecycle",[439,25725,25726],{},"Daniel Kefer und René Reuter stellten ihr Tool SecurityRat (Security Requirement Automation Tool) vor, das unter anderem\nauch als OWASP-Projekt anerkannt und unterstützt wird. Mit SecurityRat lassen sich Security-Requirements auf laufende\nund neue Software-Projekte abbilden. Aus einem Katalog von Requirements werden durch die Auswahl bestimmter Kriterien\nsicherheitsrelevante Tasks und TODOs vorgeschlagen und lassen sich automatisch als JIRA-Tickets in der entsprechenden\nSW-Projekt-Queue abbilden. Im Vordergrund steht hier die Zeitersparnis, da in viele Firmen die\nSecurity-Verantwortlichen für mehrere Projekte verantwortlich sind. Durch die Abstraktion von Standardarbeiten wie dem\nAnlegen von Tickets und dem Erstellen von Checklisten werden diese entlastet. Im weiteren gaben sie einen Ausblick auf\nihr neues Tool SecurityCat (Security Compliance and Automated Testing), das als Teil der Continuous Integration\nToolchain Sicherheitstests durchführen soll.",[1065,25728,25730],{"id":25729},"security-baselines-für-web-applikationen-in-der-praxis-oder-wieviel-sicherheit-darfs-denn-sein","Security Baselines für Web-Applikationen in der Praxis oder: „Wieviel Sicherheit darf’s denn sein?“",[439,25732,25733],{},"Ingo Hanke zeigte die Schwierigkeit auf, Kunden einen Mindeststandard an IT-Sicherheit als Teil der Dienstleistung zu\nverkaufen. Zentrales Argument war es, eine nachhaltige Lösung und Verbesserung für den Kunden zu schaffen. Da die\nBSI-Empfehlungen für die Basissicherheit in Unternehmen für viele Kleinunternehmer seiner Meinung nach nicht machbar\nsind, erklärte er, welche Grundsicherungsmaßnahmen er empfiehlt und mit den Kunden eruiert. Er verwendet hierzu eine Art\nBaukastenprinzip, um Gefahrenquellen in den Unternehmen zu evaluieren und entsprechende Maßnahmen anzubieten. Seiner\nErfahrung nach haben viele Firmen keine Vorstellung und Vorkehrungen im Bereich IT-Sicherheit und können hier durch\ngeringen Aufwand großen Nutzen erzielen. Aus seinem Talk gingen u.a. die Empfehlung hervor, mit neuen Kunden einen\n2-stündigen Workshop zu machen, bei dem unbedingt ein Entscheider dabeisein sollte. Zusätzlich empfiehlt er der\nSoftware-Branche, Sicherheitsmaßnahmen in ihren Dienstleistungen einzupreisen und im Zweifelsfall auf Billig-Projekte\nzu verzichten.",[1065,25735,25737],{"id":25736},"sicher-in-die-cloud-mit-angular-2-und-spring-boot","Sicher in die Cloud mit Angular 2 und Spring Boot",[439,25739,25740],{},"Andreas Falk zeigte in einem sehr beispiellastigen Vortrag die Gefahren bzw. Anfälligkeiten von modernen\nWebapplikationen am Beispiel von Angular2 als Frontend mit Springboot im Backend. Themen waren hier unter anderem die\nverschiedenen Schnittstellen, die das jeweilige Framework anbietet, und wie die implizite Absicherung z.b. in Angular2\ngegen Angriffe wie Cross-Site-Scripting (XSS) und Cross-Site-Request-Forgery (CSRF) funktioniert. Der Vortrag\norientierte sich am OWASP Top10 Projekt (Version 2017 RC1). Andreas erklärte anschaulich, welche Implikationen die in\nder Top10 aufgeführten Risiken mit sich bringen.",[1065,25742,25744],{"id":25743},"löschen-löschen-löschen","Löschen? Löschen. Löschen!",[439,25746,25747],{},"Volker Hammer referierte über Datenschutz und die notwendigen Pflichten von Service-Anbietern bei der Datenhaltung und\n-Vernichtung. Am Beispiel von TollCollect wurde gezeigt, welche Aufwände anfallen können, bis ein Standard entwickelt\nwerden kann, der als DIN-Norm anerkannt wird. In einem Konglomerat aus mehreren großen deutschen Firmen ist in 11\nJahren die DIN 66398 “Leitlinie zur Entwicklung eines Löschkonzepts mit Ableitung von Löschfristen für personenbezogene\nDaten“ entstanden. Neben der Beschreibung waren hier auch die Erkenntnisse interessant, die man bei der Erstellung der\nNorm und dem Festlegen der Löschkonzepte erkannt hat, u.a. die Vereinfachung der Datenhaltung und -Sicherung.",[1065,25749,25751],{"id":25750},"keynote-software-engineering-the-roots","Keynote: Software Engineering – the roots",[439,25753,25754],{},"In der Abschluss-Keynote sprach Fath Al-Fatish über die Aufwände in der Softwareentwicklung, die zu über 50% in der\nWartung liegen und beklagte die mangelnde Aus- und Weiterbildung der Softwareentwickler. Auf spassige Art erklärte er\ndie Problematik in der Aussensicht der Branche aufgrund von fehlendem Verständniss und Interesse an Nachhaltigkeit. In\nseinem Beitrag verwies er auf Arbeitsweisen und Verbesserungspotential. Innerlich habe ich mich an dieser Stelle leider\ngefragt, was in vielen anderen Betrieben schiefgeht, und habe mich hier bei allen Argumenten und Lösungsvorschlägen\ninnerlich gefreut , da ich alles positive bei synyx wiederfinde.",[439,25756,25757],{},"Insgesamt eine empfehlenswerte Konferenz, nicht mein letzter Besuch!",{"title":469,"searchDepth":507,"depth":507,"links":25759},[25760,25761,25762,25763,25764,25765,25766],{"id":25708,"depth":547,"text":25709},{"id":25715,"depth":547,"text":25716},{"id":25722,"depth":547,"text":25723},{"id":25729,"depth":547,"text":25730},{"id":25736,"depth":547,"text":25737},{"id":25743,"depth":547,"text":25744},{"id":25750,"depth":547,"text":25751},[1030,1031],"2017-05-23T14:57:28","Am 22.5. habe ich zum ersten Mal die Karlsruher Entwicklertage besucht. Insgesamt war die Veranstaltung gut\\norganisiert, man bekam schnell und unkompliziert seine Badge und die obligatorische Info-Tüte. Es gab eine große\\nAuswahl an verschiedenen Talks, die auf 6 parallele Tracks aufgeteilt waren. Die einzelnen Tracks wurden unter anderem\\nvon einigen Usergroups wie z.B. der Java Usergroup, der .NET-Usergroup und dem lokalen OWASP-Chapter organisiert.\\nDanke an die ehrenamtlichen Helfer für das Engageme","https://synyx.de/blog/karlsruher-entwicklertage-2017-conference-day/",{},"/blog/karlsruher-entwicklertage-2017-conference-day",{"title":25693,"description":25702},"blog/karlsruher-entwicklertage-2017-conference-day",[],"Am 22.5. habe ich zum ersten Mal die Karlsruher Entwicklertage besucht. Insgesamt war die Veranstaltung gut organisiert, man bekam schnell und unkompliziert seine Badge und die obligatorische Info-Tüte. Es gab eine große Auswahl an verschiedenen Talks, die auf 6 parallele Tracks aufgeteilt waren. Die einzelnen Tracks wurden unter anderem von einigen Usergroups wie z.B. der Java Usergroup, der .NET-Usergroup und dem lokalen OWASP-Chapter organisiert. Danke an die ehrenamtlichen Helfer für das Engageme","yL6rqaMobpSi3DAnvXOcesuCqY6XUSrTSNz27zUGzfg",{"id":25779,"title":25780,"author":25781,"body":25782,"category":25980,"date":25981,"description":25982,"extension":1034,"link":25983,"meta":25984,"navigation":916,"path":25985,"seo":25986,"slug":25786,"stem":25988,"tags":25989,"teaser":25993,"__hash__":25994},"blog/blog/validating-internal-structure-dependencies-using-intellij-idea.md","Validating internal structure / dependencies using IntelliJ IDEA",[166],{"type":432,"value":25783,"toc":25978},[25784,25787,25813,25839,25842,25856,25867,25877,25895,25905,25908,25915,25925,25928,25939,25949,25952,25962,25970],[435,25785,25780],{"id":25786},"validating-internal-structure-dependencies-using-intellij-idea",[439,25788,25789,25790,7267,25795,25800,25801,25806,25807,25812],{},"There are several different tools to maintain the internal structure of a java application available. The tools range\nfrom simple open source software like",[1002,25791,25794],{"href":25792,"rel":25793},"https://github.com/clarkware/jdepend",[1006],"jdepend",[1002,25796,25799],{"href":25797,"rel":25798},"https://web.archive.org/web/20200924121825/http://blog.schauderhaft.de/degraph/",[1006],"degraph"," to full fledged\narchitecture tooling like ",[1002,25802,25805],{"href":25803,"rel":25804},"http://structure101.com/",[1006],"Structure101","\nor ",[1002,25808,25811],{"href":25809,"rel":25810},"https://www.hello2morrow.com/products/sonargraph/architect9",[1006],"Sonargraph Architect",". All these provide methods to\ndefine the internal structure of an application and validate it somehow.",[439,25814,25815,25816,25821,25822,25827,25828,25833,25834,1402],{},"Since we are using ",[1002,25817,25820],{"href":25818,"rel":25819},"https://www.jetbrains.com/idea/",[1006],"IntelliJ IDEA"," in many of our teams I’d like to show a handy little\nfeature of the IDE that helps in maintaining a structured application:In IDEA you\ncan ",[1002,25823,25826],{"href":25824,"rel":25825},"https://www.jetbrains.com/help/idea/2017.1/scopes.html",[1006],"define scopes"," and match your code to them. You can then\nrule how they may or may not access each other using\nthe ",[1002,25829,25832],{"href":25830,"rel":25831},"https://www.jetbrains.com/help/idea/2017.1/dependency-viewer.html",[1006],"Dependency Viewer",". As soon as you do this the\nIDE warns you about illegal package access just as you type. Note that the shown features currently work in the\nfree ",[1002,25835,25838],{"href":25836,"rel":25837},"https://www.jetbrains.com/idea/#chooseYourEdition",[1006],"IntelliJ IDEA Community Edition",[439,25840,25841],{},"Imagine we have a simple layered application with Java packages representing these layers:",[994,25843,25844,25847,25850,25853],{},[997,25845,25846],{},"api: API-Layer features controllers that are responsible to render a RESTful API to our application",[997,25848,25849],{},"business: our higher level operations/business logic happens here",[997,25851,25852],{},"persistence: this layer is responsible for storing data and providing access to it",[997,25854,25855],{},"domain: In addition we have a domain “layer” where all the layers share their common domain",[439,25857,25858,25859,25862,25863,25866],{},"We can now define scopes for each of these layers. We can do so using ",[990,25860,25861],{},"File -> Settings"," and then select\n",[990,25864,25865],{},"Appearance & Behaviour -> Scopes",". Here we can add new scopes and assign files to it using a pattern or by\nnavigating to the corresponding packages and include/exclude them.",[439,25868,25869],{},[1002,25870,25873],{"href":25871,"rel":25872},"https://media.synyx.de/uploads//2017/05/1-creating-sopes.png",[1006],[2205,25874],{"alt":25875,"src":25876},"Creating Scopes using Settings","https://media.synyx.de/uploads//2017/05/1-creating-sopes-1024x616.png",[439,25878,25879,25880,25883,25884,25887,25888,25891,25892],{},"We then define archuitectural constraints on these scopes using the ",[990,25881,25882],{},"Analyze Dependencies View"," (accessible using the\n",[990,25885,25886],{},"Analyze"," Menu). We do so by clicking the ",[990,25889,25890],{},"Edit Rules Icon"," in the ",[990,25893,25894],{},"Dependency-View.",[439,25896,25897],{},[1002,25898,25901],{"href":25899,"rel":25900},"https://media.synyx.de/uploads//2017/05/2-defining-rules.png",[1006],[2205,25902],{"alt":25903,"src":25904},"Defining rules","https://media.synyx.de/uploads//2017/05/2-defining-rules-1024x602.png",[439,25906,25907],{},"In the example above configured simple constraints for our layered architecture: We only allow access between layers\nfrom an upper layer to a lower layer (API to Business and Business to Persistence) and we dont allow Domain to access\nany other layer.",[439,25909,25910,25911,25914],{},"When we return to the ",[990,25912,25913],{},"Dependency-View"," and re-run the analysis it displays all the violations of our architecture.\nAlso, we get the analysis as soon as we type a new Validation in the Editor.",[439,25916,25917],{},[1002,25918,25921],{"href":25919,"rel":25920},"https://media.synyx.de/uploads//2017/05/3-violations-in-dependency-view.png",[1006],[2205,25922],{"alt":25923,"src":25924},"Violations view from dependencies","https://media.synyx.de/uploads//2017/05/3-violations-in-dependency-view-1024x534.png",[439,25926,25927],{},"You can see in the screenshot that we have an illegal access from a class in scope Persistence that accesses something\nin scope Business. We can simply fix this by moving the Initializer to the business-Package and the error disappears.",[439,25929,25930,25931,25934,25935,25938],{},"As an alternative we can also access the information by running the analysis ",[990,25932,25933],{},"Illegal package dependencies"," (e.g. using\nthe ",[990,25936,25937],{},"Analyze – > Run inspection by name",") dialog. From there we can also edit our rules and navigate to the violating\ncode.",[439,25940,25941],{},[1002,25942,25945],{"href":25943,"rel":25944},"https://media.synyx.de/uploads//2017/05/4-violations-in-inspections-view.png",[1006],[2205,25946],{"alt":25947,"src":25948},"Violations-View from inspections","https://media.synyx.de/uploads//2017/05/4-violations-in-inspections-view-1024x534.png",[439,25950,25951],{},"If you enable the shared option for scopes IDEA will also write the configuration to .idea/scopes from where you can\nshare them with your team members.",[439,25953,25954],{},[1002,25955,25958],{"href":25956,"rel":25957},"https://media.synyx.de/uploads//2017/05/5-export.png",[1006],[2205,25959],{"alt":25960,"src":25961},"Scope definitions in .idea/scopes","https://media.synyx.de/uploads//2017/05/5-export-1024x510.png",[439,25963,25964,25965,1402],{},"To test and experiment with scopes yourself you can build on\nmy",[1002,25966,25969],{"href":25967,"rel":25968},"https://github.com/marckanneg/idea-scopes-demo",[1006],"my demo-project",[439,25971,25972,25973,25977],{},"You can clone it\nfrom ",[1002,25974,25975],{"href":25975,"rel":25976},"https://github.com/marckanneg/idea-scopes-demo.git",[1006]," and open\nit using your IntelliJ IDEA(File ->Open).",{"title":469,"searchDepth":507,"depth":507,"links":25979},[],[1030],"2017-05-16T12:18:36","There are several different tools to maintain the internal structure of a java application available. The tools range\\nfrom simple open source software likejdepend\\nand degraph to full fledged\\narchitecture tooling like Structure101\\nor Sonargraph Architect. All these provide methods to\\ndefine the internal structure of an application and validate it somehow.","https://synyx.de/blog/validating-internal-structure-dependencies-using-intellij-idea/",{},"/blog/validating-internal-structure-dependencies-using-intellij-idea",{"title":25780,"description":25987},"There are several different tools to maintain the internal structure of a java application available. The tools range\nfrom simple open source software likejdepend\nand degraph to full fledged\narchitecture tooling like Structure101\nor Sonargraph Architect. All these provide methods to\ndefine the internal structure of an application and validate it somehow.","blog/validating-internal-structure-dependencies-using-intellij-idea",[25990,25991,14723,25992,8279],"idea","layers","software-qualitat","There are several different tools to maintain the internal structure of a java application available. The tools range from simple open source software like jdepend and degraph to full fledged architecture…","wy8xAmArTg4Nc2aQeKpiebIqpU9fwMuBZYpx1Rq29D8",{"id":25996,"title":25997,"author":25998,"body":25999,"category":26154,"date":26155,"description":26156,"extension":1034,"link":26157,"meta":26158,"navigation":916,"path":26159,"seo":26160,"slug":26161,"stem":26162,"tags":26163,"teaser":26166,"__hash__":26167},"blog/blog/kids-lernen-programmieren-mit-jeder-menge-spass.md","Kids lernen Programmieren – mit jeder Menge Spaß",[12,172,332],{"type":432,"value":26000,"toc":26152},[26001,26004,26007,26017,26020,26023,26028,26031,26035,26038,26043,26058,26061,26084,26087,26110,26116,26137,26144],[435,26002,25997],{"id":26003},"kids-lernen-programmieren-mit-jeder-menge-spaß",[439,26005,26006],{},"Ein bisschen aufgeregt waren wir dieses Mal schon. Im Gegensatz zu den vergangenen Events erwarteten wir am Samstag fast\nnur Kinder, welche noch nie bei der Devoxx4Kids mitgemacht haben. Wir haben uns darüber sehr gefreut, aber ein wenig\naufregend ist das auch. Wie kommt das Event an? Wie die Workshops? Mit unseren „alten“ Hasen hatten wir bereits\nErfahrung sammeln können. Um gleich vorwegzunehmen: es war super! Wir alle hatten sehr viel Spaß und wir werden bestimmt\neinige Kinder beim nächsten Mal wieder sehen.",[439,26008,26009],{},[1002,26010,26013],{"href":26011,"rel":26012},"https://media.synyx.de/uploads//2017/05/IMG_7435.jpg",[1006],[2205,26014],{"alt":26015,"src":26016},"devoxx4Kids","https://media.synyx.de/uploads//2017/05/IMG_7435-300x200.jpg",[439,26018,26019],{},"Wie immer bauten wir bereits am Freitag alles in der Karlshochschule auf. Testeten noch mal alles, damit am\nSamstagmorgen alles startklar ist. Die ersten Kinder trudelten bereits um halb neun ein, aber sie mussten sich noch\netwas gedulden, Start war erst um 9:00 Uhr. Nach der Begrüßung verabschiedeten sich die Eltern und die Kids durften\nendlich in die verschiedenen Workshops.",[439,26021,26022],{},"Wir wiederholen im Frühjahr die Workshops vom Herbst, also stand erneut Jumping Sumo, Cardboard und Lego Mindstorms auf\ndem Programm.",[439,26024,26025],{},[448,26026,26027],{},"Cardboard",[439,26029,26030],{},"Der Cardboard Workshop beschäftigt sich mit der Physik des Auges, der Täuschung des Gehirns und der virtuellen Realität.\nJedes Team wurde ausgestattet mit einem Handy und einem Google CardBoard, um sich eine eigene kleine virtuelle Welt zu\nerzeugen. Begonnen hat der Workshop mit einer kleinen Einführung darüber, wie das menschliche Auge sich täuschen lässt\nund die Illusion einer dreidimensionalen Welt hergestellt werden kann. Danach haben die Kids mit den Mentoren eine\neigene kleine Umgebung erstellen können, durch die sie sich fortbewegen konnten. Der Workshop fand guten Anklang und das\nDurchschreiten der virtuell konstruierten Labyrinthe machte viel Freude.",[439,26032,26033],{},[448,26034,4011],{},[439,26036,26037],{},"Zum Einsatz kamen fertig aufgebaute Lego Mindstorm EV3 KRAZ3. Vom Aussehen her erinnert er an den Wall-E aus dem Walt\nDisney Film. Bewegen kann sich der Roboter mit Hilfe von 2 Raupenantrieben. Ein weiterer Motor lässt den Kopf und die\nArme wackeln. Dazu kommt noch ein Infrarot-Distanzsensor, Tastsensor sowie ein Farb- und Helligkeitssensor. Die Kinder\nprogrammierten den Roboter mit der Lego Mindstorm Softwarefür welche keine großen Programmierkenntnisse erforderlich\nsind, da die Programmierung aus visuellen Bausteinen die einfach per Drag&Drop zu einem Programm zusammengesteckt\nwerden können, besteht. Angefangen bei einfachen Aufgaben wie das Wackeln von Kopf und Armen, über Musizierenden Roboter\ndie dabei tanzen, bis hin zu einem Roboter der sich anhand von Farbkodierung auf dem Boden durch den Raum bewegen kann.",[439,26039,26040],{},[448,26041,26042],{},"Jumping Sumo",[439,26044,26045,26046,26051,26052,26057],{},"Nachdem wir im letzten Jahr den Jumping Sumo direkt mit Java gesteuert haben, gingen wir dieses Jahr neue Wege. Stefan\nHöhn hat die Steuerung des Jumping Sumos, basierend auf\nunserem ",[1002,26047,26050],{"href":26048,"rel":26049},"https://github.com/Devoxx4KidsDE/drone-controller",[1006],"Drone-Controller",", mit Hilfe\nvon ",[1002,26053,26056],{"href":26054,"rel":26055},"https://github.com/Devoxx4KidsDE/workshop-jumping-sumo-4-scratch",[1006],"Scratch umgesetz",". Der Workshop stand unter dem\nMotto „Mars Mission“. Die Aufgabe war, nach einer",[439,26059,26060],{},"erfolgreichen Landung auf dem Mars, den Weg zur Basis zu finden. Zum Glück hatten wir den Jumping Sumo, welcher uns bei\ndieser schwierigen Aufgabe unterstützt. Eine Batteriewarnsystem sowie die Erkundung des Mars, auch mit Hilfe von\nLive-Bildern und Schnappschüssen, dürfen bei der Suche nach der Basis natürlich nicht fehlen.",[439,26062,26063,26070,26077],{},[1002,26064,26067],{"href":26065,"rel":26066},"https://media.synyx.de/uploads//2017/05/IMG_7534.jpg",[1006],[2205,26068],{"alt":469,"src":26069},"https://media.synyx.de/uploads//2017/05/IMG_7534-150x150.jpg",[1002,26071,26074],{"href":26072,"rel":26073},"https://media.synyx.de/uploads//2017/05/IMG_7645.jpg",[1006],[2205,26075],{"alt":469,"src":26076},"https://media.synyx.de/uploads//2017/05/IMG_7645-150x150.jpg",[1002,26078,26081],{"href":26079,"rel":26080},"https://media.synyx.de/uploads//2017/05/IMG_7525.jpg",[1006],[2205,26082],{"alt":469,"src":26083},"https://media.synyx.de/uploads//2017/05/IMG_7525-150x150.jpg",[439,26085,26086],{},"Die Mittagspause konnten wir auf der Terrasse genießen. Die Sonne hatte sich zwar schon etwas zurückgezogen, aber es war\ntrotzdem schön. Nach den Aufnahmen für die Gruppenfotos ging es mit vollem Elan mit den nächsten Workshops weiter.",[439,26088,26089,26096,26103],{},[1002,26090,26093],{"href":26091,"rel":26092},"https://media.synyx.de/uploads//2017/05/IMG_7707.jpg",[1006],[2205,26094],{"alt":469,"src":26095},"https://media.synyx.de/uploads//2017/05/IMG_7707-150x150.jpg",[1002,26097,26100],{"href":26098,"rel":26099},"https://media.synyx.de/uploads//2017/05/IMG_7720.jpg",[1006],[2205,26101],{"alt":469,"src":26102},"https://media.synyx.de/uploads//2017/05/IMG_7720-150x150.jpg",[1002,26104,26107],{"href":26105,"rel":26106},"https://media.synyx.de/uploads//2017/05/IMG_7734.jpg",[1006],[2205,26108],{"alt":469,"src":26109},"https://media.synyx.de/uploads//2017/05/IMG_7734-150x150.jpg",[439,26111,26112,26113,26115],{},"Es war ein rundum gelungenes Event, bei dem Lernen mit Spaß im Vordergrund stand. Und den hatten wir alle zusammen! Wir\nfreuen uns schon auf das nächste Mal im Herbst. Wer als Mentor mitmachen möchte oder uns gerne als Sponsor unterstützen\nmöchte, kann ich gerne bei uns (",[1002,26114,4121],{"href":4120},") melden. Aber natürlich auch, falls\njemand Unterstützung bei einer Devoxx4Kids in einer anderen Stadt braucht.",[439,26117,26118,26119,520,26124,520,26128,26132,26133,1402],{},"Zum Schluss möchten wir uns noch bei den tollen Sponsoren\nbedanken ",[1002,26120,26123],{"href":26121,"rel":26122},"https://www.dm.de/arbeiten-und-lernen/arbeiten-bei-uns/filiadata-c534052.html",[1006],"FILIADATA GmbH",[1002,26125,18767],{"href":26126,"rel":26127},"http://www.emendare.de/",[1006],[1002,26129,389],{"href":26130,"rel":26131},"http://www.synyx.de",[1006],"\nund der ",[1002,26134,18688],{"href":26135,"rel":26136},"http://karlshochschule.de/de/",[1006],[439,26138,26139,26140,1402],{},"Unser neues Video zur Devoxx4Kids findet ihr auf ",[1002,26141,18659],{"href":26142,"rel":26143},"https://youtu.be/JG5yCZr6tus",[1006],[439,26145,26146,26147,1402],{},"Weitere Fotos gibt es hier in unserer ",[1002,26148,26151],{"href":26149,"rel":26150},"http://www.devoxx4kids.de/karlsruhe/galerie/",[1006],"Galerie",{"title":469,"searchDepth":507,"depth":507,"links":26153},[],[4216],"2017-05-10T18:24:50","Ein bisschen aufgeregt waren wir dieses Mal schon. Im Gegensatz zu den vergangenen Events erwarteten wir am Samstag fast\\nnur Kinder, welche noch nie bei der Devoxx4Kids mitgemacht haben. Wir haben uns darüber sehr gefreut, aber ein wenig\\naufregend ist das auch. Wie kommt das Event an? Wie die Workshops? Mit unseren „alten“ Hasen hatten wir bereits\\nErfahrung sammeln können. Um gleich vorwegzunehmen: es war super! Wir alle hatten sehr viel Spaß und wir werden bestimmt\\neinige Kinder beim nächsten Mal wieder sehen.","https://synyx.de/blog/kids-lernen-programmieren-mit-jeder-menge-spass/",{},"/blog/kids-lernen-programmieren-mit-jeder-menge-spass",{"title":25997,"description":26006},"kids-lernen-programmieren-mit-jeder-menge-spass","blog/kids-lernen-programmieren-mit-jeder-menge-spass",[26164,4216,26165,4010,9459],"cardboard","jumping-sumo","Ein bisschen aufgeregt waren wir dieses Mal schon. Im Gegensatz zu den vergangenen Events erwarteten wir am Samstag fast nur Kinder, welche noch nie bei der Devoxx4Kids mitgemacht haben. Wir…","SvtcAnYxQoOBweBfKD7x2NrJEO5cFYTvfcKcBVevoG0",{"id":26169,"title":26170,"author":26171,"body":26172,"category":26324,"date":26325,"description":469,"extension":1034,"link":26326,"meta":26327,"navigation":916,"path":26328,"seo":26329,"slug":26330,"stem":26331,"tags":26332,"teaser":26335,"__hash__":26336},"blog/blog/von-profis-lernen-heisst-siegen-lernen.md","Von Profis lernen heißt siegen lernen",[335,412],{"type":432,"value":26173,"toc":26315},[26174,26177,26199,26202,26214,26217,26220,26224,26227,26230,26234,26237,26240,26244,26247,26250,26253,26257,26260,26263,26267,26270,26276,26279,26283,26286,26289,26293,26296,26301,26304,26309,26312],[435,26175,26170],{"id":26176},"von-profis-lernen-heißt-siegen-lernen",[11947,26178,26179,26184,26189,26194],{},[439,26180,26181],{},[990,26182,26183],{},"Der nächste Sprint steht an… Wir treffen uns am Dienstag Morgen um 10:00 Uhr zum Refinement. Es gilt noch einige\nThemen fachlich genauer zu hinterfragen. Auch technische Grundlagen müssen noch geklärt werden.",[439,26185,26186],{},[990,26187,26188],{},"Abends geht’s zum Fußballtraining. Der Trainer hält erst einmal einen 15 minütigen Vortrag über das letzte Spiel am\nSonntag. Dann fängt er auch noch an über das nächste Spiel zu reden wie wichtig das sei… Ich will doch nur kicken.\nVerteil endlich die Leibchen!",[439,26190,26191],{},[990,26192,26193],{},"Mittwoch morgen unter der Dusche gehe ich nochmal die technischen Themen des Refinements durch. Mir fällt auf, dass\nunser Ansatz wohl nur bedingt funktioniert und habe eine alternative Idee. Glück gehabt… Man wär das ärgerlich gewesen\ndas erst zu bemerken, nachdem ein Tag Entwicklung investiert wurde. Im Büro angekommen setze ich mir erst einmal eine\nTimebox von einer Stunde und evaluiere eine Bibliothek.",[439,26195,26196],{},[990,26197,26198],{},"Donnerstag dann wieder Fußballtraining. Der Trainer kommt später. Steckt noch im Stau bekomme ich in der Kabine\ngesagt. Wir krallen uns die Bälle, legen sie auf die Strafraumlinie und ballern das Runde ins Eckige. Als der Trainer\ndann kommt machen wir zum Aufwärmen wieder dieses Passspiel-Laufweg-Dingens. Als ob das was bringen würde.",[439,26200,26201],{},"“Von Sportlern lernen heißt siegen lernen”. Dieser Spruch ist mehr oder weniger jedem geläufig. Die einleitende\nAnekdote löst bei so mancher Person vielleicht noch weitere Erinnerungen aus. Aus diesem Winkel betrachtet, erfordert\nder eben erwähnte Spruch vermutlich eine Überholung. Viel treffender wäre an dieser Stelle: “Vom Berufsleben lernen\nheißt siegen lernen”.",[439,26203,8663,26204,26207,26208,26210,26211,26213],{},[990,26205,26206],{},"ich"," ist froh nicht gleich in die Tastatur gehauen, sondern vorerst geplant zu haben. Das ",[990,26209,26206],{}," setzt sich\ndiszipliniert eine Timebox um neue Technologien zu evaluieren. Das selbe ",[990,26212,26206],{}," jedoch, will “einfach nur kicken”,\nanstatt das eigene Spiel und das des Teams mit spezifischen Trainingseinheiten zu verbessern.",[439,26215,26216],{},"Vergleicht man die Sportart Fussball mit dem Beruf der Softwareentwicklung, zeigen sich interessante Analogien auf. Im\nfolgenden werden einige dieser Analogien beleuchtet und regen hoffentlich zum Denken an. Ähnliches schon erlebt? Gibt es\nweitere Analogien? Stimmt gar nicht!",[439,26218,26219],{},"Falls nicht bekannt sein sollte, was wir im Berufsalltag so tun, helfen die Analogien vielleicht einen Eindruck zu\nbekommen.",[3938,26221,26223],{"id":26222},"wenns-nicht-läuft-ist-immer-der-trainer-zuerst-am-pranger","Wenns nicht läuft ist immer der Trainer zuerst am Pranger",[439,26225,26226],{},"Der “Trainer” als Alibi Verantwortlicher. Es ist immer einfacher eine einzelne Person los zu werden, als das komplette\nTeam im Hintergrund.",[439,26228,26229],{},"In Firmen und Unternehmen geht es oftmals ähnlich von statten: Führungskräfte und Projektleiter werden ausgetauscht,\nwenn das Projektziel gefährdet ist. Unter neuer Führung geht es in die entgegengesetzte Richtung weiter, in der Hoffnung\nnun alles besser zu machen. Die bereits vorhandenen Probleme lassen sich auf diese Weise aber nicht zwangsläufig\nbeseitigen. Probleme können auch im Team, als auch im Umfeld verankert sein.",[3938,26231,26233],{"id":26232},"ballbesitz-9010-und-keine-tore","Ballbesitz 90:10 und keine Tore",[439,26235,26236],{},"Kurzpassspiel, optimale Raumausnutzung, keine Fehlpässe. Für Taktik Liebhaber und Trainer sicherlich ein Leckerbissen.\nDoch nach 90 Minuten gewinnt die Mannschaft mit den meist erzielten Toren. Bei aller Leidenschaft und Liebe gewinnt am\nEnde das Ergebnis.",[439,26238,26239],{},"Auf ähnlich Art und Weise vertieft man sich auch gerne mal in ein Code Refactoring. Es soll ja keiner kommen und sagen\ndass hier könnte gegebenenfalls unter gewissen Umständen manchmal zu einem Bug oder einem Missverständnis führen. Es\nwerden so lange Codefragmente in andere Komponenten verschoben bis der eigene Zufriedensheitgrad erreicht wird. Am Ende\ndes Tages zählt jedoch der Mehrwert seitens des Anwenders.",[3938,26241,26243],{"id":26242},"guardiola-vs-ancelotti-taktische-vorgaben-vs-laissez-faire","Guardiola vs Ancelotti (taktische Vorgaben vs Laissez-faire)",[439,26245,26246],{},"Guardiola und Ancelotti zählen aktuell beide zu den erfolgreichsten Trainern der Welt. Ihre Herangehensweisen und\nFührungsstile könnten jedoch kaum unterschiedlicher sein. Während Guardiola für millimetergenaue Laufwege und exakte\ntaktische Vorgaben bekannt ist, pflegt und befürwortet Ancelotti eher den Laissez-faire Stil.",[439,26248,26249],{},"In der Softwareentwicklung erinnert das z. B. an Besprechungen und deren Ablauf. Wir haben für das Refinement 60 Minuten\nangesetzt. Also sollten auch alle Themen innerhalb dieser Zeit besprochen sein. Zur Not fallen niedrig priorisierte\nThemen runter. Man hat schließlich noch andere Themen denen man sich zuwenden muss!",[439,26251,26252],{},"Oder vielleicht doch mal vom Fahrplan abweichen und das Refinement spontan verlängern, da gegen Ende eine interessante\nDiskussion entstanden ist?",[3938,26254,26256],{"id":26255},"beidfüßig-und-alleskönner-oder-doch-einen-arjen-robben","Beidfüßig und Alleskönner oder doch einen Arjen Robben?",[439,26258,26259],{},"Arjen Robben ist im Fußball recht einzigartig. Er spielt offensiv auf der rechten Außenbahn, macht immer die gleiche\nFinte, ist linksfuß und hat den rechten Fuß nur, damit er beim laufen nicht umfällt. Auf anderen Positionen ist er lange\nnicht so effektiv. Dennoch ist er so wichtig für das Bayern Spiel, weil er Effizienz und das entscheidende Momentum mit\nbringt.",[439,26261,26262],{},"In der Softwareentwicklung kann man Robben das T-Modell gegenüber stellen. Dieses beschreibt spezialisiertes\nFachwissen (vertikaler Strich) und allgemeines Grundlagenwissen (horizontaler Strich). Es gibt in einem Team z. B.\njemanden der vorwiegend Interesse am Betrieb hat und jemanden der seinen Schwerpunkt in der Frontend Entwicklung hat.\nBeide können bei Abwesenheit des anderen aber auch dessen Arbeit erledigen. So hat man im Normalfall immer den Experten\ngriffbereit und der Experte darf auch mal Urlaub machen, ohne dass alles in seinem Bereich stehen bleibt.",[3938,26264,26266],{"id":26265},"freistoß-spray-torlinientechnik-videobeweis","Freistoß-Spray, Torlinientechnik, Videobeweis, …",[439,26268,26269],{},"Das anfangs belächelte Freistoß-Spray verhindert das nachträgliche Verschieben des Balles. Die anfangs umstrittene\nTorlinientechnik verhindert Millionenverluste wegen menschlichen Fehlentscheidungen. Der immer noch umstrittene\nVideobeweis wird spielentscheidende Fehlentscheidungen zumindest dezimieren.",[439,26271,26272,26273,1402],{},"In der Softwareentwicklung müssen wir genauso am Ball bleiben (sind wir nich alle ein bisschen Fußball? 🙂 ). Themen wie\nz. B. Betrieb mit Containern dürfen natürlich kritisch hinterfragt werden. Virtuelle Maschinen tuns ja auch, richtig?\nOder komplizierte Frontend Build Tools. Zum erstellen eines Bundles reicht doch\n",[471,26274,26275],{},"cat awesomeStuff-001.js >> app.bundle.js",[439,26277,26278],{},"Insbesondere in unserer Branche gilt es lernbeständig zu sein, stetig neue Methoden und Technologien zu evaluieren und\nauch mutig zu sein diese schrittweise zu etablieren.",[3938,26280,26282],{"id":26281},"barcelona-ist-kein-barcelona-wenn-messi-nicht-spielt","Barcelona ist kein Barcelona wenn Messi nicht spielt",[439,26284,26285],{},"Messi ist unbestritten einer der besten Fußballprofis des Planeten. Seine Aktionen und seine Anwesenheit bringt dem\nSpiel seines Teams den entscheidenden Vorteil. In Zahlen ausgedrückt hat die Argentinische Nationalmannschaft in der\nQualifikation zu Weltmeisterschaft 2018 mit Messi 6 Spiele bestritten und 5 gewonnen, ohne Messi 7 Spiele bestritten und\nnur 1 gewonnen. Das entspricht einer Siegquote von 83% (mit Messi) zu 14% (ohne Messi).",[439,26287,26288],{},"Dieses Thema lässt sich auf Teamarbeit im allgemeinen abbilden. Bezieht man sich auf den Ausfall eines Teammitglieds\nspricht man gerne vom sogenannten Truck Faktor. Wie viele Leute können vom Truck ausgeknockt werden, ohne den\nProjekterfolg zu gefährden. Finde eigentlich nur ich das etwas makaber?!? Naja, auf jeden fall darf der Erfolg nicht von\neiner Person abhängen. Bei uns gilt z. B. die Regel: kein Projekt wird mehr alleine bestritten. So steht einem drei\nWochenurlaub im Normalfall nichts entgegen, da die Last auf die Kollegen verteilt werden kann.",[3938,26290,26292],{"id":26291},"wieso-wechselt-der-denn-jetzt-den-besten-spieler-aus","“Wieso wechselt der denn jetzt den besten Spieler aus?!”",[439,26294,26295],{},"Steht man im Stadion kann man nicht immer alle Trainerentscheidungen nachvollziehen. “Wieso spielt der mit dreier\nKette?!”, “Wieso fängt der ausgerechnet mit dieser Startelf an?!”, “Wieso wechselt der den denn jetzt aus?!”. Nach dem\nSpiel stellt sich dann vielleicht heraus, dass jener bester Spieler eine leichte Verletzung hatte.",[439,26297,26298],{},[448,26299,26300],{},"Perspektivenwechsel / Empathie",[439,26302,26303],{},"Oft ist es hilfreich mal kurz durchzuatmen, einen Schritt zurück zu gehen und zu beobachten. Bei der Anforderungsanalyse\nz. B. hat es sich bewährt sogenannte Personas zu erstellen. Eine Persona ist die Beschreibung eines fiktiven Charakters\ndem die Software das Leben vereinfachen soll. Anhand der Persona kann das Team sich z. B. auf dessen Bedürfnisse\nfokussieren, statt mit noch mehr Vermutungen auch andere Möglichkeiten abzudecken.",[11947,26305,26306],{},[439,26307,26308],{},"“Von Sportlern lernen heißt siegen lernen”wird zu“Von Profis lernen heißt siegen lernen”",[439,26310,26311],{},"Profis im Sport haben alle etwas gemeinsames. Das gewisse Etwas weshalb sie erfolgreich wurden und weiterhin bleiben.\nSei es im Teamsport oder auch im Einzelsport (Tennis, Leichtathletik, …). Wir können viel von Ihnen lernen, bestimmt.\nAber auch Sportler können von uns lernen. Professionellen Softwareentwicklern, Agile Coaches und generell Teams im\nBerufsleben. In beide Richtungen lassen sich Analogien bilden. Probiert es aus. Experimentiert.",[439,26313,26314],{},"Und bleibt am Ball 😉",{"title":469,"searchDepth":507,"depth":507,"links":26316},[26317,26318,26319,26320,26321,26322,26323],{"id":26222,"depth":507,"text":26223},{"id":26232,"depth":507,"text":26233},{"id":26242,"depth":507,"text":26243},{"id":26255,"depth":507,"text":26256},{"id":26265,"depth":507,"text":26266},{"id":26281,"depth":507,"text":26282},{"id":26291,"depth":507,"text":26292},[1031],"2017-05-04T12:33:31","https://synyx.de/blog/von-profis-lernen-heisst-siegen-lernen/",{},"/blog/von-profis-lernen-heisst-siegen-lernen",{"title":26170,"description":469},"von-profis-lernen-heisst-siegen-lernen","blog/von-profis-lernen-heisst-siegen-lernen",[26333,15546,26334],"analogien","profisport","Der nächste Sprint steht an… Wir treffen uns am Dienstag Morgen um 10:00 Uhr zum Refinement. Es gilt noch einige Themen fachlich genauer zu hinterfragen. Auch technische Grundlagen müssen noch…","vic-ozc1OThUJSebUDBMCTXgtICWalJTj6v7retM5z0",{"id":26338,"title":26339,"author":26340,"body":26341,"category":26627,"date":26628,"description":26629,"extension":1034,"link":17545,"meta":26630,"navigation":916,"path":26631,"seo":26632,"slug":26345,"stem":26634,"tags":26635,"teaser":26639,"__hash__":26640},"blog/blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts.md","Urlaubsverwaltung – Die Geschichte eines Open Source Projekts",[30],{"type":432,"value":26342,"toc":26615},[26343,26346,26353,26367,26370,26373,26377,26380,26387,26400,26404,26414,26424,26428,26435,26439,26456,26469,26482,26486,26504,26512,26516,26527,26530,26534,26541,26545,26552,26556,26565,26569,26575,26584,26591,26595,26598],[435,26344,26339],{"id":26345},"urlaubsverwaltung-die-geschichte-eines-open-source-projekts",[439,26347,26348,26349],{},"Nach fast 6 Jahren hat das Open Source Projekt,\ndie ",[1002,26350,26352],{"href":17440,"rel":26351},[1006],"synyx Urlaubsverwaltung",[994,26354,26355,26358,26361,26364],{},[997,26356,26357],{},"70 offene Issues",[997,26359,26360],{},"194 gelöste Issues",[997,26362,26363],{},"2207 Commits",[997,26365,26366],{},"99 Releases",[439,26368,26369],{},"Stand heute, 26. April 2017",[439,26371,26372],{},"…und eine kleine Geschichte, die durchaus erzählenswert ist und auch ein bisschen meine eigene Geschichte spiegelt.",[3938,26374,26376],{"id":26375},"die-geburtsstunde-der-urlaubsverwaltung","Die Geburtsstunde der Urlaubsverwaltung",[439,26378,26379],{},"Alles begann damit, dass ich mein Studium der Biowissenschaften schmiss und bei synyx einstieg. Quasi ohne jegliche\nVorkenntnisse in Programmierung begann ich im August 2011 mit einer Ausbildung zur Fachinformatikerin für\nAnwendungsentwicklung – was im Nachhinein betrachtet von beiden Seiten irgendwie ziemlich mutig war 😀 In allerlei\nÜbungsprojekten eignete ich mir Wissen in objektorientierter Programmierung im Allgemeinen und Java im Speziellen an.",[439,26381,26382,26383,26386],{},"Im Oktober 2011 wurde es dann an der Zeit für ein ",[990,26384,26385],{},"“richtiges”"," Projekt. Zum damaligen Zeitpunkt wurde Urlaub bei synyx\nnoch ganz altmodisch in Papierform beantragt und genehmigt. Dieser Prozess sollte in Software abgebildet werden, um uns\nallen das Beantragen von Urlaub zu vereinfachen.",[439,26388,26389,26390,26393,26394,26399],{},"Am ",[448,26391,26392],{},"20. Oktober 2011"," erfolgte\nder ",[1002,26395,26398],{"href":26396,"rel":26397},"https://github.com/synyx/urlaubsverwaltung/commit/d194f12e3d6344495cc73c240f58a6d18bca8b83",[1006],"erste Commit"," – dies\nwar die Geburtsstunde der Urlaubsverwaltung.",[3938,26401,26403],{"id":26402},"das-erste-krabbeln","Das erste Krabbeln",[439,26405,26389,26406,26409,26410,26413],{},[448,26407,26408],{},"5. März 2012"," ging die erste Version der Urlaubsverwaltung ",[448,26411,26412],{},"live",". Diese erste Version beinhaltete die\ngrundlegenden Anforderungen ohne viel Schnickschnack. Benutzer (Mitarbeiter) konnten Urlaub beantragen und Benutzer mit\nSonderrechten (Chefs) konnten ihn genehmigen bzw. ablehnen.",[439,26415,26416,26417,26420,26421,17548],{},"Durch den relativ schnellen Live-Gang mit den grundlegenden Features konnten Bugs früh behoben (",[990,26418,26419],{},"“Oh, an den Fall hab\nich ja beim Testen und Durchklicken gar nicht gedacht”",") und die Benutzbarkeit der Features zügig optimiert werden (\n",[990,26422,26423],{},"“Stimmt, das ist eigentlich ganz schön umständlich und unübersichtlich”",[3938,26425,26427],{"id":26426},"ein-projekt-lernt-laufen","Ein Projekt lernt laufen",[439,26429,26430,26431,26434],{},"In kurzer Zeit hatten wir eine benutzbare Software, die den Prozess in Papierform abgelöst hatte. Anfangs erfolgte die\nUrlaubsbeantragung sowohl analog als auch digital, d.h. man beantragte Urlaub über die Urlaubsverwaltung und passte\nanschließend noch den Chef mit Papier und Kugelschreiber ab. Im ",[448,26432,26433],{},"Juni 2012"," trauten wir uns dann endgültig, den *\n*Papierkrieg aufzugeben** und die Urlaubsbeantragung nur noch über die Software durchzuführen.",[3938,26436,26438],{"id":26437},"ein-projekt-zieht-in-die-welt-hinaus","Ein Projekt zieht in die Welt hinaus",[439,26440,26441,26442,26447,26448,26451,26452,26455],{},"Aufgrund eines ",[1002,26443,26446],{"href":26444,"rel":26445},"https://synyx.de/2012/11/urlaubsverwaltung-was-hat-sich-getan/",[1006],"Blog-Posts"," kamen im ",[448,26449,26450],{},"Juni 2013","\nrelativ zeitgleich die ",[448,26453,26454],{},"ersten Anfragen"," zur Urlaubsverwaltung. Es gab also noch mehr Unternehmen, die dasselbe\nProblem zu lösen hatten.",[439,26457,26458,26459,26462,26463,8351,26466,1402],{},"Wir entschlossen uns das Projekt als Open Source zur freien Nutzung zur Verfügung zu stellen. Es wurden noch einige\nkleine Anpassungen vorgenommen und schließlich die Urlaubsverwaltung am ",[448,26460,26461],{},"18. Juli 2013","\nauf ",[1002,26464,4042],{"href":17440,"rel":26465},[1006],[448,26467,26468],{},"veröffentlicht",[439,26470,26471,26472,8764,26477,17548],{},"Relativ zeitgleich zog auch ich in die Welt hinaus und durfte in den ersten Kundenprojekten arbeiten. Dadurch reduzierte\nsich die Weiterentwicklung an der Urlaubsverwaltung vorerst. Natürlich gab es immer wieder neue Anforderungen zu lösen,\num unseren Prozess noch weiter zu optimieren, aber es handelte sich meist nur um kleinere Features. Viel Feinschliff\nmachte ich in meiner 20%-Weiterbildungszeit oder probierte (für mich) neue Technologien einfach in der\nUrlaubsverwaltung aus (\nz.B. ",[1002,26473,26476],{"href":26474,"rel":26475},"https://synyx.de/2012/06/scheduling-and-asynchronous-execution-with-spring/",[1006],"Spring Scheduled",[1002,26478,26481],{"href":26479,"rel":26480},"https://synyx.de/2012/05/how-to-monitor-and-manage-your-java-application-with-jmx/",[1006],"JMX",[3938,26483,26485],{"id":26484},"umstyling-von-außen-und-von-innen","Umstyling – von außen und von innen",[439,26487,3786,26488,26491,26492,26497,26498,26503],{},[448,26489,26490],{},"Oktober 2014"," stand für synyx der ",[1002,26493,26496],{"href":26494,"rel":26495},"https://synyx.de/2014/09/wir-ziehen-um/",[1006],"Umzug"," ins neue Büro an. Die Woche,\ndie ich aufgrunddessen im Home Office verbrachte, nutzte ich für\nzahlreiche ",[1002,26499,26502],{"href":26500,"rel":26501},"https://synyx.de/2014/10/urlaubsverwaltung-goes-mobile/",[1006],"Umgestaltungsarbeiten"," an der Urlaubsverwaltung.\nIch glaube, aus dieser Zeit stammen meine meisten nächtlichen Commits 😀 Während sich synyx ein neues Gewand in Form\neines neuen Büros anzog, tat es auch die Urlaubsverwaltung in Form einer verbesserten Oberfläche. Nun war es möglich,\nUrlaub auf einem Smartphone Browser zu beantragen, ohne die Krise zu kriegen.",[439,26505,26506,26507,26511],{},"Da wir immer wieder E-Mails mit Fragen bzgl. Installation und Konfiguration erhielten, entschlossen wir uns im *\n*Oktober 2015**, die Urlaubsverwaltung auf ",[1002,26508,18586],{"href":26509,"rel":26510},"https://projects.spring.io/spring-boot/",[1006]," umzustellen. Dies\nbrachte einige Vereinfachungen in der Konfiguration und erleichterte interessierten Unternehmen die Nutzung der\nUrlaubsverwaltung.",[3938,26513,26515],{"id":26514},"ein-projekt-wird-erwachsen","Ein Projekt wird erwachsen",[439,26517,26518,26519,26522,26523,26526],{},"Ab ",[448,26520,26521],{},"2015"," bekamen wir ",[448,26524,26525],{},"vermehrt Anfragen"," zu Anpassungswünschen in der Urlaubsverwaltung. Es gab sogar Unternehmen,\ndie bereit waren für die gewünschten Anpassungen zu bezahlen. Wir hatten mehrere kleine Aufträge zur Umsetzung von\nFeatures, von denen wir annahmen, dass auch andere Unternehmen davon profitieren würden.",[439,26528,26529],{},"Leider stellte sich nach einigen Aufträgen heraus, dass wir immer nur einen Kompromiss umsetzen konnten. Die Anfragen\nund erforderlichen Anpassungen waren meist sehr spezifisch auf einen Kunden zugeschnitten. Einen Mittelweg zu finden,\nder sowohl den eindeutigen Kundenwunsch, als auch die potenziellen Wünsche aller anderen Benutzer der Urlaubsverwaltung\nzufriedenstellt – und dazu noch in das vorhandene Budget passt, war einfach nicht möglich. Oft haben wir ein Auge\nzugedrückt und den Mehraufwand auf unsere Kappe genommen. Als reines Dienstleistungsunternehmen ist das auf lange Sicht\nnatürlich nicht machbar.",[3938,26531,26533],{"id":26532},"weniger-ist-manchmal-mehr","Weniger ist manchmal mehr",[439,26535,26536,26537,26540],{},"Wir kamen zu dem Punkt, an dem wir uns die Frage stellen mussten: ",[448,26538,26539],{},"Wie soll es mit der Urlaubsverwaltung weitergehen?","\nIn einer Task Force von freiwilligen Interessierten besprachen wir die Möglichkeiten zur weiteren Strategie.",[1633,26542,26544],{"id":26543},"sollen-wir-weiterhin-als-dienstleister-agieren-und-individuelle-wünsche-umsetzen","Sollen wir weiterhin als Dienstleister agieren und individuelle Wünsche umsetzen?",[439,26546,26547,26548,26551],{},"Wir waren uns einig, dass dies theoretisch möglich wäre, aber sinnvollerweise dann nur als Fork der Hauptversion\ngeschehen sollte. Wir wollten künftig vermeiden, dass spezielle Sonderwünsche in die Hauptversion einfließen und deren\nWart- und Erweiterbarkeit verschlechterten. Dies würde allerdings bedeuten, dass wir Support für unterschiedliche\nVersionen leisten müssten. Eine ",[990,26549,26550],{},"“Nebenbei-Entwicklung”"," der Urlaubsverwaltung, wie sie bisher zeitgleich zu unserem\neigentlichen Kundengeschäft betrieben wurde, wäre dann realistisch betrachtet nicht mehr sinnvoll machbar.",[1633,26553,26555],{"id":26554},"sollen-wir-die-urlaubsverwaltung-als-produkt-sehen-und-vermehrt-zeit-in-die-produktentwicklung-stecken-eventuell-als-cloudlösung","Sollen wir die Urlaubsverwaltung als Produkt sehen und vermehrt Zeit in die Produktentwicklung stecken, eventuell als Cloudlösung?",[439,26557,26558,26559,26564],{},"Nach Evaluierung der Marktlage, was bereits verfügbare Lösungen, deren Preise und in Frage kommende Kundengruppen\nanbelangt, kamen wir zu dem Schluss, dass wir uns geschäftlich lieber anderweitig orientieren und die **synyx\nUrlaubsverwaltung nur noch (wie ursprünglich) als ",[1002,26560,26563],{"href":26561,"rel":26562},"https://synyx.de/open-source/",[1006],"Open Source"," Projekt betreiben wollen.\n**",[3938,26566,26568],{"id":26567},"ist-das-der-tod-eines-projekts","Ist das der Tod eines Projekts?",[439,26570,26571,26572,1402],{},"Die Geschichte der Urlaubsverwaltung endet hier sicherlich noch nicht. Aber das Projekt hat einen Status erreicht, in\ndem es (meistens) stabil läuft und viele typische Anforderungen erfüllt. Es wird sicherlich immer wieder das ein oder\nandere neue Feature geben, aber wir haben für uns beschlossen: ",[990,26573,26574],{},"back to the roots, weniger ist manchmal mehr",[439,26576,26577,26578,26583],{},"Neue Anfragen leiten wir nun an einen von\nuns ",[1002,26579,26582],{"href":26580,"rel":26581},"http://www.andre-janus.de/",[1006],"gebrieften freiberuflichen Softwareentwickler"," weiter. Er steht für entsprechende\nSupport- und Anpassungsaufträge gerne zur Verfügung. Erscheinen die Anpassungen für alle Benutzer der Urlaubsverwaltung\nsinnvoll, können diese problemlos mittels eines Pull Requests in die Hauptversion einfließen",[439,26585,26586,26587,26590],{},"Denn das ist ja das Coole an unserer Open Source Urlaubsverwaltung: man kann sie nicht nur ",[990,26588,26589],{},"as it is"," kostenfrei nutzen,\nsondern sie auch für eigene Zwecke modifizieren und diese angepasste Version nutzen.",[1065,26592,26594],{"id":26593},"und-meine-geschichte","Und meine Geschichte?",[439,26596,26597],{},"Die endet hier sicherlich auch noch nicht. Aber ich bin erst einmal raus. Im Dezember 2016 bin ich Mutter geworden und\nhabe seither ein neues Projekt 🙂",[439,26599,26600,26601,26605,26606,26609,26610,26614],{},"Seitdem ist ",[1002,26602,125],{"href":26603,"rel":26604},"https://github.com/honnel",[1006]," der Hauptansprechpartner für die Urlaubsverwaltung als Open\nSource Projekt von synyx. Auf ",[1002,26607,4042],{"href":17440,"rel":26608},[1006],"\nbin ",[1002,26611,26206],{"href":26612,"rel":26613},"https://github.com/fraulyoner",[1006]," momentan ein eher stiller Beobachter.",{"title":469,"searchDepth":507,"depth":507,"links":26616},[26617,26618,26619,26620,26621,26622,26623,26624],{"id":26375,"depth":507,"text":26376},{"id":26402,"depth":507,"text":26403},{"id":26426,"depth":507,"text":26427},{"id":26437,"depth":507,"text":26438},{"id":26484,"depth":507,"text":26485},{"id":26514,"depth":507,"text":26515},{"id":26532,"depth":507,"text":26533},{"id":26567,"depth":507,"text":26568,"children":26625},[26626],{"id":26593,"depth":547,"text":26594},[1031],"2017-04-27T09:12:00","Nach fast 6 Jahren hat das Open Source Projekt,\\ndie synyx Urlaubsverwaltung",{},"/blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts",{"title":26339,"description":26633},"Nach fast 6 Jahren hat das Open Source Projekt,\ndie synyx Urlaubsverwaltung","blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts",[26636,389,26637,26638],"open-source","urlaub","urlaubsverwaltung","Nach fast 6 Jahren hat das Open Source Projekt, die synyx Urlaubsverwaltung 70 offene Issues 194 gelöste Issues 2207 Commits 99 Releases Stand heute, 26. April 2017 …und eine kleine…","sqByx995rAv-g0KvNPE3uIElakRwwEpLi0nwRHAg1sI",{"id":26642,"title":26643,"author":26644,"body":26645,"category":26717,"date":26718,"description":26719,"extension":1034,"link":26720,"meta":26721,"navigation":916,"path":26722,"seo":26723,"slug":26649,"stem":26724,"tags":26725,"teaser":26728,"__hash__":26729},"blog/blog/synyx-went-wild.md","synyx went wild!",[412],{"type":432,"value":26646,"toc":26715},[26647,26650,26653,26656,26665,26668,26677,26680,26683,26692,26695,26704,26707],[435,26648,26643],{"id":26649},"synyx-went-wild",[439,26651,26652],{},"Früh klingelte der Wecker am Freitag morgen. Deutlicher früher als gewohnt, doch bevor der erste Alarmton zur Höchstform\nauflaufen konnte, war ich bereits hellwach – synyxCamp! Am Office angelangt bot sich ein Bild, das an jugendliche\nKlassenfahrten erinnerte: Euphorisch-aufgeregte Leute huschen von hier nach da, packen ein paar letzte Sachen,\nschleppen allerlei Kisten Richtung Fahrstuhl und scherzen dabei über das Bevorstehende. Noch nicht im Bus angelangt –\nhatten wir unser Büro und den Alltag bereits hinter uns gelassen. Die Routine wich an diesem Morgen zurück, verdrängt\nvon Spannung und Vorfreude.",[439,26654,26655],{},"Nach etwas mehr als einer Stunde Fahrt war das Ziel erreicht. Wir erspähten unsere Unterkunft auf einer sonnigen\nLichtung, umringt von den charakteristischen Nadelbäumen des Schwarzwalds. Die Feuchtigkeit der vergangenen Nacht ließ\ndas Gras auf der anliegenden Wiese glitzern – Natur pur. Innerhalb des Hotels erwartete uns bereits ein ausgiebiges\nFrühstücksbuffet, das dem synyx-eigenen frühstyxx in Nichts nach Stand: deftige Speisen, frisches Obst und Gemüse,\nSäfte, Kaffee und etliche Teesorten.",[439,26657,26658],{},[1002,26659,26662],{"href":26660,"rel":26661},"https://media.synyx.de/uploads//2017/04/frueh1.jpg",[1006],[2205,26663],{"alt":469,"src":26664},"https://media.synyx.de/uploads//2017/04/frueh1-1024x683.jpg",[439,26666,26667],{},"Nach ausreichender Stärkung, widmeten wir uns dem organisatorischen Teil. Viele interessante Themen-Vorschläge fanden\nden Weg an unsere Camp-Timetable: Kotlin-Hacking Sessions; eine Devoxx4Kids-Gruppe näherte sich dem Trend IoT; das\nhauseigene Buchungstool erhielt neue Features. Doch wer glaubt, dass wir uns ausschließlich reinen IT-Themen zugewandt\nhaben, begibt sich auf den Holzweg – das Marketing-Team gewährte Interessierten beispielsweise eine Übersicht in der\nWelt der Wortschöpfungen; die Geschäftsführung eröffnete uns einen tiefen Einblick in die 15-jährige Firmengeschichte.\nSo sprinteten wir enthusiastisch von Workshop zu Workshop durch den Arbeitstag, der lediglich vom gemeinsamen\nMittagessen unterbrochen wurde.",[439,26669,26670],{},[1002,26671,26674],{"href":26672,"rel":26673},"https://media.synyx.de/uploads//2017/04/timetable2.jpg",[1006],[2205,26675],{"alt":469,"src":26676},"https://media.synyx.de/uploads//2017/04/timetable2-1024x683.jpg",[439,26678,26679],{},"Als sich die Sonne gemächlich verabschiedete, näherten sich auch die letzten Sessions ihrem Ende. Stolz und erfüllt von\nden Ergebnissen des ersten Tages, steuerten wir das gut-bürgerliche Buffet an, das den Grundstein für einen gemeinsamen\nAbend legte. Bei Wein, Bier und Brettspielen resümierten wir den ersten Camp-Tag, sprachen über den Schwarzwald und\nallerhand persönliche Dinge.",[439,26681,26682],{},"Der folgende Tag begrüßte uns erneut mit kräftigem Sonnenschein und wir starteten mit vollem Elan in den zweiten Teil\nunseres Camp-Aufenthalts. Die Themenvielfalt erstreckte sich auch an diesem Tag weit über das Gebiet der Informatik\nhinaus, was einige synyxer nutzten um Erfahrungen und Eindrücke aus anderen Tätigkeitsbereichen zu sammeln. Mit\nwertvollen Erkenntnissen und unzähligen neuen Zeilen Code im Gepäck, läuteten wir am Abend eine feucht-fröhliche Runde\nein. Die urig-eingerichtete Kellerbar des Hotels bot uns den optimalen Rahmen um das erste synyxCamp gebührend zu\nfeiern.",[439,26684,26685],{},[1002,26686,26689],{"href":26687,"rel":26688},"https://media.synyx.de/uploads//2017/04/draussen3.jpg",[1006],[2205,26690],{"alt":469,"src":26691},"https://media.synyx.de/uploads//2017/04/draussen3-1024x683.jpg",[439,26693,26694],{},"Mit dem ein oder anderen Kater im Schlepptau, ging es am Sonntag Vormittag zurück nach Karlsruhe in die Gartenstraße.\nNachdem unsere Hardware wieder im Büro verstaut war, gesellten sich ein paar synyxer auf der Terrasse zusammen, um\nnochmals Revue passieren zu lassen. So ließen wir ein ereignisreiches Wochenende ausklingen, das seinen Erwartungen mehr\nals gerecht wurde. Wir haben gehackt, gecodet, organisiert, diskutiert, argumentiert und präsentiert – zusammen\ngegessen, gelacht, getrunken, Paraglider bestaunt, Frisbee gespielt und die Sonne genossen. Das erste synyxCamp hat ein\nFeuer geschürt, das sich nicht löschen lässt und zukünftig weiter angeheizt werden will.",[439,26696,26697],{},[1002,26698,26701],{"href":26699,"rel":26700},"https://media.synyx.de/uploads//2017/04/fly4.jpg",[1006],[2205,26702],{"alt":469,"src":26703},"https://media.synyx.de/uploads//2017/04/fly4-1024x683.jpg",[439,26705,26706],{},"So… let‘s go wild again synyx! 😉",[439,26708,26709,26710,18773],{},"Noch mehr synyxCamp gibt es in diesem ",[1002,26711,26714],{"href":26712,"rel":26713},"https://youtu.be/TaQ-9muog-E",[1006],"Video",{"title":469,"searchDepth":507,"depth":507,"links":26716},[],[1031],"2017-04-13T15:37:30","Früh klingelte der Wecker am Freitag morgen. Deutlicher früher als gewohnt, doch bevor der erste Alarmton zur Höchstform\\nauflaufen konnte, war ich bereits hellwach – synyxCamp! Am Office angelangt bot sich ein Bild, das an jugendliche\\nKlassenfahrten erinnerte: Euphorisch-aufgeregte Leute huschen von hier nach da, packen ein paar letzte Sachen,\\nschleppen allerlei Kisten Richtung Fahrstuhl und scherzen dabei über das Bevorstehende. Noch nicht im Bus angelangt –\\nhatten wir unser Büro und den Alltag bereits hinter uns gelassen. Die Routine wich an diesem Morgen zurück, verdrängt\\nvon Spannung und Vorfreude.","https://synyx.de/blog/synyx-went-wild/",{},"/blog/synyx-went-wild",{"title":26643,"description":26652},"blog/synyx-went-wild",[17502,15253,26726,26727],"synyxcamp","synyxgoeswild","Früh klingelte der Wecker am Freitag morgen. Deutlicher früher als gewohnt, doch bevor der erste Alarmton zur Höchstform auflaufen konnte, war ich bereits hellwach – synyxCamp! Am Office angelangt bot…","p4XR--yXREJNiTM-VT3Ijmg1xwlyE7gSVUev8YEncN8",{"id":26731,"title":26732,"author":26733,"body":26734,"category":26911,"date":26912,"description":26913,"extension":1034,"link":26914,"meta":26915,"navigation":916,"path":26916,"seo":26917,"slug":26738,"stem":26919,"tags":26920,"teaser":26924,"__hash__":26925},"blog/blog/devoxx4kids-javaland4kids.md","Devoxx4Kids @ JavaLand4Kids",[124,332],{"type":432,"value":26735,"toc":26906},[26736,26739,26766,26770,26778,26781,26790,26798,26805,26814,26817,26819,26828,26851,26855,26862,26865,26881,26884],[435,26737,26732],{"id":26738},"devoxx4kids-javaland4kids",[439,26740,26741,26742,26747,26748,26753,26754,26759,26760,26765],{},"Dieses Jahr waren wir, ",[1002,26743,26746],{"href":26744,"rel":26745},"http://www.devoxx4kids.de/",[1006],"Devoxx4Kids Deutschland",", wieder als Mentoren mit bei der\nOrganisation und Durchführung\nder ",[1002,26749,26752],{"href":26750,"rel":26751},"https://web.archive.org/web/20160420190058/http://www.javaland.eu:80/de/javaland4kids/",[1006],"JavaLand4Kids"," dabei. Die\nvon der ",[1002,26755,26758],{"href":26756,"rel":26757},"https://www.doag.org/de/home/",[1006],"DOAG"," organisierte Veranstaltung findet alljährlich im Rahmen\nder ",[1002,26761,26764],{"href":26762,"rel":26763},"https://www.javaland.eu/de/home/",[1006],"JavaLand Konferenz"," statt. Am Tag 0, bevor die “Alt”-Entwickler zum\nPhantasialand in Brühl strömen, haben dort 30 Kinder aus den vierten Klassen der Brühler Sankt August Grundschule die\nChance mit viel Spiel und Spaß erste Kenntnisse in der Programmierung zu erlernen.",[3938,26767,26769],{"id":26768},"jumping-sumo-4-scratch","Jumping Sumo 4 Scratch",[439,26771,26772,26773,26777],{},"Wir hatten dieses Jahr die Möglichkeit die 30 Kinder mit\nunserem ",[1002,26774,26776],{"href":26054,"rel":26775},[1006],"Jumping Sumo 4 Scratch Workshop"," spielerisch\nan die Informatik heranzuführen.",[439,26779,26780],{},"Die kleinen robusten Jumping Sumo Roboter sind mit zwei von einander getrennt ansteuerbaren Rädern ausgestattet, wodurch\nsie in sehr flink in beliebige Richtungen fahren können. Mit Hilfe eines Sprungmechanismus können sie außerdem bis zu\neinem Meter hoch springen. Zusätzlich kann durch die verbaute Sensorik die Lage des Roboters festgestellt und angepasst\nwerden. Zu guter Letzt ist über die integrierte Kamera die Möglichkeit vorhanden durch die “Augen” des Roboters die\nUmgebung zu erkunden.",[439,26782,26783],{},[1002,26784,26787],{"href":26785,"rel":26786},"https://media.synyx.de/uploads//2017/03/IMG_20170327_091100.jpg",[1006],[2205,26788],{"alt":469,"src":26789},"https://media.synyx.de/uploads//2017/03/IMG_20170327_091100-1024x768.jpg",[439,26791,26792,26793,26797],{},"Die Steuerung bei diesem Workshop basiert auf der graphischen Programmierung\nvia ",[1002,26794,26796],{"href":4032,"rel":26795},[1006],"Scratch 2.0",". Die einzelnen Anweisungen werden wie Puzzleteile mit einander verbunden\nund bilden einen Ablauf welcher durch unterschiedliche Ereignisse gestartet werden kann. Damit ist es zum Beispiel\nmöglich durch einen Tastendruck eine Bewegungsabfolge des kleinen Roboters zu starten.",[439,26799,26800,26801,26804],{},"Die Kommunikation zwischen dem zur Programmierung verwendeten Computer und dem Jumping Sumo wird über WLAN und\nden ",[1002,26802,26050],{"href":26048,"rel":26803},[1006]," sichergestellt und bildet die technische\nBasis unseres Workshops. Dieser Controller wurde dafür in mühevoller Kleinarbeit reverse-engenieered da das genaue\nProtokoll bis zu diesem Zeitpunkt nicht bekannt war.",[439,26806,26807],{},[1002,26808,26811],{"href":26809,"rel":26810},"https://media.synyx.de/uploads//2017/03/2017-03-27-12.21.13-e1490863119907.jpg",[1006],[2205,26812],{"alt":469,"src":26813},"https://media.synyx.de/uploads//2017/03/2017-03-27-12.21.13-e1490863119907-1024x576.jpg",[439,26815,26816],{},"Ziel des Workshops ist es dem kleinen Roboter bei seiner Marsmission zu helfen. Diese startet allerdings mit einer\nBruchlandung wodurch die Programme des Jumping Sumos durcheinander gekommen sind. Die Kinder erhielten die Aufgabe ihren\nJumping Sumo durch ihre Programmierung wieder zu reparieren. Für diese Aufgabe wurden verschiedene Progammbausteine\nbenötigt. Anhand dieser Bausteine konnten wir den Kindern zahlreiche Programmierkonzepte wie Bedingungen, Schleifen und\nMethoden spielerisch näher bringen und durch das direkte Feedback des Jumping Sumos einfacher vermitteln.",[3938,26818,4011],{"id":4010},[439,26820,26821,26822,26827],{},"Bauen und spielen. Das Erfolgsrezept von Lego wurde auch auf der Javaland4Kids aufgenommen. Die Kinder durften ihren\neigenen Roboter aufbauen und einen einfachen Wegfolge-Algorithmus programmieren, sodass ihr Roboter einer roten Linie\nfolgt. Als kleiner Helfer stand den Kindern ",[1002,26823,26826],{"href":26824,"rel":26825},"https://www.ald.softbankrobotics.com/en/cool-robots/pepper",[1006],"Pepper"," zur\nSeite.",[439,26829,26830,26837,26844],{},[1002,26831,26834],{"href":26832,"rel":26833},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.33.47-e1490863662532.jpg",[1006],[2205,26835],{"alt":469,"src":26836},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.33.47-e1490863662532-1024x576.jpg",[1002,26838,26841],{"href":26839,"rel":26840},"https://media.synyx.de/uploads//2017/03/IMG_20170327_140827664.jpg",[1006],[2205,26842],{"alt":469,"src":26843},"https://media.synyx.de/uploads//2017/03/IMG_20170327_140827664-576x1024.jpg",[1002,26845,26848],{"href":26846,"rel":26847},"https://media.synyx.de/uploads//2017/03/IMG_20170327_113718.jpg",[1006],[2205,26849],{"alt":469,"src":26850},"https://media.synyx.de/uploads//2017/03/IMG_20170327_113718-768x1024.jpg",[3938,26852,26854],{"id":26853},"chibitronics","Chibitronics",[439,26856,3786,26857,26861],{},[1002,26858,26854],{"href":26859,"rel":26860},"https://chibitronics.com/",[1006]," Workshops drehte sich alles um Kupferleiterbahnen und deren Verdrahtung.\nAuf der Rückseite der Kupferleiterbahnen sind Klebestreifen aufgebraucht, wodurch diese auf Papier aufgeklebt werden\nkönnen. Damit war es den Kindern möglich einfache Schaltkreise zu kleben und mittels einer Knopfzelle den Schaltkreis zu\nschließen und die LEDs zum leuchten zu bringen.",[439,26863,26864],{},"Die Kinder erhielten die Aufgabe Osterkarten zu basteln und die Nase des Hasen zum Leuchten zu bringen.",[439,26866,26867,26874],{},[1002,26868,26871],{"href":26869,"rel":26870},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.53.02-e1490863318470.jpg",[1006],[2205,26872],{"alt":469,"src":26873},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.53.02-e1490863318470-576x1024.jpg",[1002,26875,26878],{"href":26876,"rel":26877},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.48.57-e1490863304407.jpg",[1006],[2205,26879],{"alt":469,"src":26880},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.48.57-e1490863304407-576x1024.jpg",[439,26882,26883],{},"Ein großer Dank geht an das Organisationsteam rund um Simone Fischer die dieser Devoxx4Kids auf der Javaland4Kids einen\ntollen Rahmen gegeben haben.",[439,26885,26886,26887,26890,26891,26894,26895,26900,26901,1402],{},"Die nächste Devoxx4Kids werden in ",[448,26888,26889],{},"Wiesbaden am 22.4.2017"," und in ",[448,26892,26893],{},"Karlsruhe am 06.05.2017"," stattfinden. Alle\nInformationen dazu gibt es auf ",[1002,26896,26899],{"href":26897,"rel":26898},"http://www.Devoxx4Kids.de",[1006],"Devoxx4Kids.de"," und\nüber ",[1002,26902,26905],{"href":26903,"rel":26904},"https://twitter.com/devoxx4kidsde",[1006],"Twitter",{"title":469,"searchDepth":507,"depth":507,"links":26907},[26908,26909,26910],{"id":26768,"depth":507,"text":26769},{"id":4010,"depth":507,"text":4011},{"id":26853,"depth":507,"text":26854},[4216],"2017-03-31T09:50:08","Dieses Jahr waren wir, Devoxx4Kids Deutschland, wieder als Mentoren mit bei der\\nOrganisation und Durchführung\\nder JavaLand4Kids dabei. Die\\nvon der DOAG organisierte Veranstaltung findet alljährlich im Rahmen\\nder JavaLand Konferenz statt. Am Tag 0, bevor die “Alt”-Entwickler zum\\nPhantasialand in Brühl strömen, haben dort 30 Kinder aus den vierten Klassen der Brühler Sankt August Grundschule die\\nChance mit viel Spiel und Spaß erste Kenntnisse in der Programmierung zu erlernen.","https://synyx.de/blog/devoxx4kids-javaland4kids/",{},"/blog/devoxx4kids-javaland4kids",{"title":26732,"description":26918},"Dieses Jahr waren wir, Devoxx4Kids Deutschland, wieder als Mentoren mit bei der\nOrganisation und Durchführung\nder JavaLand4Kids dabei. Die\nvon der DOAG organisierte Veranstaltung findet alljährlich im Rahmen\nder JavaLand Konferenz statt. Am Tag 0, bevor die “Alt”-Entwickler zum\nPhantasialand in Brühl strömen, haben dort 30 Kinder aus den vierten Klassen der Brühler Sankt August Grundschule die\nChance mit viel Spiel und Spaß erste Kenntnisse in der Programmierung zu erlernen.","blog/devoxx4kids-javaland4kids",[26853,4216,11539,26921,26165,9556,26922,26923,24653],"javaland4kids","lego","pepper","Dieses Jahr waren wir, Devoxx4Kids Deutschland, wieder als Mentoren mit bei der Organisation und Durchführung der JavaLand4Kids dabei. Die von der DOAG organisierte Veranstaltung findet alljährlich im Rahmen der JavaLand…","iPxpWN7xqoSTOEbJ_LOz39qKI0-Kuk-xJ0SR2tWItFM",{"id":26927,"title":26928,"author":26929,"body":26930,"category":27180,"date":27181,"description":27182,"extension":1034,"link":27183,"meta":27184,"navigation":916,"path":27185,"seo":27186,"slug":26934,"stem":27187,"tags":27188,"teaser":27196,"__hash__":27197},"blog/blog/visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana.md","Visualising sensors and coffee machines with ESP8266, MQTT, InfluxDB and Grafana",[292],{"type":432,"value":26931,"toc":27178},[26932,26935,26938,26941,26944,26947,26950,26959,26965,26968,26979,26982,26987,26990,26999,27002,27007,27010,27013,27016,27019,27024,27027,27030,27035,27038,27041,27044,27047,27050,27053,27056,27061,27064,27067,27081,27084,27087,27092,27095,27100,27103,27108,27111,27114,27117,27120,27123,27160,27164,27171],[435,26933,26928],{"id":26934},"visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana",[439,26936,26937],{},"A few months ago, here at the synyx office we started out with a simple idea: hook up a couple of sensors to an ESP8266\nmodule (or twenty) and have it write its data somewhere for visualisation purposes. Then we got creative.",[439,26939,26940],{},"Our current setup consists out of a number of ESP8266 (currently NodeMCU) boards, featuring humidity (DHT-22, BME280)\nas well as CO2 (MH-Z14) sensors which publish their data over MQTT. In addition, we also hooked the Jura coffee\nmachines in the office up to a couple of ESP8266 modules via their serial interface to read out statistics.",[439,26942,26943],{},"All of this is managed centrally from a command & control server (C&C) which communicates over the MQTT broker (\nMosquitto), with the nodes actively announcing themselves when they come online and receiving their configuration from\nthe C&C. ESP8266 firmware updates are provided as Over The Air (OTA) updates via HTTP.",[439,26945,26946],{},"A custom MQTT-to-Influx service (based on libmosquitto and POCO) is used to write published sensor and coffee machine\nMQTT events into the InfluxDB instance using its HTTP line protocol. A Grafana instance then uses these time series to\nshow current temperatures, humidity and CO2 levels, as well as coffee use on a single dashboard.",[439,26948,26949],{},"The resulting dashboard looks like this:",[439,26951,26952],{},[1002,26953,26956],{"href":26954,"rel":26955},"https://media.synyx.de/uploads//2017/03/iot_grafana_dashboard.png",[1006],[2205,26957],{"alt":469,"src":26958},"https://media.synyx.de/uploads//2017/03/iot_grafana_dashboard-1024x526.png",[439,26960,26961],{},[1002,26962,26963],{"href":26963,"rel":26964},"https://snapshot.raintank.io/dashboard/snapshot/Ept58LQH5U8sRSW7LP9hl17Cajy0T7i4",[1006],[439,26966,26967],{},"It shows the data from a total of four ESP8266-based nodes:",[994,26969,26970,26973,26976],{},[997,26971,26972],{},"One with a single DHT-22 temperature/humidity sensor.",[997,26974,26975],{},"One with a DHT-22 and MH-Z14 CO2 sensor.",[997,26977,26978],{},"Two connected to a single coffee machine each.",[439,26980,26981],{},"Although the coffee machines in question can produce more than just coffee and espresso, we deliberately limited\nourselves to these two counters so that we would not have to set up a separate dashboard (or two) for just the coffee\nmachines to display the counter for the dozens of products they offer 🙂",[439,26983,26984],{},[448,26985,26986],{},"Infrastructure",[439,26988,26989],{},"Visualised the infrastructure we created looks like this:",[439,26991,26992],{},[1002,26993,26996],{"href":26994,"rel":26995},"https://media.synyx.de/uploads//2017/03/iot_infra.png",[1006],[2205,26997],{"alt":469,"src":26998},"https://media.synyx.de/uploads//2017/03/iot_infra-1024x576.png",[439,27000,27001],{},"Central to everything is the Mosquitto MQTT broker. It facilitates communication between the ESP8266 nodes, C&C and\nInfluxDB.",[439,27003,27004],{},[448,27005,27006],{},"The ESP8266 nodes",[439,27008,27009],{},"For the firmware of the ESP8266 (ESP-12E-based NodeMCU) boards we use C++ and the Sming [1] framework. The latter\nis compatible with Arduino libraries, but allows one to use a callback-based system instead of the loop-based system\nof Arduino, in addition to allowing one to use the full C++ language instead of being limited to the Arduino C\ndialect.",[439,27011,27012],{},"The firmware is identical across all nodes, using a modular (class-based) system to allow each module (DHT, CO2, Jura,\nJura Terminal) to be enabled, disabled and configured from the C&C using MQTT messages. The current firmware image is\njust a tad over 264 kB in size, easily fitting within the 1 MB slot allocated by the rboot boot manager.",[439,27014,27015],{},"As we use ESP-12E ESP8266 modules, we have 4 MB of Flash, split up into two slots for firmware (one active, one as\nbackup or OTA update target) and 1 MB of Flash for storage (using the Spiffs filesystem) for each slot. At this point we\ndo however not store any data locally on the nodes.",[439,27017,27018],{},"With the use of the rboot boot manager, we also gain easy access to HTTP-based OTA updates. An MQTT message from C&C\ntriggers the process, telling Sming’s rboot HTTP update class to fetch the firmware image from the HTTP server, write it\nto the other firmware image slot and boot from it.",[439,27020,27021],{},[448,27022,27023],{},"Sensors",[439,27025,27026],{},"We currently use the DHT-22 sensors for humidity and temperature using the Sming-provided DHT library, but will likely\nswitch to the much more accurate BME280 (Bosch-manufactured) sensors. These also provide air pressure and take up less\nspace than a single DHT-22 sensor.",[439,27028,27029],{},"The MH-Z14 CO2 sensor has an analogue output, a PWM output and a UART (bi-directional). Of these we use the UART (\nconnected to UART0 on the ESP-12E) interface primarily for its ease of use. After querying the sensor, we get a\nresponse from which we can easily calculate the current CO2 level (in parts per million).",[439,27031,27032],{},[448,27033,27034],{},"Coffee machine",[439,27036,27037],{},"At the office we have multiple Jura coffee machines. Being higher-end machines they come with a DE-9f serial connector\non the back which provides a TTL (5V) level serial interface. Using a logic level shifter (bi-directional), we shift\nthe voltage to the 3.3V UART of the ESP-12E.",[439,27039,27040],{},"We put a NodeMCU and logic level shifter into a small enclosure with DE-9f connector, and connected it via a regular\nserial cable (1:1) to the coffee machine. Using available sources [2] we knew the pin-out of the DE-9f connector on\nthe machine, as well as the protocol it speaks. The resulting hardware is small enough to be tucked away behind the\nmachine.",[439,27042,27043],{},"A small complication we found is that none of the coffee machines we have here (Xs9, Xs90, XJ9, X3) seem to enable\ntheir UARTs when in standby mode (all pins are 0V), which makes it impossible for us to tell the machine to wake up out\nof standby. This is however not a problem for further functionality.",[439,27045,27046],{},"The +5V from the coffee machine is used to power the ESP-12E when it comes out of standby (power button pressed),\nafter which the node requests its configuration from the C&C server and starts querying the coffee machine for the\ncontents of its EEPROM storage, to read out counters for coffee use.",[439,27048,27049],{},"For this we use the ‘RT:0000’ command, which instructs the system to return the first row of values in the EEPROM (\noffset 0). This command is encoded using the Jura protocol ‘standard’ before it’s sent. This involves post-fixing CR\nand LF and taking each bit of the bytes we wish to send, putting them at position 2 or 5 of an 0xFF masked byte,\nessentially padding out the payload bytes.",[439,27051,27052],{},"The response has to be decoded in the reverse fashion: taking bits 2 and 5 out of each byte we receive and assembling\none byte out of four received. This response also ends with a CR and LF.",[439,27054,27055],{},"Decoded, the response looks like this:",[439,27057,27058],{},[471,27059,27060],{},"rt:008D0001054E0000000F00390000009300290000162500000000000000000000",[439,27062,27063],{},"We get a lower-case confirmation of the command we sent, followed by the value. In this string each two bytes (four hex\nnumbers) form a counter for a product, meaning in theory after 0xFFFF (65,535 decimal) cups of a product we would see an\noverflow. If we look at the earlier Grafana dashboard, we can however see that the Xs9 is still at fewer than 9,000 cups\nof coffee (the most popular product), despite having been in use for years. A 16-bit counter is therefore likely\nsufficient.",[439,27065,27066],{},"So far we confirmed these counters across our coffee machines:",[8310,27068,27069,27072,27075,27078],{},[997,27070,27071],{},"espresso",[997,27073,27074],{},"2x espresso",[997,27076,27077],{},"coffee",[997,27079,27080],{},"2x coffee",[439,27082,27083],{},"These counters are all in sequential order in the EEPROM. Other counters follow these four, but since not each machine\nhas the same products, the offset of their counter might be slightly different or be absent.",[439,27085,27086],{},"After reading out the relevant counters using appropriate offsets, they are published via MQTT on their own topics, to\nbe written by the MQTT-to-Influx service into the InfluxDB.",[439,27088,27089],{},[448,27090,27091],{},"LEDs",[439,27093,27094],{},"We’re looking at connecting various types of LED lighting to centrally control them (colour, intensity, etc.), including\nWS2812b RGB LEDs and kin.",[439,27096,27097],{},[448,27098,27099],{},"Grafana",[439,27101,27102],{},"Since we use Grafana elsewhere in the company already for data visualisation, we used it for this project as well. When\ntrying to configure things in a slightly more complex fashion (summing series, tables with just labels and associated\ncurrent value, etc.), we did however quickly run into limitations.",[439,27104,27105],{},[448,27106,27107],{},"Next steps",[439,27109,27110],{},"The coming time we will mostly take the existing system further: adding more sensors (light level, noise), as well as\nLEDs. We wish to add the ability to carry one’s favourite coffees between machines using NFC tags or similar. Maybe even\nadd the coffee-carrying robots many of us seem to keep mentioning.",[439,27112,27113],{},"For the sensor nodes we are working on creating (acrylic) enclosures which should be both practical and attractive\nenough to be put around the office.",[439,27115,27116],{},"The C&C server and associated UI can be made more functional and extensive, with further configuration options added to\nthe ESP8266 firmware’s modules.",[439,27118,27119],{},"The use of Grafana is at this point slightly controversial due to the limitations we found. We may look around for a\nmore scriptable alternative if these limitations prove to be insurmountable.",[439,27121,27122],{},"Beyond all of this the main goal of the project remains to improve comfort and fun levels for everyone 🙂",[439,27124,27125,8351,27132,8351,27139,8351,27146,27153],{},[1002,27126,27129],{"href":27127,"rel":27128},"https://media.synyx.de/uploads//2017/03/DSC02242_04.jpg",[1006],[2205,27130],{"alt":469,"src":27131},"https://media.synyx.de/uploads//2017/03/DSC02242_04-150x150.jpg",[1002,27133,27136],{"href":27134,"rel":27135},"https://media.synyx.de/uploads//2017/03/DSC02245_02.jpg",[1006],[2205,27137],{"alt":469,"src":27138},"https://media.synyx.de/uploads//2017/03/DSC02245_02-150x150.jpg",[1002,27140,27143],{"href":27141,"rel":27142},"https://media.synyx.de/uploads//2017/03/DSC02244.jpg",[1006],[2205,27144],{"alt":469,"src":27145},"https://media.synyx.de/uploads//2017/03/DSC02244-150x150.jpg",[1002,27147,27150],{"href":27148,"rel":27149},"https://media.synyx.de/uploads//2017/03/DSC02248_02.jpg",[1006],[2205,27151],{"alt":469,"src":27152},"https://media.synyx.de/uploads//2017/03/DSC02248_02-150x150.jpg",[1002,27154,27157],{"href":27155,"rel":27156},"https://media.synyx.de/uploads//2017/03/DSC02243.jpg",[1006],[2205,27158],{"alt":469,"src":27159},"https://media.synyx.de/uploads//2017/03/DSC02243-150x150.jpg",[439,27161,27162],{},[448,27163,18435],{},[439,27165,27166,27167],{},"[1] ",[1002,27168,27169],{"href":27169,"rel":27170},"https://github.com/SmingHub/Sming",[1006],[439,27172,27173,27174],{},"[2] ",[1002,27175,27176],{"href":27176,"rel":27177},"http://protocoljura.wiki-site.com/index.php/Hauptseite",[1006],{"title":469,"searchDepth":507,"depth":507,"links":27179},[],[1030],"2017-03-23T15:18:41","A few months ago, here at the synyx office we started out with a simple idea: hook up a couple of sensors to an ESP8266\\nmodule (or twenty) and have it write its data somewhere for visualisation purposes. Then we got creative.","https://synyx.de/blog/visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana/",{},"/blog/visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana",{"title":26928,"description":26937},"blog/visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana",[27189,27190,27191,27192,27193,27194,27195],"c","esp8266","grafana","influxdb","mqtt","nodemcu","sming","A few months ago, here at the synyx office we started out with a simple idea: hook up a couple of sensors to an ESP8266 module (or twenty) and have…","VMNFvjh6e-GRL2R6v2E9WgKH7L4l686dKDHSzXxsAp0",{"id":27199,"title":27200,"author":27201,"body":27202,"category":27428,"date":27429,"description":27430,"extension":1034,"link":27431,"meta":27432,"navigation":916,"path":27433,"seo":27434,"slug":27206,"stem":27436,"tags":27437,"teaser":27438,"__hash__":27439},"blog/blog/synyx-app-raumbelegung.md","synyx App-Raumbelegung",[54],{"type":432,"value":27203,"toc":27422},[27204,27207,27219,27225,27228,27239,27242,27246,27250,27253,27255,27259,27262,27282,27285,27289,27292,27300,27304,27307,27311,27314,27317,27320,27324,27327,27353,27356,27370,27373,27377,27380,27383,27403,27406,27414],[435,27205,27200],{"id":27206},"synyx-app-raumbelegung",[439,27208,27209,27210,27213,27214,27218],{},"Die synyx App-Raumbelegung ist kostenfrei und ",[1002,27211,26563],{"href":26561,"rel":27212},[1006]," und macht eine spontane,\nunkomplizierte ",[1002,27215,27217],{"href":13095,"rel":27216},[1006],"Meetingplanung"," möglich. Der Grund für dies\nsynyx App ist einfach: Vermehrt kam es dazu, dass freie Meetingräume gesucht wurden und unklar war ob ein Raum frei ist.\nEs ist umständlich zuerst auf das Handy zu sehen oder zurück zum Arbeitsplatz zu gehen, um dann im Kalender zu prüfen,\nwelcher Raum gerade frei ist. Viel praktischer wäre es doch, wenn die Räume von außen gekennzeichnet wären, ob sie frei\nsind. Doch wie könnte dies umgesetzt werden?",[3938,27220,27222],{"id":27221},"vorgehen-open-source-oder-kommerziell",[448,27223,27224],{},"Vorgehen – Open Source oder kommerziell?",[439,27226,27227],{},"Um den Status einzelner Räume anzuzeigen, ist es sinnvoll vor diesen ein Tablet anzubringen, welches den Status des\njeweiligen Raumes zeigt. Hierfür galt es zu entscheiden:",[994,27229,27230,27233,27236],{},[997,27231,27232],{},"App kaufen",[997,27234,27235],{},"OpenSource Software verwenden",[997,27237,27238],{},"eigene App programmieren und verwenden",[439,27240,27241],{},"Die Entscheidung fiel auf die OpenSource Software „Reservator“ von futurice. Ausschlaggebend für diese Entscheidung war,\ndass schon eine lauffähige App vorhanden ist und diese an Wünsche und Bedürfnisse von synyx angepasst werden kann. Die\nAndroid App „Reservator“ verwendet als Meetingräume die Ressourcen eines Google Accounts.",[3938,27243,27245],{"id":27244},"funktionsweise-der-synyx-app-raumbelegung","Funktionsweise der synyx App-Raumbelegung",[1633,27247,27249],{"id":27248},"vorraussetzungen","Vorraussetzungen",[439,27251,27252],{},"Die App ist nur auf einem Android Tablet lauffähig, aktuell ist sie nur in der horizontal Ansicht verwendbar. Das Tablet\nbenötigt mindestens die Android Version 4.0. Um die App verwenden zu können wird aktuell ein Google Konto gebraucht,\nwelches vor dem Start der App auf dem Tablet als Benutzer Konto eingerichtet wurde. Falls dies nicht der fall ist, tritt\nbeim öffnen der App ein Fehler auf, dass sicher gestellt werden soll, dass ein Google Konto vorhanden ist. Ebenfalls\nmuss sichergestellt sein, dass die Räume im Google Kalendar Ressource vorhanden sind, damit die App die Räume findet.\nDementsprechend benötigt die App auch eine Internetverbindung um die Informationen vom Google Kalender zu erhalten.",[1633,27254],{"id":469},[1633,27256,27258],{"id":27257},"app-funktionen","App Funktionen",[439,27260,27261],{},"Nach dem erfolgreichen starten der App, wird die Raumübersicht angezeigt. Bei dieser Ansicht werden sämtliche Räume, auf\ndie das Google Konto zugriff hat, dargestellt. Rechts oben in der Ecke befinden sich die Einstellungen, hier kann\nfolgendes eingestellt werden:",[994,27263,27264,27267,27270,27273,27276,27279],{},[997,27265,27266],{},"Benutzer Google Konto und Konto für die Reservierungen",[997,27268,27269],{},"Standard-Raum, vor dem das Tablet hängt",[997,27271,27272],{},"Räume, die in der Raumübersicht angezeigt werden sollen",[997,27274,27275],{},"Sprache (englisch oder deutsch)",[997,27277,27278],{},"Meeting Bezeichnung (Name des Reservierers oder Meetingname)",[997,27280,27281],{},"Ansicht der Reservierung (Standardansicht mit Zeitbalken oder Ansicht mit Zeitbuttons)",[1633,27283],{"id":27284},"_1",[1633,27286,27288],{"id":27287},"aufbau-der-app","Aufbau der App",[439,27290,27291],{},"Beim Start der App gelangt man auf die Raumübersicht. Hier werden alle Räume aufgelistet. Nach einer Minute wechselt die\nApp zu der Statusansicht des Default Raumes. Die Statusansicht zeigt den Status des Raums, ob dieser belegt ist oder\nfrei. Falls ein Raum belegt ist, wird der Name der Person gezeigt, die das Meeting angelegt hat. Sobald auf den\nHintergrund der Statusansicht getippt wird, findet ein Wechsel zur Wochenansicht statt. In der Wochenansicht sind\nsämtliche Meetings innerhalb einer Woche von 7:00 – 21:00 Uhr tabellarisch aufgelistet. Nach einer Minute findet ein\nautomatischer Wechsel zur Statusansicht statt. Über einen Button gelangt man von der Wochenansicht aber auch wieder\nzurück zur Raumübersicht. Von der Raumübersicht gelangt man mit antippen des Raumnamens auch in die Wochenansicht. Über\nalle drei Ansichten gelangt man zur Reservationsansicht. Bei der Raumübersicht und Statusansicht erfolgt der Wech- sel\nüber einen Button, wohingegen in der Wochenansicht ein freier Bereich angeklickt werden muss. In der Reservieransicht\nkann der ausgewertetählte Raum reserviert werden, hierfür wird ein Name angegeben und die Meetingzeit mit Start und\nEndzeit.",[439,27293,27294],{},[1002,27295,27298],{"href":27296,"rel":27297},"https://media.synyx.de/uploads//2017/03/blog.jpg",[1006],[2205,27299],{"alt":469,"src":27296},[1633,27301,27303],{"id":27302},"synchronisation","Synchronisation",[439,27305,27306],{},"Die App synchronisiert sich in regelmäßgen Abständen mit dem Google Kalender. Nach einer getätigten Reservierung oder\nlöschen eines Meetings über die App, wird eine Synchronisation angestoßen und dem Google Kalendar die Änderungen\nmitgeteilt. Wenn nichts an der App gemacht wird, erfolgt die Synchronisation alle 10 Sekunden.",[3938,27308,27310],{"id":27309},"änderungen","Änderungen",[439,27312,27313],{},"Bevor Änderungen an der App stattfanden, wurde festgelegt, dass sämtliche Änderungen abwärts zum Master von futurice\nkompatibel sind. Dies wurde so festgelegt, damit der von uns angelegte neue Branch eventuell in ihren Master gemerged\nwerden kann. Damit futurice unsere Änderungen oder neue Features auch verwenden können. Zu Beginn wurde die App\ninternationalisiert. Sämtliche Strings waren auf englisch und in der App verstreut. Alle Texte die später angezeigt\nwerden wurden in ein separates File ausgelagert. In einem weiterem File wurden die deutschen Entsprechungen zu den\nTexten gespeichert. Somit kann nun über die Settings gewählt werden, in welcher Sprache die App sein soll.",[439,27315,27316],{},"Die Synchronisation mit den Google Kalendar wurde noch an einer anderen Stelle angestoßen, da die App nicht immer den\nAktuellen Raumstatus angezeigt hat, nachdem ein Meeting über die App angelegt wurde.",[1633,27318],{"id":27319},"_2",[1633,27321,27323],{"id":27322},"graphische-änderungen","Graphische Änderungen",[439,27325,27326],{},"Es wurden an der App einige graphische Änderungen vorgenommen, unteranderem wurde eine neue Reservierungsansicht zu der\nbereits verwendeten hinzugefügt. Da die bereits vorhandene Ansicht für uns nicht sehr effizient war. In den\nEinstellungen kann eingestellt werden, welche Ansicht für die Reservierung verwendet werden soll.",[27328,27329,27330,27341],"table",{},[27331,27332,27333],"thead",{},[27334,27335,27336,27339],"tr",{},[27337,27338],"th",{},[27337,27340],{},[27342,27343,27344],"tbody",{},[27334,27345,27346,27350],{},[27347,27348,27349],"td",{},"Alte Reservierungs Ansicht",[27347,27351,27352],{},"Neue Reservierungs Ansicht",[439,27354,27355],{},"Weitere Veränderungen wurden vorgenommen:",[994,27357,27358,27361,27364,27367],{},[997,27359,27360],{},"Button um zur Statusansicht eines Raum zu gelangen, damit ein schneller Wechsel möglich is",[997,27362,27363],{},"Uhrzeit auf der Raumstatusseite, wurde hinzugefügt, damit die aktuelle Uhrzeit sichtbar ist und die Kollegen einen\nbesseren Überblick haben wann der Raum frei ist",[997,27365,27366],{},"Länge der Reservierung wird auf der Raumstatusansicht angezeigt, hierdurch ist schnell ersichtlich wie lang das\nMeeting noch geht",[997,27368,27369],{},"App ist internationalisiert (englisch und deutsch), somit kann die App individuell auf eine der zwei Sprachen\neingestellt werden",[439,27371,27372],{},"Zum Testen wurde ein Tablet mit einer App vor einen Meetingraum gehängt. Nach den positiven Feedbacks der Kollegen\nwurden an den anderen Meetingräumen ebenfalls Tablets mit der App aufgehängt.",[3938,27374,27376],{"id":27375},"ausblick-der-synyx-app-raumbelegung","Ausblick der synyx App-Raumbelegung",[439,27378,27379],{},"Das Projekt befindet sich noch im Prozess. Die synyx Open Source App zur Raumbelegung weißt noch einige Bugs auf, die\ngefixt werden müssen. Ein bekannter Bug ist die Anzeige des Raumnamens, nachträglich geänderte Raumnamen befinden sich\nnicht in den Ressourcen eines Raumes. Im Moment werden die Raumnamen aus den angelegten Meetings gefiltert. Somit kommt\nes dazu, dass der alte Raumname (Raum 1) angezeigt wird, wenn der Raum keine Meetings hat.",[439,27381,27382],{},"Durch den aktiven Einsatz der App sind Wünsche und Verbesserungsvorschläge aufgetreten, die als weitere Veränderungen an\nder App geplant:",[994,27384,27385,27388,27391,27394,27397,27400],{},[997,27386,27387],{},"synyx Logo einbinden",[997,27389,27390],{},"Button für One-Click-Buchung für 30 min",[997,27392,27393],{},"Bessere Lösung für Raumnamen",[997,27395,27396],{},"Layout Überarbeitung",[997,27398,27399],{},"Anbindung anderer Kalender (Kolab)",[997,27401,27402],{},"…",[439,27404,27405],{},"Geplant ist es, die App synyx intern in den App Store hochzuladen, damit nach einer Veränderung nicht jedes Tablet mit\ndem Laptop manuell auf den neusten Stand gebracht werden muss.",[439,27407,27408,27409,1402],{},"Die aktuelle Version der synyx App-Raumbelegung befindet sich kostenfrei und Open Source\nauf ",[1002,27410,27413],{"href":27411,"rel":27412},"https://github.com/synyx/meeting-room-tablet",[1006],"Github",[439,27415,27416],{},[1002,27417,27420],{"href":27418,"rel":27419},"https://media.synyx.de/uploads//2017/03/Screenshot_20170316-085921.png",[1006],[2205,27421],{"alt":469,"src":27418},{"title":469,"searchDepth":507,"depth":507,"links":27423},[27424,27425,27426,27427],{"id":27221,"depth":507,"text":27224},{"id":27244,"depth":507,"text":27245},{"id":27309,"depth":507,"text":27310},{"id":27375,"depth":507,"text":27376},[13208],"2017-03-16T09:22:00","Die synyx App-Raumbelegung ist kostenfrei und Open Source und macht eine spontane,\\nunkomplizierte Meetingplanung möglich. Der Grund für dies\\nsynyx App ist einfach: Vermehrt kam es dazu, dass freie Meetingräume gesucht wurden und unklar war ob ein Raum frei ist.\\nEs ist umständlich zuerst auf das Handy zu sehen oder zurück zum Arbeitsplatz zu gehen, um dann im Kalender zu prüfen,\\nwelcher Raum gerade frei ist. Viel praktischer wäre es doch, wenn die Räume von außen gekennzeichnet wären, ob sie frei\\nsind. Doch wie könnte dies umgesetzt werden?","https://synyx.de/blog/synyx-app-raumbelegung/",{},"/blog/synyx-app-raumbelegung",{"title":27200,"description":27435},"Die synyx App-Raumbelegung ist kostenfrei und Open Source und macht eine spontane,\nunkomplizierte Meetingplanung möglich. Der Grund für dies\nsynyx App ist einfach: Vermehrt kam es dazu, dass freie Meetingräume gesucht wurden und unklar war ob ein Raum frei ist.\nEs ist umständlich zuerst auf das Handy zu sehen oder zurück zum Arbeitsplatz zu gehen, um dann im Kalender zu prüfen,\nwelcher Raum gerade frei ist. Viel praktischer wäre es doch, wenn die Räume von außen gekennzeichnet wären, ob sie frei\nsind. Doch wie könnte dies umgesetzt werden?","blog/synyx-app-raumbelegung",[],"Die synyx App-Raumbelegung ist kostenfrei und Open Source und macht eine spontane, unkomplizierte Meetingplanung möglich. Der Grund für dies synyx App ist einfach: Vermehrt kam es dazu, dass freie Meetingräume…","kVOaXAG8J-IyvIdDiypTRDMTBTS5BuZZFnwcaUk1Smc",{"id":27441,"title":27442,"author":27443,"body":27444,"category":27641,"date":27642,"description":27643,"extension":1034,"link":27644,"meta":27645,"navigation":916,"path":27646,"seo":27647,"slug":27448,"stem":27649,"tags":27650,"teaser":27654,"__hash__":27655},"blog/blog/axon-3-event-replaying.md","Axon 3: Event Replaying",[247,400],{"type":432,"value":27445,"toc":27627},[27446,27449,27463,27467,27474,27477,27481,27484,27488,27491,27495,27502,27506,27509,27517,27522,27525,27534,27544,27578,27581,27588,27597,27601,27604,27608,27611,27615,27618,27620,27622,27625],[435,27447,27442],{"id":27448},"axon-3-event-replaying",[439,27450,27451,27456,27457,27462],{},[1002,27452,27455],{"href":27453,"rel":27454},"http://www.axonframework.org/",[1006],"Axon"," is a lightweight framework that supports the implemenation of CQRS patterns by\nproviding commonly used building blocks. One of those patterns is an event sourced application architecture. Even though\nEvent Sourcing and CQRS are orthogonal concepts they fit together very well and are often used together. Event sourcing\nin an ",[1002,27458,27461],{"href":27459,"rel":27460},"https://msdn.microsoft.com/en-us/library/jj591577.aspx",[1006],"ES/CQRS"," architecture means that all changes to the\napplication state are done via domain events and the current state can always be rebuilt from the series of events\navailable in a persistent event store. In addition to the event store there might also be one or more read models, for\nexample to achieve opimtized query performance. No matter if there are read models or not, the event store is considered\nthe single source of truth.",[3938,27464,27466],{"id":27465},"event-replaying-what-and-why","Event Replaying: what and why?",[439,27468,27469,27470,27473],{},"Besides rebuilding current application state from the stored events we can employ a technique called ",[448,27471,27472],{},"Event Replaying","\nto achieve several other goals. For this purpose we need a mechanism to read all events from the event store and send\nthem to a set of components which are interested in handling them. This implies selecting and registering those\ncomponents.",[439,27475,27476],{},"Some use cases where event replaying can be applied are: generating new read models, removing inconsistencies by\nrebuilding an existing read model, or for analysis and debugging purposes.",[1065,27478,27480],{"id":27479},"adding-new-read-models","Adding new read models",[439,27482,27483],{},"Suppose you have a library managemenent application where you can track and add meta data about your books. Sometime you\nmight want to switch from your table based search implementation to Elasticsearch. This can be done with event\nreplaying: all you have to do is to implement one or more event handlers that are responsible for extracting appropriate\ninformation from the domain events and inserting them into the Elasticsearch index.",[1065,27485,27487],{"id":27486},"removing-inconsistencies-from-existing-read-models","Removing inconsistencies from existing read models",[439,27489,27490],{},"As software developers we may have to deal with bugs in our software even though we try hard to avoid them.\nNevertheless, bugs are inevitable. Imagine there is a bug in one of the event handling components that are supposed to\nrebuild the aforementioned Elasticsearch index. In such a situation, first the bug must be fixed (in code) and then all\ndomain events simply have to be replayed to the now well behaving event handler again.",[1065,27492,27494],{"id":27493},"debugging-purposes","Debugging purposes",[439,27496,27497,27498,27501],{},"Sometimes you want to know exactly ",[990,27499,27500],{},"when"," an inconsistent application state has been introduced. One approach to achieve\nthis could be to replay all events up to a certain point in time and examine the corresponding application state.",[3938,27503,27505],{"id":27504},"how","How?",[439,27507,27508],{},"Introducing event replaying in an application that already uses the Axon 3 framework is fairly easy. Axon provides all\nbuilding blocks needed to achieve this, in particular there are event handlers and event processors. Event handlers\nimplement all business logic, whereas event processors are responsible for taking care of the technical aspects of event\nprocessing.",[439,27510,27511,27512,6562],{},"There are two types of event processors: Subscribing Event Processors and Tracking Event Processors. From\nthe ",[1002,27513,27516],{"href":27514,"rel":27515},"https://docs.axonframework.org/v/3.0/part3/event-processing.html",[1006],"Axon documentation",[11947,27518,27519],{},[439,27520,27521],{},"The Subscribing Event Processors subscribe themselves to a source of Events and are invoked by the thread managed by\nthe publishing mechanism. Tracking Event Processors, on the other hand, pull their messages from a source using a\nthread\nthat it manages itself.",[439,27523,27524],{},"It’s the tracking event processors that provide event replaying capabilities. They’re keeping track of which events have\nalready been processed by means of storing a token. So let’s configure Axon to use tracking processors (instead of the\ndefault subscribing processors):",[464,27526,27528],{"className":6253,"code":27527,"language":6255,"meta":469,"style":469},"eventHandlingConfiguration.usingTrackingProcessors();\n",[471,27529,27530],{"__ignoreMap":469},[474,27531,27532],{"class":476,"line":477},[474,27533,27527],{},[439,27535,27536,27537,27540,27541,6562],{},"This will automatically create tracking event processors for your event handlers. Axon uses a token store to determine\nwhether there are events that need to be processed. This token store will be checked regulary. If you’re using the Axon\nJPA provider there is already an entity class ",[471,27538,27539],{},"TokenEntry"," available. You have to tell your JPA provider where it is\nlocated. In Spring Boot this can be done with ",[471,27542,27543],{},"@EntityScan",[464,27545,27547],{"className":6253,"code":27546,"language":6255,"meta":469,"style":469}," @EntityScan(\n basePackages = {\n ...,\n \"org.axonframework.eventhandling.tokenstore.jpa\"\n }\n )\n",[471,27548,27549,27554,27559,27564,27569,27573],{"__ignoreMap":469},[474,27550,27551],{"class":476,"line":477},[474,27552,27553],{}," @EntityScan(\n",[474,27555,27556],{"class":476,"line":507},[474,27557,27558],{}," basePackages = {\n",[474,27560,27561],{"class":476,"line":547},[474,27562,27563],{}," ...,\n",[474,27565,27566],{"class":476,"line":584},[474,27567,27568],{}," \"org.axonframework.eventhandling.tokenstore.jpa\"\n",[474,27570,27571],{"class":476,"line":607},[474,27572,5704],{},[474,27574,27575],{"class":476,"line":642},[474,27576,27577],{}," )\n",[439,27579,27580],{},"This is everything needed to configure your application to be able to do event replaying.",[439,27582,27583,27584,27587],{},"In order to trigger an event replay in production you just need to delete the tracking tokens associated with an event\nprocessor from the",[471,27585,27586],{},"token_entry"," table. The corresponding event processor then pulls all events from the event store.",[439,27589,27590,27591,27596],{},"This is a really simple configuration. It’s also possible to configure seperate tracking event processors for different\nreplaying use cases. See\nthe ",[1002,27592,27595],{"href":27593,"rel":27594},"http://www.axonframework.org/apidocs/3.0/org/axonframework/config/EventHandlingConfiguration.html",[1006],"EventHandlingConfiguration","\nclass for more detail.",[3938,27598,27600],{"id":27599},"common-pitfalls","Common pitfalls",[439,27602,27603],{},"Event replaying is a very useful technique but you have to be aware of some common pitfalls.",[1065,27605,27607],{"id":27606},"replaying-events-to-external-services","Replaying events to external services",[439,27609,27610],{},"Make sure that replayed events never get processed accidentally by the wrong event handlers. For example events might be\ntransformed into messages and published to a messaging middleware, and eventually consumed by external systems. In this\ncase you want to exclude the corresponding event handlers from event replaying because messages should only be published\nonce.",[1065,27612,27614],{"id":27613},"eventual-consistency","Eventual Consistency",[439,27616,27617],{},"If you’ve used the default subscribing event processors and switch to tracking event processors remember that they are\npulling for events in a thread managed on their own. If your user interface relies on everything being handled in one\nthread, the user interface will break now. In this case, consider using subscribing event processors for\nbusiness-as-usual actions and only use tracking ones if you intent to trigger an event replay. This requires some more\nelaborate configuration for when event replaying is executed. Another option could be to let your user interface pull\nfor updates.",[1065,27619],{"id":469},[3938,27621,9392],{"id":9391},[439,27623,27624],{},"Event replaying in Axon 3 is supported by tracking event processors: they keep track of the last position in the event\nlog. In order to replay all events all you have to do is resetting the corresponding tracking token.",[1024,27626,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":27628},[27629,27634,27635,27640],{"id":27465,"depth":507,"text":27466,"children":27630},[27631,27632,27633],{"id":27479,"depth":547,"text":27480},{"id":27486,"depth":547,"text":27487},{"id":27493,"depth":547,"text":27494},{"id":27504,"depth":507,"text":27505},{"id":27599,"depth":507,"text":27600,"children":27636},[27637,27638,27639],{"id":27606,"depth":547,"text":27607},{"id":27613,"depth":547,"text":27614},{"id":469,"depth":547,"text":469},{"id":9391,"depth":507,"text":9392},[1030],"2017-03-01T08:14:51","Axon is a lightweight framework that supports the implemenation of CQRS patterns by\\nproviding commonly used building blocks. One of those patterns is an event sourced application architecture. Even though\\nEvent Sourcing and CQRS are orthogonal concepts they fit together very well and are often used together. Event sourcing\\nin an ES/CQRS architecture means that all changes to the\\napplication state are done via domain events and the current state can always be rebuilt from the series of events\\navailable in a persistent event store. In addition to the event store there might also be one or more read models, for\\nexample to achieve opimtized query performance. No matter if there are read models or not, the event store is considered\\nthe single source of truth.","https://synyx.de/blog/axon-3-event-replaying/",{},"/blog/axon-3-event-replaying",{"title":27442,"description":27648},"Axon is a lightweight framework that supports the implemenation of CQRS patterns by\nproviding commonly used building blocks. One of those patterns is an event sourced application architecture. Even though\nEvent Sourcing and CQRS are orthogonal concepts they fit together very well and are often used together. Event sourcing\nin an ES/CQRS architecture means that all changes to the\napplication state are done via domain events and the current state can always be rebuilt from the series of events\navailable in a persistent event store. In addition to the event store there might also be one or more read models, for\nexample to achieve opimtized query performance. No matter if there are read models or not, the event store is considered\nthe single source of truth.","blog/axon-3-event-replaying",[27651,27652,27653,711],"axon","cqrs","event-sourcing","Axon is a lightweight framework that supports the implemenation of CQRS patterns by providing commonly used building blocks. One of those patterns is an event sourced application architecture. Even…","EsUmpQAdRuDUdhFKW4wvztcDCze9dI5vCmw1_n2XNcA",{"id":27657,"title":27658,"author":27659,"body":27660,"category":27970,"date":27971,"description":27972,"extension":1034,"link":27973,"meta":27974,"navigation":916,"path":27975,"seo":27976,"slug":27664,"stem":27978,"tags":27979,"teaser":27983,"__hash__":27984},"blog/blog/welcome-to-spring-ldap-with-ssl-the-entrance-is-free.md","Welcome to Spring LDAP with SSL: The entrance is free",[332],{"type":432,"value":27661,"toc":27968},[27662,27665,27680,27705,27708,27715,27722,27725,27736,27745,27754,27757,27764,27767,27778,27784,27791,27834,27837,27870,27881,27888,27896,27924,27927,27934,27937,27957,27966],[435,27663,27658],{"id":27664},"welcome-to-spring-ldap-with-ssl-the-entrance-is-free",[439,27666,27667,27668,27673,27674,27679],{},"Some time ago we started to create our own intranet called OpenCoffee, which is\na ",[1002,27669,27672],{"href":27670,"rel":27671},"https://en.wikipedia.org/wiki/Microservices",[1006],"microservice"," landscape based on our own Spring Boot Starters, a service\ndiscovery and an ",[1002,27675,27678],{"href":27676,"rel":27677},"https://oauth.net/2/",[1006],"OAuth2 service",". The vision is to create a system with a very easy integration\nof new applications by just adding a few Spring Boot Starters and starting to code the specific functionality of the new\nservice.",[439,27681,27682,27683,27688,27689,27693,27694,27699,27700,1402],{},"We passed that stage of the developer friendly integration, started to look at the security and vulnerability of the\nsystem where we tried to make things more secure. At some point we talked about SSL encryption of the traffic between\nthe OAuth2 service and our ",[1002,27684,27687],{"href":27685,"rel":27686},"https://de.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol",[1006],"LDAP",". We used Spring\nLDAP for the communication between these two services and started reading\nthe ",[1002,27690,22273],{"href":27691,"rel":27692},"http://docs.spring.io/spring-ldap/docs/1.3.x/reference/html/configuration.html",[1006]," on how to get this\ndone. We achieved this quite easily by replacing\nthe ",[1002,27695,27698],{"href":27696,"rel":27697},"https://github.com/spring-projects/spring-ldap/blob/master/core/src/main/java/org/springframework/ldap/core/support/DirContextAuthenticationStrategy.java",[1006],"authentication strategy","\nin the LDAP context source with the\nprovided ",[1002,27701,27704],{"href":27702,"rel":27703},"https://github.com/spring-projects/spring-ldap/blob/master/core/src/main/java/org/springframework/ldap/core/support/DefaultTlsDirContextAuthenticationStrategy.java",[1006],"tls authentication strategy",[439,27706,27707],{},"Spring is doing a good job of an easy configuration, thanks guys.",[11947,27709,27710],{},[439,27711,27712],{},[990,27713,27714],{},"We activated the TLS authentication strategy and it was not safe anymore",[439,27716,27717,27718,27721],{},"At the beginning we were happy but then we started our tests and there it happened. Our tests failed because it was very\neasy to get access via our OAuth2 service with an activated TLS authentication strategy. You just have to know a\nusername that is defined in our LDAP and it will grant access with every password ",[990,27719,27720],{},"you"," want to use. That is not even\nsecurity through obscurity. Just a friendly bouncer.",[439,27723,27724],{},"Not so good for us so we started to dig into the code, maybe it was a mistake on our side. After a long debugging\nsession with two instances of our OAuth2 service, with and without the TLS authentication strategy, we found out that\nthe last bind to the LDAP server, which should contain the user credentials, was an anonymous bind.",[439,27726,27727,27728,27731,27732,27735],{},"This anonymous bind returned with the message “",[990,27729,27730],{},"Yeah OAuth2 server, you can let the anonymous user log in, he is\nsecure","“. Of course that last anonymous bind was ",[990,27733,27734],{},"not ok",". Was it our fault or does Spring LDAP have a bug with\nanonymous binds and the TLS authentication strategy?",[439,27737,27738,27739,27744],{},"We started to isolate the bug and created\na ",[1002,27740,27743],{"href":27741,"rel":27742},"https://github.com/derTobsch/tls-bug-demo/tree/master/docker",[1006],"docker container"," with SSL encryption, activated\nanonymous binds and allowed anonymous user search through the directory tree.",[439,27746,27747,27748,27753],{},"After some time we had our docker container together with all the configuration we needed to reproduce the bug,\nhopefully. A small ",[1002,27749,27752],{"href":27750,"rel":27751},"https://github.com/derTobsch/tls-bug-demo",[1006],"Spring Boot project"," with Spring LDAP was easy to set up\nand we configured everything. We logged in with correct credentials and everything was fine.",[439,27755,27756],{},"Then after all this debugging and a good session with my colleagues from synyx, thanks to all of you – that was a really\nnice Friday with you – we tried to login with the best incorrect credentials “user” and the password “wurstsalat”. Maybe\nyou know what happened, we were logged in and had a good feeling from chasing, finding and isolating this bug.",[11947,27758,27759],{},[439,27760,27761],{},[990,27762,27763],{},"Was it only the authentication strategy that caused that bug?",[439,27765,27766],{},"It was not only the authentication strategy. It was a combination of three things:",[994,27768,27769,27772,27775],{},[997,27770,27771],{},"‘Groupsearch’ is activated and used",[997,27773,27774],{},"Anonymous binds are activated and allowed to search through the directory tree",[997,27776,27777],{},"‘DefaultTlsDirContextAuthenticationStrategy’ is used as authentication strategy",[439,27779,27780,27781,1402],{},"When you know what is happening, you can easily see the problems with the ",[471,27782,27783],{},"DefaultTlsDirContextAuthenticationStrategy",[439,27785,27786,27787,27790],{},"At the beginning of the communication a TLS connection is started and a lookup of the user ",[471,27788,27789],{},"cn=user"," is made:",[464,27792,27794],{"className":16895,"code":27793,"language":16897,"meta":469,"style":469},"...\n588241ed conn=1011 fd=16 TLS established tls_ssf=128 ssf=128\n588241ed conn=1011 op=1 BIND dn=\"\" method=128\n588241ed conn=1011 op=1 RESULT tag=97 err=0 text=\n588241ed conn=1011 op=2 SRCH base=\"ou=People,dc=example,dc=org\" scope=2 deref=3 filter=\"(\u003Cstrong>cn=user\u003C/strong>)\"\n588241ed \u003C = bdb_equality_candidates: (cn) not indexed\n588241ed conn=1011 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=\n...\n",[471,27795,27796,27800,27805,27810,27815,27820,27825,27830],{"__ignoreMap":469},[474,27797,27798],{"class":476,"line":477},[474,27799,14198],{},[474,27801,27802],{"class":476,"line":507},[474,27803,27804],{},"588241ed conn=1011 fd=16 TLS established tls_ssf=128 ssf=128\n",[474,27806,27807],{"class":476,"line":547},[474,27808,27809],{},"588241ed conn=1011 op=1 BIND dn=\"\" method=128\n",[474,27811,27812],{"class":476,"line":584},[474,27813,27814],{},"588241ed conn=1011 op=1 RESULT tag=97 err=0 text=\n",[474,27816,27817],{"class":476,"line":607},[474,27818,27819],{},"588241ed conn=1011 op=2 SRCH base=\"ou=People,dc=example,dc=org\" scope=2 deref=3 filter=\"(\u003Cstrong>cn=user\u003C/strong>)\"\n",[474,27821,27822],{"class":476,"line":642},[474,27823,27824],{},"588241ed \u003C = bdb_equality_candidates: (cn) not indexed\n",[474,27826,27827],{"class":476,"line":663},[474,27828,27829],{},"588241ed conn=1011 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=\n",[474,27831,27832],{"class":476,"line":694},[474,27833,14198],{},[439,27835,27836],{},"then the second bind is made, which should contain the credentials but",[464,27838,27840],{"className":16895,"code":27839,"language":16897,"meta":469,"style":469},"...\n588241ee conn=1013 fd=16 TLS established tls_ssf=128 ssf=128\n588241ee conn=1013 op=1 BIND \u003Cstrong>dn=\"\"\u003C/strong> method=128\n588241ee conn=1013 op=1 RESULT tag=97 err=0 text=\n588241ee conn=1013 op=2 SRCH base=\"ou=Groups,dc=example,dc=org\" scope=1 deref=3 filter=\"(member=cn=user,ou=people,dc=example,dc=org)\"\n...\n",[471,27841,27842,27846,27851,27856,27861,27866],{"__ignoreMap":469},[474,27843,27844],{"class":476,"line":477},[474,27845,14198],{},[474,27847,27848],{"class":476,"line":507},[474,27849,27850],{},"588241ee conn=1013 fd=16 TLS established tls_ssf=128 ssf=128\n",[474,27852,27853],{"class":476,"line":547},[474,27854,27855],{},"588241ee conn=1013 op=1 BIND \u003Cstrong>dn=\"\"\u003C/strong> method=128\n",[474,27857,27858],{"class":476,"line":584},[474,27859,27860],{},"588241ee conn=1013 op=1 RESULT tag=97 err=0 text=\n",[474,27862,27863],{"class":476,"line":607},[474,27864,27865],{},"588241ee conn=1013 op=2 SRCH base=\"ou=Groups,dc=example,dc=org\" scope=1 deref=3 filter=\"(member=cn=user,ou=people,dc=example,dc=org)\"\n",[474,27867,27868],{"class":476,"line":642},[474,27869,14198],{},[439,27871,27872,27873,27876,27877],{},"you can see the empty ",[471,27874,27875],{},"dn=\"\""," which means the user will be logged in without any authentication. If you want to take a\ndeeper look you can check out the ",[1002,27878,27880],{"href":27741,"rel":27879},[1006],"sample project",[11947,27882,27883],{},[439,27884,27885],{},[990,27886,27887],{},"Let’s contribute to Spring LDAP",[439,27889,27890,27891,27895],{},"We opened a ",[1002,27892,22218],{"href":27893,"rel":27894},"https://github.com/spring-projects/spring-ldap/pull/432",[1006]," with a recommendation how to fix\nthis issue and as you can see below the last bind will now contain the user credentials",[464,27897,27899],{"className":16895,"code":27898,"language":16897,"meta":469,"style":469},"...\n58824373 conn=1015 fd=16 TLS established tls_ssf=128 ssf=128\n58824373 conn=1015 op=1 BIND \u003Cstrong>dn=\"cn=user,ou=People,dc=example,dc=org\"\u003C/strong> method=128\n58824373 conn=1015 op=1 RESULT tag=97 err=49 text=\n...\n",[471,27900,27901,27905,27910,27915,27920],{"__ignoreMap":469},[474,27902,27903],{"class":476,"line":477},[474,27904,14198],{},[474,27906,27907],{"class":476,"line":507},[474,27908,27909],{},"58824373 conn=1015 fd=16 TLS established tls_ssf=128 ssf=128\n",[474,27911,27912],{"class":476,"line":547},[474,27913,27914],{},"58824373 conn=1015 op=1 BIND \u003Cstrong>dn=\"cn=user,ou=People,dc=example,dc=org\"\u003C/strong> method=128\n",[474,27916,27917],{"class":476,"line":584},[474,27918,27919],{},"58824373 conn=1015 op=1 RESULT tag=97 err=49 text=\n",[474,27921,27922],{"class":476,"line":607},[474,27923,14198],{},[439,27925,27926],{},"and users with wrong credentials will not be allowed to log in.",[11947,27928,27929],{},[439,27930,27931],{},[990,27932,27933],{},"A long but satisfying journey",[439,27935,27936],{},"It took as quite some time to get near the bug and understand it, but in the end we learned something again",[994,27938,27939,27942,27945,27948],{},[997,27940,27941],{},"Have valuable integration tests",[997,27943,27944],{},"Do not give up and debug as deep as you can. You can learn a lot.",[997,27946,27947],{},"Open source projects are there to contribute",[997,27949,27950,27951,27956],{},"It is fun to chase some ",[1002,27952,27955],{"href":27953,"rel":27954},"https://en.wikipedia.org/wiki/Starship_Troopers_(film)",[1006],"bugs"," 🙂",[439,27958,27959,27960,27965],{},"I hope you enjoyed the blog and it will make you curious what the ",[1002,27961,27964],{"href":27962,"rel":27963},"https://github.com/open-coffee",[1006],"OpenCoffee"," will be.",[1024,27967,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":27969},[],[9045,1030,1412],"2017-02-17T10:53:19","Some time ago we started to create our own intranet called OpenCoffee, which is\\na microservice landscape based on our own Spring Boot Starters, a service\\ndiscovery and an OAuth2 service. The vision is to create a system with a very easy integration\\nof new applications by just adding a few Spring Boot Starters and starting to code the specific functionality of the new\\nservice.","https://synyx.de/blog/welcome-to-spring-ldap-with-ssl-the-entrance-is-free/",{},"/blog/welcome-to-spring-ldap-with-ssl-the-entrance-is-free",{"title":27658,"description":27977},"Some time ago we started to create our own intranet called OpenCoffee, which is\na microservice landscape based on our own Spring Boot Starters, a service\ndiscovery and an OAuth2 service. The vision is to create a system with a very easy integration\nof new applications by just adding a few Spring Boot Starters and starting to code the specific functionality of the new\nservice.","blog/welcome-to-spring-ldap-with-ssl-the-entrance-is-free",[27980,1426,27981,27982],"oauth2","spring-boot","spring-ldap","Some time ago we started to create our own intranet called OpenCoffee, which is a microservice landscape based on our own Spring Boot Starters, a service discovery and an OAuth2…","t80MrMboDJkPsgFP9FgZjHfjRfpqmJUiyUzT4la3Q2s",{"id":27986,"title":27987,"author":27988,"body":27989,"category":28083,"date":28084,"description":28085,"extension":1034,"link":28086,"meta":28087,"navigation":916,"path":28088,"seo":28089,"slug":27993,"stem":28090,"tags":28091,"teaser":28092,"__hash__":28093},"blog/blog/synyx-at-the-fosdem-2017.md","synyx at the FOSDEM 2017",[139],{"type":432,"value":27990,"toc":28081},[27991,27995,28001,28006,28009,28012,28015,28021,28029,28038,28044,28050,28056,28062,28068,28075,28078],[435,27992,27994],{"id":27993},"synyx-at-the-fosdem-2017","synyx at the FOSDEM 2017",[439,27996,27997,27998],{},"As a part of our annual budget we decided to visit the ",[448,27999,28000],{},"Free and Open Source Developers’ European Meeting (FOSDEM).",[439,28002,28003],{},[448,28004,28005],{},"What is FOSDEM?",[439,28007,28008],{},"It’s is a free, community-driven developer event at the ULB Solbosch Campus, Brussel. It was first held in 2001 and has\nsince been growing. Besides the main track where speakers can introduce their projects or hold talks in separate rooms\nthere were a bunch of developer rooms, workshops and certification possibilities.",[439,28010,28011],{},"As an sysadmin trainee I found myself enjoying all this new input on my first business trip. All of the speakers I\ntalked to were very open and helpful.",[439,28013,28014],{},"Some of the most interesting talks I’ve attended were:",[439,28016,28017,28020],{},[448,28018,28019],{},"Daniel Stenberg"," held an interesting and funny talk about the history of curl and how it became such an important\ntool. He also talked about the future of the projects, problems and risks.",[439,28022,28023,28028],{},[1002,28024,28027],{"href":28025,"rel":28026},"https://fosdem.org/2017/schedule/event/curl/",[1006],"Link"," to the talk.",[439,28030,28031,8351,28034,28037],{},[448,28032,28033],{},"Vesna",[448,28035,28036],{},"Manojlovic",", Community Builder at RIPE NCC, talked about Ethics in Network Measurements. She explained why\nthe field is political and how we should act to keep the internet, programming and technology ethically sound. In my\nopinion one of the talks everybody in IT should see.",[439,28039,28040,28028],{},[1002,28041,28027],{"href":28042,"rel":28043},"https://fosdem.org/2017/schedule/event/network_measurement_ethics/",[1006],[439,28045,28046,28049],{},[448,28047,28048],{},"Hanno Böck","s talk “Is the Linux Desktop less secure than Windows 10?” examined the vulnerabilities of the Linux\nDesktop and how the Super Mario soundtrack can own your system. Besides the possible attack points he talked about ways\nto fix it and asked the community to help.",[439,28051,28052,28028],{},[1002,28053,28027],{"href":28054,"rel":28055},"https://fosdem.org/2017/schedule/event/linux_desktop_versus_windows10/",[1006],[439,28057,28058,28061],{},[448,28059,28060],{},"Andrew Savchenko"," gave a gentle overview of “Quantum computing and post-quantum cryptography”. The risk for our\ncryptography and how we can possibly safely encrypt data in the future. As an interesting talk about a very complicated\ntopic, the “gentle overview” turned out to be very complex.",[439,28063,28064,28028],{},[1002,28065,28027],{"href":28066,"rel":28067},"https://fosdem.org/2017/schedule/event/quantum/",[1006],[439,28069,28070,28071,1402],{},"There were many more talks on various topics including some interesting lightning talks I invite you to explore and\ndiscuss, found ",[1002,28072,22916],{"href":28073,"rel":28074},"https://fosdem.org/2017/schedule/",[1006],[439,28076,28077],{},"I’d like to thank everybody involved in the FOSDEM organization, speakers, synyx and my coworkers who also went there.",[439,28079,28080],{},"Can’t wait for the next FOSDEM in 2018 with you!",{"title":469,"searchDepth":507,"depth":507,"links":28082},[],[1031],"2017-02-15T07:26:11","As a part of our annual budget we decided to visit the Free and Open Source Developers’ European Meeting (FOSDEM).","https://synyx.de/blog/synyx-at-the-fosdem-2017/",{},"/blog/synyx-at-the-fosdem-2017",{"title":27987,"description":28085},"blog/synyx-at-the-fosdem-2017",[],"As a part of our annual budget we decided to visit the Free and Open Source Developers’ European Meeting (FOSDEM). What is FOSDEM? It’s is a free, community-driven developer…","OqpQMQa3RdFPtrwqiccPSOt2laHSq7WIXWagzlzomqg",{"id":28095,"title":28096,"author":28097,"body":28098,"category":28161,"date":28162,"description":28163,"extension":1034,"link":28164,"meta":28165,"navigation":916,"path":28166,"seo":28167,"slug":28168,"stem":28169,"tags":28170,"teaser":28171,"__hash__":28172},"blog/blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung.md","Kommunikationsgrenzen überschreiten durch Visualisierung",[332],{"type":432,"value":28099,"toc":28159},[28100,28103,28106,28109,28118,28121,28144,28147,28156],[435,28101,28096],{"id":28102},"kommunikationsgrenzen-überschreiten-durch-visualisierung",[439,28104,28105],{},"Als IT-Dienstleister setzt man bekanntlicherweise die Ideen und Wünsche des Kunden um. Deshalb befasst man sich sehr\nfrüh im Projekt mit dessen Fachlichkeit, um ein möglichst tiefes Verständnis über das Produkt und dessen Kontext zu\nerhalten.",[439,28107,28108],{},"Hier bot es sich in unserem neuen Projekt an eine neue Richtung einzuschlagen, um schneller ein einheitliches\nVerständnis zu erlangen. Das neue Projekt beschäftigt sich mit der Unterstützung der Arbeitsabläufe an einem\nContainerterminal. Ein Containerterminal ist ein Umschlagspunkt für Container, welche zum Beispiel für den weiteren\nTransport von einem Binnenschiff auf ein LKW umgeschlagen werden. Bei diesem Projekt bot es sich an das\nContainerterminal als Modell mit den wichtigsten Elementen zu modellieren. Also wurden Kräne, Stacker und LKWs\nkurzerhand nach gebaut. Nein nicht digital. Analog.",[439,28110,28111],{},[1002,28112,28115],{"href":28113,"rel":28114},"https://media.synyx.de/uploads//2017/01/IMG_6110.jpg",[1006],[2205,28116],{"alt":469,"src":28117},"https://media.synyx.de/uploads//2017/01/IMG_6110-1024x683.jpg",[439,28119,28120],{},"Mit unserem Modell sind wir nun in der Lage den kompletten Durchlauf eines Container an einem Containerterminal von der\nEinfahrtkontrolle über die Abfertigung mit einem Kran oder Stacker bis zur Ausfahrtkontrolle nach zu stellen.",[439,28122,28123,28130,8351,28137],{},[1002,28124,28127],{"href":28125,"rel":28126},"https://media.synyx.de/uploads//2017/01/IMG_6103.jpg",[1006],[2205,28128],{"alt":469,"src":28129},"https://media.synyx.de/uploads//2017/01/IMG_6103-150x150.jpg",[1002,28131,28134],{"href":28132,"rel":28133},"https://media.synyx.de/uploads//2017/01/IMG_6093.jpg",[1006],[2205,28135],{"alt":469,"src":28136},"https://media.synyx.de/uploads//2017/01/IMG_6093-150x150.jpg",[1002,28138,28141],{"href":28139,"rel":28140},"https://media.synyx.de/uploads//2017/01/IMG_6100.jpg",[1006],[2205,28142],{"alt":469,"src":28143},"https://media.synyx.de/uploads//2017/01/IMG_6100-150x150.jpg",[439,28145,28146],{},"Die Kommunikation zwischen dem Entwicklerteam und dem Kunden wurde dadurch deutlich vereinfacht. Gerade in Plannings und\nReviews, aber auch bei offenen Fragen beim Bearbeiten eines Tickets während des Sprints, griffen wir immer wieder auf\ndas Modell zurück, um Abläufe zu verstehen und einzelne Details nach zu stellen und diese zu analysieren.",[439,28148,28149],{},[1002,28150,28153],{"href":28151,"rel":28152},"https://media.synyx.de/uploads//2017/01/IMG_6073.jpg",[1006],[2205,28154],{"alt":469,"src":28155},"https://media.synyx.de/uploads//2017/01/IMG_6073-1024x682.jpg",[439,28157,28158],{},"Mit unseren bisherigen Erfahrungen können wir das Vorgehen mit dem Modell nur empfehlen. Es übertrifft Scribbles durch\nseine Dynamik und Interaktionsmöglichkeiten und holt durch den spielerischen Aspekt sehr einfach die Kollegen und den\nKunden ab. Für unsere nächsten Projekte würden wir solch ein Modell sofort wieder einsetzen.",{"title":469,"searchDepth":507,"depth":507,"links":28160},[],[1030],"2017-02-01T13:09:53","Als IT-Dienstleister setzt man bekanntlicherweise die Ideen und Wünsche des Kunden um. Deshalb befasst man sich sehr\\nfrüh im Projekt mit dessen Fachlichkeit, um ein möglichst tiefes Verständnis über das Produkt und dessen Kontext zu\\nerhalten.","https://synyx.de/blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung/",{},"/blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung",{"title":28096,"description":28105},"kommunikationsgrenzen-ueberschreiten-durch-visualisierung","blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung",[14820,26922,9557],"Als IT-Dienstleister setzt man bekanntlicherweise die Ideen und Wünsche des Kunden um. Deshalb befasst man sich sehr früh im Projekt mit dessen Fachlichkeit, um ein möglichst tiefes Verständnis über das…","yBLnpCfDXDWR0Z2cUZDnJDlmWILpt1A3W25OafRG8_8",{"id":28174,"title":28175,"author":28176,"body":28177,"category":28282,"date":28283,"description":28284,"extension":1034,"link":28285,"meta":28286,"navigation":916,"path":28287,"seo":28288,"slug":28181,"stem":28289,"tags":28290,"teaser":28292,"__hash__":28293},"blog/blog/jdk-jongleur.md","JDK Jongleur",[154],{"type":432,"value":28178,"toc":28280},[28179,28182,28185,28188,28200,28203,28206,28216,28219,28222,28232,28235,28245,28248,28258,28261,28264,28275,28278],[435,28180,28175],{"id":28181},"jdk-jongleur",[439,28183,28184],{},"Als Java Entwickler im Dienstleistungs-Umfeld hat man meist mit vielen Projekten und damit auch mit mehreren\nJDK-Versionen zu tun. Und selbst wenn man nur an einem Projekt mit fixer JDK-Version arbeitet, steht immer ein neues\nJDK vor der Tür, dessen Features man kennenlernen will oder dessen Kompatibilität mit dem aktuellen Projekt untersucht\nwerden soll.",[439,28186,28187],{},"Die unterschiedlichen Betriebssysteme haben alle mehr oder weniger gute Mechanismen, um systemweit das verwendete JDK zu\nändern. Bei einer globalen Änderung der JDK-Version kann es zumindest bei Major-Versionssprüngen unschöne Nebeneffekte\ngeben. Daher ist es meistens sinnvoller, das gewünschte JDK aus dem Internet herunterzuladen und dann lokal für eine\nShell eines Benutzers durch das Setzen von PATH und JAVA_HOME verfügbar machen.",[439,28189,28190,28191,1483,28196,28199],{},"Diese manuellen Schritte nimmt einem ",[1002,28192,28195],{"href":28193,"rel":28194},"https://github.com/shyiko/jabba",[1006],"Jabba",[1002,28197,28193],{"href":28193,"rel":28198},[1006],") ab. Jabba\nist ein in Go geschriebenes Tool, das zum einen Download und lokale Installation verschiedenster JDK-Versionen erledigt\nund zum anderen das lokale Wechseln dieser Versionen sehr einfach macht.",[439,28201,28202],{},"Nach der Installation des Tools kann man sich per ‘jabba ls-remote’",[439,28204,28205],{},"alle für Jabba gepflegten JDKs auflisten lassen:",[464,28207,28209],{"className":16895,"code":28208,"language":16897,"meta":469,"style":469},"jabba ls-remote\n\n",[471,28210,28211],{"__ignoreMap":469},[474,28212,28213],{"class":476,"line":477},[474,28214,28215],{},"jabba ls-remote\n",[439,28217,28218],{},"Neue JDKs können auf dem Github Repo von Jabba als PullRequest hinzugefügt werden (falls es die Maintainer nicht schon\ngemacht haben).",[439,28220,28221],{},"JDK-Versionen, die lokal noch nicht installiert wurden, können mit ‘jabba install’ installiert werden.",[464,28223,28225],{"className":16895,"code":28224,"language":16897,"meta":469,"style":469},"jabba install 1.8.112\n\n",[471,28226,28227],{"__ignoreMap":469},[474,28228,28229],{"class":476,"line":477},[474,28230,28231],{},"jabba install 1.8.112\n",[439,28233,28234],{},"Falls es schon systemweit installierte JDKs gibt, können diese mit ‘jabba link’ verlinkt und damit zur lokalen\nVerwendung in Jabba bereitgestellt werden.",[464,28236,28238],{"className":16895,"code":28237,"language":16897,"meta":469,"style":469},"jabba link system@1.8.112 /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk\n\n",[471,28239,28240],{"__ignoreMap":469},[474,28241,28242],{"class":476,"line":477},[474,28243,28244],{},"jabba link system@1.8.112 /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk\n",[439,28246,28247],{},"Will man in seiner Shell ein bestimmtes JDK nutzen, kann dieses einfach per ‘jabba use’ ausgewählt werden:",[464,28249,28251],{"className":16895,"code":28250,"language":16897,"meta":469,"style":469},"jabba use 1.8.112\n\n",[471,28252,28253],{"__ignoreMap":469},[474,28254,28255],{"class":476,"line":477},[474,28256,28257],{},"jabba use 1.8.112\n",[439,28259,28260],{},"Alles in allem ist Jabba ein kleines, handliches Tool, das genau das tut, was es soll und mir meinen Arbeitsalltag mit\nunterschiedlichen und zum Teil auch early-access Versionen von JDK 9 erheblich einfacher macht.",[439,28262,28263],{},"Wie grenzt sich Jabba nun von ähnlichen Tools (oder Skript-Lösungen) ab?",[994,28265,28266,28269,28272],{},[997,28267,28268],{},"Es bietet eine konsistente Nutzung auf die Linux, Windows und Mac.",[997,28270,28271],{},"Neben dem Setzen der JDK-Version kümmert sich Jabba auch um Download und lokale Installation (aka. Unzip).",[997,28273,28274],{},"Installation und JDK-Wahl haben nur Nutzer- bzw. Shell-lokale Effekte.",[439,28276,28277],{},"An dieser Stelle geht ein herzlicher Dank an Sven Ruppert (@SvenRuppert), der mich auf dieses praktische Tool aufmerksam\ngemacht hat.",[1024,28279,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":28281},[],[1030],"2017-01-16T10:10:07","Als Java Entwickler im Dienstleistungs-Umfeld hat man meist mit vielen Projekten und damit auch mit mehreren\\nJDK-Versionen zu tun. Und selbst wenn man nur an einem Projekt mit fixer JDK-Version arbeitet, steht immer ein neues\\nJDK vor der Tür, dessen Features man kennenlernen will oder dessen Kompatibilität mit dem aktuellen Projekt untersucht\\nwerden soll.","https://synyx.de/blog/jdk-jongleur/",{},"/blog/jdk-jongleur",{"title":28175,"description":28184},"blog/jdk-jongleur",[28291],"jdk","Als Java Entwickler im Dienstleistungs-Umfeld hat man meist mit vielen Projekten und damit auch mit mehreren JDK-Versionen zu tun. Und selbst wenn man nur an einem Projekt mit fixer JDK-Version arbeitet,…","CGBoSzrYGMvHm9e95aYCx2ggm7EDm8hKSNFlLS7z8gk",{"id":28295,"title":28296,"author":28297,"body":28298,"category":28412,"date":28413,"description":28414,"extension":1034,"link":28415,"meta":28416,"navigation":916,"path":28417,"seo":28418,"slug":28302,"stem":28419,"tags":28420,"teaser":28421,"__hash__":28422},"blog/blog/lichtschwerterundso-und-wieder-geht-es-mit-synyx-ins-kino.md","#lichtschwerterundso – und wieder geht es mit synyx ins Kino",[172],{"type":432,"value":28299,"toc":28410},[28300,28303,28306,28309,28312,28315,28322,28329,28338,28347,28356,28365,28374,28383,28392,28401],[435,28301,28296],{"id":28302},"lichtschwerterundso-und-wieder-geht-es-mit-synyx-ins-kino",[439,28304,28305],{},"Nachdem wir nach unserem synema letztes Jahr so viel positives Feedback erhalten haben, war klar, dass wir das\nwiederholen wollen. Passend, dass wieder ein Star Wars Film, dieses mal “ROGUE ONE: A STAR WARS STORY”, seine Premiere\nim Dezember feierte. Dieses mal konnten wir unser Event in der kultigen, wunderschönen Schauburg ausrichten.",[439,28307,28308],{},"Am Freitag, den 16.12. starteten wir um 18 Uhr mit Brezeln, Brownies und ein paar Kaltgetränken. Nach dem\nCome-Together konnte es losgehen. Janine, Agile Coach bei synyx, begrüßte das Publikum und den Überraschungsgast des\nAbends: Prinzessin Leia! Was für eine Sensation 🙂 Und das war längst nicht alles. Denn auch Leia hatte eine Überraschung\nim Gepäck: 5 Lego Star Wars Sets! Diese wurden nach dem Film unter den Gästen verlost.",[439,28310,28311],{},"Zum Abschluss des Abends wurde noch das ein oder andere Getränk bei netten Gesprächen zu sich genommen. Es war ein\ngelungenes Event und ein toller Abend, oder? Und wer weiß, nächstes Jahr startet Stars Wars 8…..",[439,28313,28314],{},"Auf unserem YouTube Kanal gibt es dazu noch 2 Videos:",[439,28316,28317],{},[1002,28318,28321],{"href":28319,"rel":28320},"https://youtu.be/u3xhhAKC8X4",[1006],"synema – Film",[439,28323,28324],{},[1002,28325,28328],{"href":28326,"rel":28327},"https://youtu.be/KcnyHnS2i_s",[1006],"synema – Das Interview",[439,28330,28331],{},[1002,28332,28335],{"href":28333,"rel":28334},"https://media.synyx.de/uploads//2016/12/IMG_5938.jpg",[1006],[2205,28336],{"alt":469,"src":28337},"https://media.synyx.de/uploads//2016/12/IMG_5938-683x1024.jpg",[439,28339,28340],{},[1002,28341,28344],{"href":28342,"rel":28343},"https://media.synyx.de/uploads//2016/12/IMG_5847.jpg",[1006],[2205,28345],{"alt":469,"src":28346},"https://media.synyx.de/uploads//2016/12/IMG_5847-1024x683.jpg",[439,28348,28349],{},[1002,28350,28353],{"href":28351,"rel":28352},"https://media.synyx.de/uploads//2016/12/IMG_5970.jpg",[1006],[2205,28354],{"alt":469,"src":28355},"https://media.synyx.de/uploads//2016/12/IMG_5970-1024x683.jpg",[439,28357,28358],{},[1002,28359,28362],{"href":28360,"rel":28361},"https://media.synyx.de/uploads//2016/12/IMG_5862.jpg",[1006],[2205,28363],{"alt":469,"src":28364},"https://media.synyx.de/uploads//2016/12/IMG_5862-1024x683.jpg",[439,28366,28367],{},[1002,28368,28371],{"href":28369,"rel":28370},"https://media.synyx.de/uploads//2016/12/IMG_5954.jpg",[1006],[2205,28372],{"alt":469,"src":28373},"https://media.synyx.de/uploads//2016/12/IMG_5954-1024x683.jpg",[439,28375,28376],{},[1002,28377,28380],{"href":28378,"rel":28379},"https://media.synyx.de/uploads//2016/12/IMG_5780.jpg",[1006],[2205,28381],{"alt":469,"src":28382},"https://media.synyx.de/uploads//2016/12/IMG_5780-1024x683.jpg",[439,28384,28385],{},[1002,28386,28389],{"href":28387,"rel":28388},"https://media.synyx.de/uploads//2016/12/IMG_5826.jpg",[1006],[2205,28390],{"alt":469,"src":28391},"https://media.synyx.de/uploads//2016/12/IMG_5826-1024x683.jpg",[439,28393,28394],{},[1002,28395,28398],{"href":28396,"rel":28397},"https://media.synyx.de/uploads//2016/12/IMG_5764.jpg",[1006],[2205,28399],{"alt":469,"src":28400},"https://media.synyx.de/uploads//2016/12/IMG_5764-1024x683.jpg",[439,28402,28403],{},[1002,28404,28407],{"href":28405,"rel":28406},"https://media.synyx.de/uploads//2016/12/IMG_5815.jpg",[1006],[2205,28408],{"alt":469,"src":28409},"https://media.synyx.de/uploads//2016/12/IMG_5815-1024x683.jpg",{"title":469,"searchDepth":507,"depth":507,"links":28411},[],[1031],"2016-12-22T12:52:21","Nachdem wir nach unserem synema letztes Jahr so viel positives Feedback erhalten haben, war klar, dass wir das\\nwiederholen wollen. Passend, dass wieder ein Star Wars Film, dieses mal “ROGUE ONE: A STAR WARS STORY”, seine Premiere\\nim Dezember feierte. Dieses mal konnten wir unser Event in der kultigen, wunderschönen Schauburg ausrichten.","https://synyx.de/blog/lichtschwerterundso-und-wieder-geht-es-mit-synyx-ins-kino/",{},"/blog/lichtschwerterundso-und-wieder-geht-es-mit-synyx-ins-kino",{"title":28296,"description":28305},"blog/lichtschwerterundso-und-wieder-geht-es-mit-synyx-ins-kino",[],"Nachdem wir nach unserem synema letztes Jahr so viel positives Feedback erhalten haben, war klar, dass wir das wiederholen wollen. Passend, dass wieder ein Star Wars Film, dieses mal “ROGUE…","qReBFZA9Gcvb-MI-0AlhrrWOHYX_1n_M0PYOVqe55NE",{"id":28424,"title":28425,"author":28426,"body":28427,"category":28657,"date":28658,"description":28659,"extension":1034,"link":28660,"meta":28661,"navigation":916,"path":28662,"seo":28663,"slug":28431,"stem":28665,"tags":28666,"teaser":28672,"__hash__":28673},"blog/blog/android-installing-multiple-variations-of-the-same-app-on-one-device.md","Android: Installing multiple variations of the same app on one device",[190],{"type":432,"value":28428,"toc":28652},[28429,28432,28441,28444,28448,28451,28454,28561,28564,28568,28571,28577,28581,28584,28644,28647,28650],[435,28430,28425],{"id":28431},"android-installing-multiple-variations-of-the-same-app-on-one-device",[439,28433,28434,28435,28440],{},"This post is bit of a follow up to my last post on how\nto ",[1002,28436,28439],{"href":28437,"rel":28438},"https://synyx.de/2016/08/android-building-apks-for-different-environments-using-build-types-and-product-flavors/",[1006],"build an app for multiple environments and local development using build types and product flavors",".\nWe will focus on how to install multiple of the resulting APKs on a single device.",[439,28442,28443],{},"Given the scenario that we use some hardware devices for development, but we also use those devices to review our\nprogress over the last weeks with our customers. We don’t want to uninstall / reinstall the app every time, as this\nwould also mean to lose all data (if we use the debug certificate for our development and the release certificate for\naccessing actual systems like discribed in the previous post). So we’d like to install both, the debug APK and the APK\nfor a test / demo environment on one device. How can we achieve that?",[3938,28445,28447],{"id":28446},"modifiying-the-applicationid","Modifiying the applicationId",[439,28449,28450],{},"To install the app multiple times, you have to provide a unique applicationId to every instance you want to install.",[439,28452,28453],{},"You can do that with the ‘applicationIdSuffix’ property in the buildTypes:",[464,28455,28459],{"className":28456,"code":28457,"language":28458,"meta":469,"style":469},"language-groovy shiki shiki-themes github-light github-dark"," buildTypes {\n debug{\n applicationIdSuffix \".debug\";\n ...\n }\n local{\n applicationIdSuffix \".local\";\n ...\n }\n tst{ // build types may not start with 'test'\n applicationIdSuffix \".test\";\n ...\n }\n stage{\n applicationIdSuffix \".stage\";\n ...\n }\n release {\n // for releases we want to keep the original applicationId\n ...\n }\n }\n\n","groovy",[471,28460,28461,28466,28471,28476,28481,28485,28490,28495,28499,28503,28508,28513,28517,28521,28526,28531,28535,28539,28544,28549,28553,28557],{"__ignoreMap":469},[474,28462,28463],{"class":476,"line":477},[474,28464,28465],{}," buildTypes {\n",[474,28467,28468],{"class":476,"line":507},[474,28469,28470],{}," debug{\n",[474,28472,28473],{"class":476,"line":547},[474,28474,28475],{}," applicationIdSuffix \".debug\";\n",[474,28477,28478],{"class":476,"line":584},[474,28479,28480],{}," ...\n",[474,28482,28483],{"class":476,"line":607},[474,28484,5704],{},[474,28486,28487],{"class":476,"line":642},[474,28488,28489],{}," local{\n",[474,28491,28492],{"class":476,"line":663},[474,28493,28494],{}," applicationIdSuffix \".local\";\n",[474,28496,28497],{"class":476,"line":694},[474,28498,28480],{},[474,28500,28501],{"class":476,"line":700},[474,28502,5704],{},[474,28504,28505],{"class":476,"line":913},[474,28506,28507],{}," tst{ // build types may not start with 'test'\n",[474,28509,28510],{"class":476,"line":920},[474,28511,28512],{}," applicationIdSuffix \".test\";\n",[474,28514,28515],{"class":476,"line":926},[474,28516,28480],{},[474,28518,28519],{"class":476,"line":932},[474,28520,5704],{},[474,28522,28523],{"class":476,"line":938},[474,28524,28525],{}," stage{\n",[474,28527,28528],{"class":476,"line":944},[474,28529,28530],{}," applicationIdSuffix \".stage\";\n",[474,28532,28533],{"class":476,"line":950},[474,28534,28480],{},[474,28536,28537],{"class":476,"line":956},[474,28538,5704],{},[474,28540,28541],{"class":476,"line":962},[474,28542,28543],{}," release {\n",[474,28545,28546],{"class":476,"line":4876},[474,28547,28548],{}," // for releases we want to keep the original applicationId\n",[474,28550,28551],{"class":476,"line":4888},[474,28552,28480],{},[474,28554,28555],{"class":476,"line":4900},[474,28556,5704],{},[474,28558,28559],{"class":476,"line":4913},[474,28560,1276],{},[439,28562,28563],{},"Apply the changes, and the different buildTypes APKs may now co-exist on a device.",[3938,28565,28567],{"id":28566},"which-is-which","Which is which?",[439,28569,28570],{},"Now that we have installed the same app multiple times… how can we know on the first look, which of the apps is for\nwhich environment?",[439,28572,28573],{},[2205,28574],{"alt":28575,"src":28576},"demo1","https://media.synyx.de/uploads//2016/08/demo1-300x133.png",[3938,28578,28580],{"id":28579},"setting-the-activity-titles","Setting the Activity titles",[439,28582,28583],{},"To ensure we can also distinguish the different environments inside the app, we can append the environment from the\nBuildConfig to our Activity titles:",[464,28585,28587],{"className":709,"code":28586,"language":711,"meta":469,"style":469},"public class DemoActivity extends AppCompatActivity {\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n updateTitle();\n }\n private void updateTitle() {\n if(!\"PROD\".equals(BuildConfig.ENVIRONMENT)){\n setTitle(BuildConfig.ENVIRONMENT + \" \" + getTitle());\n }\n }\n}\n",[471,28588,28589,28594,28598,28603,28608,28613,28617,28622,28627,28632,28636,28640],{"__ignoreMap":469},[474,28590,28591],{"class":476,"line":477},[474,28592,28593],{},"public class DemoActivity extends AppCompatActivity {\n",[474,28595,28596],{"class":476,"line":507},[474,28597,21043],{},[474,28599,28600],{"class":476,"line":547},[474,28601,28602],{}," protected void onCreate(Bundle savedInstanceState) {\n",[474,28604,28605],{"class":476,"line":584},[474,28606,28607],{}," super.onCreate(savedInstanceState);\n",[474,28609,28610],{"class":476,"line":607},[474,28611,28612],{}," updateTitle();\n",[474,28614,28615],{"class":476,"line":642},[474,28616,1276],{},[474,28618,28619],{"class":476,"line":663},[474,28620,28621],{}," private void updateTitle() {\n",[474,28623,28624],{"class":476,"line":694},[474,28625,28626],{}," if(!\"PROD\".equals(BuildConfig.ENVIRONMENT)){\n",[474,28628,28629],{"class":476,"line":700},[474,28630,28631],{}," setTitle(BuildConfig.ENVIRONMENT + \" \" + getTitle());\n",[474,28633,28634],{"class":476,"line":913},[474,28635,5704],{},[474,28637,28638],{"class":476,"line":920},[474,28639,1276],{},[474,28641,28642],{"class":476,"line":926},[474,28643,703],{},[439,28645,28646],{},"Maybe you’ll want to write the code for updating the title a bit cleaner, but that’s it for the example 😉",[439,28648,28649],{},"Please let me hear your opinion about this approach in the comments!",[1024,28651,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":28653},[28654,28655,28656],{"id":28446,"depth":507,"text":28447},{"id":28566,"depth":507,"text":28567},{"id":28579,"depth":507,"text":28580},[1031],"2016-12-01T12:31:19","This post is bit of a follow up to my last post on how\\nto build an app for multiple environments and local development using build types and product flavors.\\nWe will focus on how to install multiple of the resulting APKs on a single device.","https://synyx.de/blog/android-installing-multiple-variations-of-the-same-app-on-one-device/",{},"/blog/android-installing-multiple-variations-of-the-same-app-on-one-device",{"title":28425,"description":28664},"This post is bit of a follow up to my last post on how\nto build an app for multiple environments and local development using build types and product flavors.\nWe will focus on how to install multiple of the resulting APKs on a single device.","blog/android-installing-multiple-variations-of-the-same-app-on-one-device",[11132,28667,28668,28669,28670,28671],"apk","build-types","buildtypes","device","environments","This post is bit of a follow up to my last post on how to build an app for multiple environments and local development using build types and product flavors. We…","OjJCSIxdBQyitqKpOhNdC_mLAJijF1bQEV7GlZ7dshs",{"id":28675,"title":28676,"author":28677,"body":28678,"category":28743,"date":28744,"description":28745,"extension":1034,"link":28746,"meta":28747,"navigation":916,"path":28748,"seo":28749,"slug":28682,"stem":28750,"tags":28751,"teaser":28759,"__hash__":28760},"blog/blog/transformation-oder-neuaufbau.md","Transformation oder Neuaufbau?",[312],{"type":432,"value":28679,"toc":28741},[28680,28683,28686,28689,28696,28699,28702,28709,28712,28715,28718,28725,28728,28735,28738],[435,28681,28676],{"id":28682},"transformation-oder-neuaufbau",[439,28684,28685],{},"Viele – es sind leider noch zu wenige! – etablierte Unternehmen erkennen mittlerweile, dass heute andere\nMarktbedingungen vorherrschen und sie nicht mehr so weitermachen können wie bisher. Ihnen fehlt die Fähigkeit sich\nschnell an neue Marktanforderungen anzupassen und schneller neue Produkte, Dienstleistungen oder gar Innovationen an den\nMarkt zu bringen.",[439,28687,28688],{},"Die Gründe dafür? Sie haben mehr oder weniger große technische, organisatorische und innovative Schulden aufgebaut.\nTechnische Schulden und das Bewusstsein dafür gibt es schon länger. Sie haben in der Vergangenheit nur nicht so weh\ngetan. Das Geschäft lief trotzdem. Ok, hier und da hat man gemerkt, dass eine Erneuerung der IT notwendig wäre, aber der\nSchmerz war nicht so groß. Diese Zeiten sind vorbei. Plötzlich sind da Branchenfremde Unternehmen im eigenen Markt aktiv\nund diese Newcomer gewinnen immer mehr Marktanteile mit ihren IT-getriebenen Geschäftsmodellen.",[11947,28690,28691],{},[439,28692,28693],{},[990,28694,28695],{},"„Wir müssen uns komplett neu erfinden, zu einem agilen, flexiblen und innovativen Unternehmen transformieren!“.",[439,28697,28698],{},"Das Verständnis für die Wichtigkeit und Wertschöpfung der IT rückt nun endlich in den Vordergrund. Man wird sich\nbewusst, wenn man nicht den Anschluss verlieren will, muss man seine IT erneuern und näher ans Business bringen. Nur mit\nder IT alleine ist es nicht getan. Um digitale Geschäftsmodelle zu entwickeln und neue Innovationen zu kreieren braucht\nes Zeit, den Blick über den Tellerrand hinaus und vor allem muss man neue Wege gehen. Dies passt so ganz und gar nicht\nzu den meisten bestehenden Organisationsformen, Strukturen und Kulturen der „alten“ Unternehmen. Sie sind auf Effizienz\ngetrimmt. Ihre Prozesse sind auf den optimalen Ablauf des bestehenden Geschäftsmodells ausgerichtet. Alles muss nach\nPlan laufen. In die Zukunft planen funktioniert aber immer weniger, wenn diese sich immer schneller wandelt.",[439,28700,28701],{},"Wie kann man geschäftlich aufholen? Wie schafft man es, neue digitale Produkte, Dienstleistungen oder gar neue\nGeschäftsmodelle zu entwickeln? Wie innovativ und erfolgreich werden? Die Antwort vieler Unternehmer darauf ist: „Wir\nmüssen uns komplett neu erfinden, zu einem agilen, flexiblen und innovativen Unternehmen transformieren!“. Im Ergebnis\nmuss es so sein, nur ob eine Transformation des bestehenden Unternehmens der erste und richtige Weg ist, mag ich\nzumindest bezweifeln.",[11947,28703,28704],{},[439,28705,28706],{},[990,28707,28708],{},"Der Abbau der organisatorischen und prozessualen Schuld ist nicht so einfach durch Zeit und Geld zu erschlagen.",[439,28710,28711],{},"Ich denke schon eine Weile darüber nach, was es für ein klassisches Unternehmen bedeutet sich zu transformieren. Je\nlänger ich darüber nachdenke, umso weniger halte ich es für den richtigen Weg, sondern bleibe häufiger bei einem\nalternativen Vorgehen hängen – einem Neuaufbau eines oder mehrerer Unternehmen neben dem bestehenden. Aber schauen wir\nuns doch einmal an, was es bedeuten würde, wenn sich ein klassisches Unternehmen vornimmt sich zu transformieren.",[439,28713,28714],{},"Angefangen beim Abbau der technologischen Schuld. Der Abbau kostet Zeit und Geld. Das ist definitiv machbar. Der Abbau\nder organisatorischen und prozessualen Schuld ist nicht so einfach durch Zeit und Geld zu erschlagen. Hier stehen große\nVeränderungen bei Mitarbeitern, Hierarchien, bisherigen Handlungs- und Denkmustern, persönlichen Zielvereinbarungen,\nPositionen im Unternehmen, ja gar bei den Aufgaben der Unternehmensführung an. Klassische Hierarchien,\nTop-Down-Ansätze und Command and Control sind von gestern und müssen mehr Selbstorganisation und flexibler\nZusammenarbeit weichen. Kommunikation auf Augenhöhe ist erwünscht. Das sind viele und sehr, sehr hohe Hürden, die\nüberwunden werden wollen. Das erfordert nicht nur einen organisatorischen sondern auch einen kulturellen Wandel.",[439,28716,28717],{},"Das klingt stark nach einer Herkulesaufgabe, mit einer ungewissen Zukunft vor Augen. Und zuletzt helfen die ganzen\nAnstrengungen einer Unternehmenstransformation nicht, wenn man nicht in der Lage ist innovativer zu werden. Innovation\nhängt wiederum ganz stark von den Menschen ab, die im Unternehmen tätig sind. Sind das kreative Menschen? Hat man\nKreativität, Offenheit und Querdenkern einen hohen Stellenwert bei Personalentscheidungen beigemessen? Oder hat man doch\neher Spezialisten mit linearem Lebenslauf und genau den notwendigen Fähigkeiten für die fachlichen Anforderungen der\nvorhandenen Prozesse, den Vorzug gegeben? War es möglich bzw. sogar gewünscht Dinge auszuprobieren, Fehler zu machen und\ndaraus zu lernen oder war es bisher eher so, dass Fehler auf Teufel komm raus vermieden werden mussten? Sprechen die\nvergangen Personalentscheidungen und die Mitarbeiterentwicklung eher für oder gegen Innovationsvorhaben?",[11947,28719,28720],{},[439,28721,28722],{},[990,28723,28724],{},"Mit einer Transformation und deren Konsequenzen stört man die laufenden Prozesse und gefährdet damit das bisherige\nGeschäftsmodell, welches aktuell Einnahmen sichert und Stabilität bringt.",[439,28726,28727],{},"Die meist vorherrschenden technischen, organisatorischen und innovativen Schulden sind in meinen Augen eine denkbar\nschlechte Voraussetzung für eine Transformation hin zu einem agilen, schnellen und innovativen Unternehmen. Die\nvorhandenen Prozesse sind auf das bestehende Geschäft ausgerichtet. Mit einer Transformation und deren Konsequenzen\nstört man die laufenden Prozesse und gefährdet damit das bisherige Geschäftsmodell, welches aktuell Einnahmen sichert\nund Stabilität bringt. Eine Transformation stellt demnach auch in finanzieller Hinsicht ein hohes Risiko dar. Nämlich\nwenn die Qualität oder der Output aufgrund von Veränderungen, Missstimmungen oder sonstiger “Nebenwirkungen” leiden.\nAber es wird nicht nur das bestehende Geschäft negativ beeinflusst, sondern auch das parallel neu aufzubauende Geschäft\nkann negativ tangiert werden. Wenn es viele Abhängigkeiten zu der bisherigen und wenig agilen Unternehmensorganisation\ngibt, kann ich mir die erfolgreiche Entwicklung eines neuen Geschäftsmodells innerhalb bestehender Strukturen nicht\nvorstellen, sonst gäbe es doch diese bereits schon, wenn nicht genau das im Wege stünde.",[11947,28729,28730],{},[439,28731,28732],{},[990,28733,28734],{},"Das bestehende Unternehmen dient dem Neuen als Investor, und sorgt für die notwendige Liquidität.",[439,28736,28737],{},"Ich sehe hier viele Risiken, aber nur wenige Chancen. Viel eher sehe ich die Chance, neben dem bestehenden, ein oder\nmehrere neue Unternehmen mit neuem Geschäftsmodell und flexibler Organisation, sowie vielfältigen und breit\naufgestellten Menschen aufzubauen. Und dies, ohne die Abhängigkeiten der bestehenden Unternehmensorganisation! Das\nbestehende Unternehmen dient dem Neuen als Investor, und sorgt für die notwendige Liquidität. Dabei wird der\nLebenszyklus des bestehenden Unternehmens mindestens so lange verlängert, bis das neue Unternehmen selbstständig\nbestehen kann. Wenn dies geschafft ist, kann man sich überlegen wie man mit dem „alten“ Unternehmen umgeht. Man kann es\nnach und nach abwickeln oder jetzt eine Transformation angehen, welche wiederum durch das neu geschaffenen Unternehmen\nfinanziert wird.",[439,28739,28740],{},"Aber egal welchen Weg man beschreitet, man sollte sich bewusst sein, dass hinter einem Unternehmen immer Menschen\nstehen. Und in diesem Fall sollte man den Menschen aus dem alten Unternehmen dafür danken, dass auch sie den Neuaufbau\nermöglicht haben. Dies sollte von vornherein als konkretes Ziel in der ganzen Strategie berücksichtigt werden. Mit dem\nbewussten Ziel des „Zurückgebens“ wäre ein positiver und motivierender Ausblick für die Zukunft geschaffen. Das ist ein\nKonzept, welches für mich schlüssig ist und dem ich mehr Erfolgschancen gebe, als einer Transformation um jeden Preis.",{"title":469,"searchDepth":507,"depth":507,"links":28742},[],[1031],"2016-11-29T16:12:43","Viele – es sind leider noch zu wenige! – etablierte Unternehmen erkennen mittlerweile, dass heute andere\\nMarktbedingungen vorherrschen und sie nicht mehr so weitermachen können wie bisher. Ihnen fehlt die Fähigkeit sich\\nschnell an neue Marktanforderungen anzupassen und schneller neue Produkte, Dienstleistungen oder gar Innovationen an den\\nMarkt zu bringen.","https://synyx.de/blog/transformation-oder-neuaufbau/",{},"/blog/transformation-oder-neuaufbau",{"title":28676,"description":28685},"blog/transformation-oder-neuaufbau",[28752,28753,12104,28754,28755,28756,28757,28758],"augenhoehe","change-management","disruption","newwork","start-up","strategie","transformation","Viele – es sind leider noch zu wenige! – etablierte Unternehmen erkennen mittlerweile, dass heute andere Marktbedingungen vorherrschen und sie nicht mehr so weitermachen können wie bisher. Ihnen fehlt die…","YwFLRK-amaEQ2KJrnB9yaO80dhdgHKiJgA6YlwGr2rk",{"id":28762,"title":28763,"author":28764,"body":28765,"category":28838,"date":28839,"description":469,"extension":1034,"link":28840,"meta":28841,"navigation":916,"path":28842,"seo":28843,"slug":28844,"stem":28845,"tags":28846,"teaser":28847,"__hash__":28848},"blog/blog/eindruecke-von-der-w-jax-2016.md","Eindrücke von der W-JAX 2016",[312],{"type":432,"value":28766,"toc":28836},[28767,28770,28779,28788,28797,28810,28819,28833],[435,28768,28763],{"id":28769},"eindrücke-von-der-w-jax-2016",[439,28771,28772],{},[1002,28773,28776],{"href":28774,"rel":28775},"https://media.synyx.de/uploads//2016/11/headerwjax-2.jpg",[1006],[2205,28777],{"alt":28778,"src":28774},"headerwjax",[439,28780,28781,28782,28787],{},"Zwischen dem 07. und 11.11.2016 trafen sich mehr als 100 Speaker auf der ",[1002,28783,28786],{"href":28784,"rel":28785},"https://jax.de/",[1006],"W-JAX"," in München und\nhaben rund um Java, Architektur und Software-Innovation ihr Bestes gegeben. synyx war einer der Sponsoren auf der\nW-JAX und ich durfte uns als Ansprechpartner vor Ort vertreten.",[439,28789,28790],{},[1002,28791,28794],{"href":28792,"rel":28793},"https://media.synyx.de/uploads//2016/11/sponsor-1.jpg",[1006],[2205,28795],{"alt":28796,"src":28792},"sponsor",[439,28798,28799,28800,28805,28806,1402],{},"Mich haben besonders die Talks rund um die Digitale Transformation interessiert. Mit meinem Shirt „LETS AUTOMATE THE\nSHIT OUT OF IT“. War ich für den Talk von ",[1002,28801,28804],{"href":28802,"rel":28803},"https://twitter.com/ufried",[1006],"Uwe Friedrichsen"," von codecentric zu „DevOps is\nnot enough – Embedding DevOps in a broader Context“ passend angezogen. Auch wenn der Titel für den ein oder anderen noch\nnichts mit der Transformation von Unternehmen zu tun haben will, hat Uwe Friedrichsen genau das aufgezeigt. Devops ist\nein ganz klarer Treiber für eine Transformation und beeinflusst diese maßgeblich. Vorausgesetzt der Wille ist da, sich\ndarauf einzulassen. Die Slides dazu finden\nsich ",[1002,28807,8234],{"href":28808,"rel":28809},"http://de.slideshare.net/ufried/devops-is-not-enough-embedding-devops-in-a-broader-context",[1006],[439,28811,28812],{},[1002,28813,28816],{"href":28814,"rel":28815},"https://media.synyx.de/uploads//2016/11/letsautomatetheshit-e1479307946593.jpg",[1006],[2205,28817],{"alt":28818,"src":28814},"LetsAutomateTheShitOutOfIT",[439,28820,28821,28822,28827,28828,28832],{},"Weiter haben mir die Einblicke von ",[1002,28823,28826],{"href":28824,"rel":28825},"https://twitter.com/onkelkodi",[1006],"Konstantin Diener"," in sein Unternehmen cosee und über\ndie Entwicklung von einer klassisch hierarchischen Unternehmensorganisation hin zu einem Modell von mehr\nSelbstorganisation gefallen. Er hat mit eindrücklichen Beispielen aufgezeigt, wie sich die klassische Management-Denke\nund Hierarchie negativ auf Softwarearchitektur, Betrieb und die Flexibilität des Unternehmens ausgewirkt und im\nGegensatz dazu, wie sich Verantwortung Übergeben positiv auf die Kultur und das Unternehmen ausgewirkt\nhaben. ",[1002,28829,17461],{"href":28830,"rel":28831},"https://kdiener.medium.com/vortrag-wie-cloud-technologien-das-unternehmen-ver%C3%A4ndern-5d84bd2fedb0",[1006],"\ngeht es zu den Slides.",[439,28834,28835],{},"In den 2 Tagen auf der W-JAX konnte ich einige interessante Eindrücke mitnehmen. Und wer weiß, vielleicht sieht man\nsich mal von der anderen Seite des Rednerpults.",{"title":469,"searchDepth":507,"depth":507,"links":28837},[],[1031],"2016-11-15T09:30:49","https://synyx.de/blog/eindruecke-von-der-w-jax-2016/",{},"/blog/eindruecke-von-der-w-jax-2016",{"title":28763,"description":469},"eindruecke-von-der-w-jax-2016","blog/eindruecke-von-der-w-jax-2016",[],"Zwischen dem 07. und 11.11.2016 trafen sich mehr als 100 Speaker auf der W-JAX in München und haben rund um Java, Architektur und Software-Innovation ihr Bestes gegeben. synyx war einer…","T34kLKfami--oprHsZb49L7H-W3S14C1vDnvZr18bTQ",{"id":28850,"title":28851,"author":28852,"body":28853,"category":29254,"date":29255,"description":28860,"extension":1034,"link":29256,"meta":29257,"navigation":916,"path":29258,"seo":29259,"slug":28857,"stem":29260,"tags":29261,"teaser":29264,"__hash__":29265},"blog/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors.md","Bean X of type Y is not eligible for getting processed by all BeanPostProcessors",[166],{"type":432,"value":28854,"toc":29246},[28855,28858,28861,28865,28868,28871,28913,28933,28938,28947,28953,28967,28971,28978,29002,29009,29013,29016,29028,29032,29035,29046,29053,29056,29065,29078,29106,29109,29113,29116,29144,29147,29198,29219,29228,29231,29235,29238,29241,29244],[435,28856,28851],{"id":28857},"bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",[439,28859,28860],{},"Recently we had a problem related Springs auto-proxy feature that I think is worth writing about.",[3938,28862,28864],{"id":28863},"the-problem","The Problem",[439,28866,28867],{},"We use Spring as our framework of choice because it provides us with a nice set of convenience features when\nbootstrapping and plugging together our application.",[439,28869,28870],{},"One of these features is caching: We cache our users’ roles because their definitions are stored in a pretty slow\nexternal system and change rarely.",[464,28872,28874],{"className":709,"code":28873,"language":711,"meta":469,"style":469},"\n@Component\npublic class RoleRepository {\n @Cacheable(CacheConfig.ROLES_NAME)\n public Set\u003CRole> loadRoles() {\n // .. slow call to external system\n }\n}\n\n",[471,28875,28876,28880,28885,28890,28895,28900,28905,28909],{"__ignoreMap":469},[474,28877,28878],{"class":476,"line":477},[474,28879,917],{"emptyLinePlaceholder":916},[474,28881,28882],{"class":476,"line":507},[474,28883,28884],{},"@Component\n",[474,28886,28887],{"class":476,"line":547},[474,28888,28889],{},"public class RoleRepository {\n",[474,28891,28892],{"class":476,"line":584},[474,28893,28894],{}," @Cacheable(CacheConfig.ROLES_NAME)\n",[474,28896,28897],{"class":476,"line":607},[474,28898,28899],{}," public Set\u003CRole> loadRoles() {\n",[474,28901,28902],{"class":476,"line":642},[474,28903,28904],{}," // .. slow call to external system\n",[474,28906,28907],{"class":476,"line":663},[474,28908,15319],{},[474,28910,28911],{"class":476,"line":694},[474,28912,703],{},[439,28914,28915,28916,28919,28920,28923,28924,28929,28930,28932],{},"Since our ",[471,28917,28918],{},"RoleRepository"," is a component managed by Spring it gets picked up automatically during boot and\n",[471,28921,28922],{},"loadRoles()"," gets backed by a cache. Spring implements this by proxying our repository.\nSee ",[1002,28925,28928],{"href":28926,"rel":28927},"http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html",[1006],"Spring Reference Documentation","\nfor details. As a result the “real” ",[471,28931,28922],{}," method gets triggered only once in 10 minutes and all the other calls\nare served from the cache.",[439,28934,28935,28936,1402],{},"After several sprints we noticed a problem with the caching. While in caching worked for other beans it stopped working\nfor the shown ",[471,28937,28918],{},[439,28939,28940,28941,28943,28944,28946],{},"We noticed this because our health-check which (indirectly) triggers the ",[471,28942,28922],{}," and runs every 5 seconds did not\nhit the cache and therefore produced a log-entry every 5 seconds. The cache for roles was empty, regardless of how\noften ",[471,28945,28922],{}," was called.",[439,28948,28949,28950,28952],{},"While debugging the issue we found out that the proxy that should do the caching was not generated for ",[471,28951,28918],{},".\nDuring bootstrap of the application Spring gave us a corresponding hint:",[464,28954,28956],{"className":16895,"code":28955,"language":16897,"meta":469,"style":469},"\nINFO 7189 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'RoleRepository' of type [class ...RoleRepository] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n\n",[471,28957,28958,28962],{"__ignoreMap":469},[474,28959,28960],{"class":476,"line":477},[474,28961,917],{"emptyLinePlaceholder":916},[474,28963,28964],{"class":476,"line":507},[474,28965,28966],{},"INFO 7189 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'RoleRepository' of type [class ...RoleRepository] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n",[3938,28968,28970],{"id":28969},"what-is-a-beanpostprocessor","What is a BeanPostProcessor",[439,28972,28973,28974,28977],{},"A ",[471,28975,28976],{},"BeanPostProcessor"," is a special component that allows to manipulate other beans after they are created.",[439,28979,28980,28981,28984,28985,28988,28989,520,28992,520,28995,28998,28999,29001],{},"These postprocessors can manipulate beans or even replace them completely. Spring ships with several implementations\nthat do all kinds of stuff. For example there is one that checks if a bean implements ",[471,28982,28983],{},"ApplicationContextAware"," and sets\nthe ",[471,28986,28987],{},"ApplicationContext"," if so. Also many of the “proxy-stuff” for ",[471,28990,28991],{},"@Async",[471,28993,28994],{},"@Transactional",[471,28996,28997],{},"@Caching"," and so on is\ndone using a ",[471,29000,28976],{},". You can also implement your own postprocessors.",[439,29003,29004,29005,29008],{},"To be able to postprocess all beans with all ",[471,29006,29007],{},"BeanPostProcessors"," Spring has to create them before it creates the\n“regular” beans.",[3938,29010,29012],{"id":29011},"the-chicken-and-the-egg","The Chicken and the Egg",[439,29014,29015],{},"But you can also inject Spring beans into postprocessors. If you inject a bean into a postprocessor Spring has to create\nthis bean even before it creates the corresponding postprocessor you inject it to. In this case it no longer guarantees\nthat all postprocessors are able to process the injected bean but instead logs the message shown above.",[439,29017,29018,29019,29021,29022,29025,29026,1402],{},"In our example the log-message means that there is one postprocessor that (directly or indirectly) leads to creation of\nour ",[471,29020,28918],{}," and there are more postprocessors to be created later (probaly the one that handles ",[471,29023,29024],{},"@Cachable",")\nthat will not be able to post-process our ",[471,29027,28918],{},[3938,29029,29031],{"id":29030},"debugging-the-problem","Debugging the Problem",[439,29033,29034],{},"Unfortunately the log-entry does not help very much in finding out what exactly is the problem.",[439,29036,29037,29038,29041,29042,29045],{},"The statement is produced by ",[471,29039,29040],{},"BeanPostProcessorChecker"," which is an inner class in ",[471,29043,29044],{},"PostProcessorRegistrationDelegate",".\nTo find out what exactly happens here I set a breakpoint right at the log-statement and booted our application in\ndebug-mode. Since there are several beans created I waited for the statement in question (in our case the one with\nbeanName = roleRepository).",[439,29047,29048,29049,29052],{},"To find out what caused the creation of our roleRepository I simply followed the call-stack down the ",[471,29050,29051],{},"getObject","\nmethods. In our case the chain was:",[439,29054,29055],{},"roleRepository -> authorityService -> customerPermissionEvaluator -> delegatingPermissionEvaluator ->\ncustomMethodSecurityConfiguration -> methodSecurityMetadataSource -> (… more Spring setup code …)",[439,29057,29058],{},[1002,29059,29062],{"href":29060,"rel":29061},"https://media.synyx.de/uploads//2016/11/debugging_getObject.png",[1006],[2205,29063],{"alt":29064,"src":29060},"debugging_getobject",[439,29066,29067,29068,29070,29071,29074,29075,6562],{},"So the ",[471,29069,28918],{}," is created because it is needed by our custom implementation of Spring Securities\n",[471,29072,29073],{},"PermissionEvaluator","-Interface that is used to evaluate security related expressions like the ones that can be used\nwith ",[471,29076,29077],{},"@PreAuthorize",[464,29079,29081],{"className":709,"code":29080,"language":711,"meta":469,"style":469},"\n@PreAuthorize(\"hasPermission(#customer, AUTHORITY_BILLING)\")\npublic Optional\u003CBillingDoc> findById(Customer customer, String documentId) {\n // boring business logic here\n}\n\n",[471,29082,29083,29087,29092,29097,29102],{"__ignoreMap":469},[474,29084,29085],{"class":476,"line":477},[474,29086,917],{"emptyLinePlaceholder":916},[474,29088,29089],{"class":476,"line":507},[474,29090,29091],{},"@PreAuthorize(\"hasPermission(#customer, AUTHORITY_BILLING)\")\n",[474,29093,29094],{"class":476,"line":547},[474,29095,29096],{},"public Optional\u003CBillingDoc> findById(Customer customer, String documentId) {\n",[474,29098,29099],{"class":476,"line":584},[474,29100,29101],{}," // boring business logic here\n",[474,29103,29104],{"class":476,"line":607},[474,29105,703],{},[439,29107,29108],{},"So when Spring Security is bootstrapped (which seems to be done before caching is) it also initializes a part of our\n“business-beans” which then cannot be postprocessed with caching.",[3938,29110,29112],{"id":29111},"the-fix","The Fix",[439,29114,29115],{},"To fix the problem I cut the eager dependency from our security-code and replaced it by a lazy one.",[464,29117,29119],{"className":709,"code":29118,"language":711,"meta":469,"style":469},"\n@Autowired\npublic CustomerPermissionEvaluator(AuthorityService authorityService) {\n this.authorityService = authorityService;\n}\n\n",[471,29120,29121,29125,29130,29135,29140],{"__ignoreMap":469},[474,29122,29123],{"class":476,"line":477},[474,29124,917],{"emptyLinePlaceholder":916},[474,29126,29127],{"class":476,"line":507},[474,29128,29129],{},"@Autowired\n",[474,29131,29132],{"class":476,"line":547},[474,29133,29134],{},"public CustomerPermissionEvaluator(AuthorityService authorityService) {\n",[474,29136,29137],{"class":476,"line":584},[474,29138,29139],{}," this.authorityService = authorityService;\n",[474,29141,29142],{"class":476,"line":607},[474,29143,703],{},[439,29145,29146],{},"was changed to",[464,29148,29150],{"className":709,"code":29149,"language":711,"meta":469,"style":469},"\n@Autowired\npublic CustomerPermissionEvaluator(ObjectFactory\u003CAuthorityService> authorityServiceObjectFactory) {\n this.authorityServiceObjectFactory = authorityServiceObjectFactory;\n}\n@Override\npublic boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n AuthorityService service = this.authorityServiceObjectFactory.getObject();\n // ... do stuff with service\n}\n\n",[471,29151,29152,29156,29160,29165,29170,29174,29179,29184,29189,29194],{"__ignoreMap":469},[474,29153,29154],{"class":476,"line":477},[474,29155,917],{"emptyLinePlaceholder":916},[474,29157,29158],{"class":476,"line":507},[474,29159,29129],{},[474,29161,29162],{"class":476,"line":547},[474,29163,29164],{},"public CustomerPermissionEvaluator(ObjectFactory\u003CAuthorityService> authorityServiceObjectFactory) {\n",[474,29166,29167],{"class":476,"line":584},[474,29168,29169],{}," this.authorityServiceObjectFactory = authorityServiceObjectFactory;\n",[474,29171,29172],{"class":476,"line":607},[474,29173,703],{},[474,29175,29176],{"class":476,"line":642},[474,29177,29178],{},"@Override\n",[474,29180,29181],{"class":476,"line":663},[474,29182,29183],{},"public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\n",[474,29185,29186],{"class":476,"line":694},[474,29187,29188],{}," AuthorityService service = this.authorityServiceObjectFactory.getObject();\n",[474,29190,29191],{"class":476,"line":700},[474,29192,29193],{}," // ... do stuff with service\n",[474,29195,29196],{"class":476,"line":913},[474,29197,703],{},[439,29199,29200,29201,29204,29205,29208,29209,29212,29213,29216,29217,11288],{},"By using Springs ",[471,29202,29203],{},"ObjectFactory"," and calling its ",[471,29206,29207],{},"getObject()"," later (not during construction of the\n",[471,29210,29211],{},"CustomerPermissionEvaluator",") we delay the creation of ",[471,29214,29215],{},"AuthorityService"," (and of beans it needs such as the\n",[471,29218,28918],{},[439,29220,29221,29222,29224,29225,1402],{},"By doing so they can later be processed by all ",[471,29223,29007],{}," and the log message in question disappeares &\ncaching works again ",[448,29226,29227],{},"\\o/",[439,29229,29230],{},"Note that there are other ways to solve the problem. One other thing I can think of is to change the order the\nPostProcessors are created.",[3938,29232,29234],{"id":29233},"final-thougths","Final Thougths",[439,29236,29237],{},"In conclusion you might want to actively watch the statements Spring logs at INFO and above during your applications\nbootstrap.",[439,29239,29240],{},"Especially the mentioned statement about beans being not eligible for auto-proxying should not contain any of your\nnon-infrastructure beans.",[439,29242,29243],{},"Also make sure there is no eager dependency between your infrastructure-related code and your business-logic. At least\ndouble-check and challenge these decisions. This is especially true because dependency-graphs get pretty big pretty\nfast. In our example there were 20 other “business” beans not eligable for auto-proxying because of the same eager\ndependency.",[1024,29245,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":29247},[29248,29249,29250,29251,29252,29253],{"id":28863,"depth":507,"text":28864},{"id":28969,"depth":507,"text":28970},{"id":29011,"depth":507,"text":29012},{"id":29030,"depth":507,"text":29031},{"id":29111,"depth":507,"text":29112},{"id":29233,"depth":507,"text":29234},[1030],"2016-11-04T09:45:17","https://synyx.de/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors/",{},"/blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",{"title":28851,"description":28860},"blog/bean-x-of-type-y-is-not-eligible-for-getting-processed-by-all-beanpostprocessors",[29262,15546,711,29263,1426],"bug","proxy","Recently we had a problem related Springs auto-proxy feature that I think is worth writing about. The Problem We use Spring as our framework of choice because it provides us…","dbQYToCUutZQNdubN0Gw1vO0n5J1vWrUHeNWvdOO19k",{"id":29267,"title":29268,"author":29269,"body":29270,"category":29318,"date":29319,"description":29320,"extension":1034,"link":29321,"meta":29322,"navigation":916,"path":29323,"seo":29324,"slug":29274,"stem":29325,"tags":29326,"teaser":29327,"__hash__":29328},"blog/blog/synyx-sommerfeyerey-2016.md","synyx-Sommerfeyerey 2016",[103],{"type":432,"value":29271,"toc":29316},[29272,29275,29278,29281,29284,29287,29297,29300,29303,29306],[435,29273,29268],{"id":29274},"synyx-sommerfeyerey-2016",[439,29276,29277],{},"synyx ist in den letzten Jahren nicht nur bezüglich der Mitarbeiterzahl beträchtlich gewachsen. Auch privat hat sich bei\nden synyxern viel verändert: Einige von uns haben geheiratet und sind Eltern geworden. Das Unterschreiben von\nGlückwunschkarten und das Besorgen von Geschenken ist derzeit Teil unseres Firmenalltags, an den wir uns schon fast\ngewöhnt haben. Mehrere neue Mitarbeiter und auch ein paar “alte Hasen” haben bereits Kinder, sodass die Alterspanne des\nsynyx-Nachwuchses von “neugeboren und sowas von zum Knuddeln” bis zu “Hilfe – Pupertät – und trotzdem noch zum\nKnuddeln” reicht. Die meisten Kinder sind jedoch im Kleinkind- und Kindergartenalter. Und all diese Kinder sollten bei\nunserer diesjährigen Sommerfeyerey am 8. Oktober ganz besonders auf ihre Kosten kommen.",[439,29279,29280],{},"Als Location hatten wir uns das Radhaus in Rüppurr ausgesucht. Dort gibt es, neben einer gemütlichen Hütte und einer\ngroßen Terrasse, eine weitläufige Wiese. Darauf befindet sich ein kleiner Spielplatz und es ist noch genügend Platz zum\nRennen und Toben für alle. Aber wir wären nicht synyx, wenn wir einfach nur rennen und toben würden…",[439,29282,29283],{},"Die Bewegungslustigen unter uns durften sich an Goofballz versuchen. Das sind aufblasbare, körpergroße Gummibälle zum\nReinschlüpfen. Kopf und Oberkörper sind durch den Ball geschützt und man hat genug Beinfreiheit, um wild durch die\nGegend zu rennen, Fußball zu spielen, mit anderen Spielern zusammenzurumpeln und sich dann auf dem Boden herumzukugeln.\nUnd das wurde ausgiebigst getan, wenn man denn vor Lachen überhaupt noch rennen konnte. Es macht übrigens auch sehr\ngroßen Spaß, einem Goofballz-Spiel einfach nur zuzuschauen :-).",[439,29285,29286],{},"Wir hatten aber auch ruhigere Spiele wie Kubb und Riesenmikado im Angebot und der Spielplatz wurde zum Austoben bestens\ngenutzt.",[439,29288,29289],{},[1002,29290,29293],{"href":29291,"rel":29292},"https://media.synyx.de/uploads//2016/10/essen.jpg",[1006],[2205,29294],{"alt":29295,"src":29296},"Essen","https://media.synyx.de/uploads//2016/10/essen-300x169.jpg",[439,29298,29299],{},"Nicht nur das Vergnügungsangebot, auch der diesjährige Zeitrahmen war familienfreundlich gestaltet. Feierten wir in der\nVergangenheit oft ganze Nächte durch, so begannen wir in diesem Jahr schon am Nachmittag und machten es uns “nur” bis\nMitternacht in der Radhaushütte gemütlich. Aber der Reihe nach:",[439,29301,29302],{},"Um 16 Uhr gab es Sektempfang und Kinderdiscomusik mit Seifenblasenmaschine. Dann wurde bis 18 Uhr gespielt, getobt,\ngerannt, gekugelt, gelacht. Ab 18 Uhr waren dann kulinarische Vergnügungen angesagt – die Flammkuchenparty begann. Es\ngab ein abwechslungsreiches, mediterran angehauchtes Vorspeisenbuffet und im Anschluss verschiedenste Flammkuchen –\ndeftig und süß. Es wurde serviert bis alle satt waren. Von uns synyxern selbst zubereitete Nachtische schlossen das\ngemeinsame Abendessen ab, u.a. gab es srilankanischen Karamellpudding, verschiedene leckere Kuchen, … und das Mousse au\nChocolat unseres Chefs Thomas kam bei unseren Kindern ganz besonders gut an.",[439,29304,29305],{},"Zu guter Letzt ein herzliches Dankeschön an alle, die mitgeholfen haben, dieses Familienfest zu planen und zu gestalten.\nEs hat sich wieder einmal gezeigt, dass wir synyxer nicht nur super feiern können, sondern auch toll zusammenarbeiten\nund Ideen und Zeit einbringen, um auch außerhalb unseres gemeinsamen Arbeitsalltags eine schöne Zeit miteinander zu\nverbringen.",[439,29307,29308],{},[1002,29309,29312],{"href":29310,"rel":29311},"https://media.synyx.de/uploads//2016/10/getraenk.jpg",[1006],[2205,29313],{"alt":29314,"src":29315},"Getränk","https://media.synyx.de/uploads//2016/10/getraenk-300x169.jpg",{"title":469,"searchDepth":507,"depth":507,"links":29317},[],[1031],"2016-10-27T11:29:43","synyx ist in den letzten Jahren nicht nur bezüglich der Mitarbeiterzahl beträchtlich gewachsen. Auch privat hat sich bei\\nden synyxern viel verändert: Einige von uns haben geheiratet und sind Eltern geworden. Das Unterschreiben von\\nGlückwunschkarten und das Besorgen von Geschenken ist derzeit Teil unseres Firmenalltags, an den wir uns schon fast\\ngewöhnt haben. Mehrere neue Mitarbeiter und auch ein paar “alte Hasen” haben bereits Kinder, sodass die Alterspanne des\\nsynyx-Nachwuchses von “neugeboren und sowas von zum Knuddeln” bis zu “Hilfe – Pupertät – und trotzdem noch zum\\nKnuddeln” reicht. Die meisten Kinder sind jedoch im Kleinkind- und Kindergartenalter. Und all diese Kinder sollten bei\\nunserer diesjährigen Sommerfeyerey am 8. Oktober ganz besonders auf ihre Kosten kommen.","https://synyx.de/blog/synyx-sommerfeyerey-2016/",{},"/blog/synyx-sommerfeyerey-2016",{"title":29268,"description":29277},"blog/synyx-sommerfeyerey-2016",[],"synyx ist in den letzten Jahren nicht nur bezüglich der Mitarbeiterzahl beträchtlich gewachsen. Auch privat hat sich bei den synyxern viel verändert: Einige von uns haben geheiratet und sind Eltern…","9dg6rO-G2GuOl-igEwY4C80ZRfqnwF860rlpGDa38d0",{"id":29330,"title":29331,"author":29332,"body":29333,"category":29411,"date":29412,"description":29413,"extension":1034,"link":29414,"meta":29415,"navigation":916,"path":29416,"seo":29417,"slug":29337,"stem":29418,"tags":29419,"teaser":29423,"__hash__":29424},"blog/blog/agiles-arbeiten-als-dienstleister.md","Agiles Arbeiten als Dienstleister",[83],{"type":432,"value":29334,"toc":29409},[29335,29338,29341,29344,29347,29350,29353,29356,29359,29362,29365,29368,29371,29374,29377,29388,29391,29394,29397,29400,29403,29406],[435,29336,29331],{"id":29337},"agiles-arbeiten-als-dienstleister",[439,29339,29340],{},"“Agiles Arbeiten als Dienstleister – Eine besondere Herausforderung oder in letzter Konsequenz nicht möglich?” Diesen\nTitel haben wir als Aufhänger für die Scrum User Group in Karlsruhe am 5. Oktober gewählt. Als reines\nDienstleistungsunternehmen ist synyx im Alltag mit vielen Herausforderungen konfrontiert, die aus unserer Sicht oft\nspezifisch für das Projektgeschäft sind. Daher wollten Janine Bechtold und ich als Vertreter der ScrumMaster bei synyx\ndie User Group nutzen, um einige Kernprobleme, die unserer Meinung nach die Ursache für die Alltagsprobleme darstellen,\nin großer Runde zu diskutieren. Zum einen erhofften wir uns konkrete Lösungsansätze, zum anderen interessierte uns aber\nauch, wie groß tatsächlich der Unterschied zu anderen Arbeitskontexten, wie z.B. eigene Produktentwicklung oder interne\nIT ist. Denn wie die Leitfrage schon suggeriert, haben wir uns in der Tat schon oft gefragt, ob bestimmte Aspekte agilen\nArbeitens im Dienstleistungsbereich überhaupt umsetzbar sind.",[439,29342,29343],{},"In der Vorbereitung auf den Abend haben wir für uns fünf Kernthemen identifiziert, auf die wir uns fokussiert haben.\nAnhand von kurzen Beispielszenen haben Janine und ich jeweils die Problematik veranschaulicht um Impulse für die\nanschließende Diskussion zu geben:",[439,29345,29346],{},"1. Kein Verständnis für Intrinsische Motivation",[439,29348,29349],{},"Beispiel: Kunde hat keine Produktvision und beharrt auf Auftragserfüllung.",[439,29351,29352],{},"2. Interessenkonflikte beim Dienstleister",[439,29354,29355],{},"Beispiel: Ein Kundenauftrag könnte optimal mit einer Standardsoftware bedient werden, was aber dazu führen würde, dass\ndas Budget für die Beauftragung des Dienstleisters für Individualentwicklung nicht mehr genutzt wird.",[439,29357,29358],{},"3. Entscheidungskompetenz, Kontrolle & versteckte Hierachie",[439,29360,29361],{},"Beispiel: Team möchte eigene Design Ideen in neuer Software umsetzen, um die UX zu verbessern. Product Owner vom Kunden\nbesteht aber auf altem Design, weil seine direkten Vorgesetzten es so wünschen.",[439,29363,29364],{},"4. Crossfunktionalität – Zugriff/Verfügbarkeit von Fachexperten",[439,29366,29367],{},"Beispiel: Zusammenarbeit mit Anwendern aus der Fachabteilung würde für besseres Verständnis der User Stories führen.\nDiese sind aber für die Unterstützung eines externen Dienstleisters nicht verfügbar.",[439,29369,29370],{},"5.Wertekonflikt zwischen Dienstleister und Kunde",[439,29372,29373],{},"Beispiel: Das Entwicklerteam des Dienstleisters hat moralische Bedenken, weil die Umsetzung eines bestimmten Features\nden Endnutzer stark benachteiligen würde. Der Kunde macht mit diesem Feature aber großen Gewinn.",[439,29375,29376],{},"Aufgrund der Teilnehmerzahl haben wir uns in der Gruppe entschieden nur drei der Themen zu bearbeiten und sind daher mit\nfolgenden drei Fragen ins World Café gestartet:",[8310,29378,29379,29382,29385],{},[997,29380,29381],{},"Woher kommt die Motivation im Scrum Team beim Dienstleister?",[997,29383,29384],{},"Wieviel Entscheidungsfreiheit ist notwendig um eine gesunde Balance zwischen Selbstorganisation und Erfüllung des\nKundenwunsches zu gewährleisten.",[997,29386,29387],{},"Können Werte aus zwei verschiedenen Welten vereint werden und wenn ja, was braucht es dafür?",[439,29389,29390],{},"Uns war wichtig, dass am Ende des Abends jeder Teilnehmer etwas mitnimmt, das für die eigene Arbeit möglichst konkret\nanwendbar ist und dort Probleme löst. Daher will ich nicht versuchen die erarbeiteten Ergebnisse allgemein zusammen zu\nfassen sondern stelle hier nur dar, welche Erkenntnisse ich für mich selbst und meine Arbeit bei synyx aus dem Abend\nziehen konnte.",[439,29392,29393],{},"Vertrauen ist die Grundlage für eine gute Zusammenarbeit. Kompromisse einzugehen um ein gemeinsames Ziel zu erreichen,\nschafft Vertrauen und daraus wächst auf beiden Seiten die Bereitschaft für weitere Zugeständnisse. Hierbei ist der\nDienstleister in der Pflicht den ersten Schritt zu tun.",[439,29395,29396],{},"Gleichzeitig sollte der Dienstleister aber auch in der Lage sein, klar Position zu beziehen. Ich glaube, dass Menschen\ndie beste Arbeit machen, wenn sie einen Sinn in ihrer Arbeit sehen. Dieses Verständnis für intrinsische Motivation\nsollte beim Dienstleister existieren. Die eingegangenen Kompromisse bzw. die Zugeständnisse gegenüber dem Kunden dürfen\nnicht dazu führen, dass die Mitarbeiter die Motivation verlieren oder die Sinnhaftigkeit ihrer Arbeit anzweifeln. Wenn\ndie Mitarbeiter nur noch das nötigste machen oder gar kündigen, leidet nicht nur das Projekt des Kunden sondern auch der\nDienstleister selbst hat ernsthafte Probleme.",[439,29398,29399],{},"Als konkret anwendbares Beispiel um eine gemeinsame Motivation zu schaffen, könnte der Dienstleister etwa einen\nVisions-Workshop als essentiellen Bestandteil in jedem Projekt-Kickoff einplanen.",[439,29401,29402],{},"Es gibt viele Aspekte, die ein Team oder einzelne Mitarbeiter motiviert ihre Arbeit zu leisten. Beispielsweise der\nZusammenhalt mit den Kollegen, mit denen man vielleicht sogar befreundet ist. Oder Begeisterung für die Technik, die man\nin einem ansonsten weniger spannenden Projekt einsetzen darf. Gegebenenfalls ist es auch schlicht die Loyalität zum\neigenen Unternehmen, für dessen Fortbestand man auch zeitweise schlechte Projektbedingungen erträgt. In diesen Fällen\nmuss man sich allerdings bewusst machen, wie sich die Motivation des Mitarbeiters auf sein Handeln auswirkt.\nBegeisterung für die Erfüllung des Kundenwunsches ist nicht zwingend erforderlich, aber dann darf man auch keine\nHöchstleistung erwarten. Schließlich ist hier wieder der ScrumMaster in der Verantwortung im Team Gespräche über die\nunterschiedlichen Motivationen anzuregen. Somit hat das Team und insbesondere der Product Owner die Chance andere\nImpulse zu setzen um ggf. eine andere Fokussierung anzuregen.",[439,29404,29405],{},"Zusammenfassend nehme ich mit, dass wir Mut brauchen um für unsere Überzeugung einzustehen. Wenn wir daran glauben, dass\nintrinsisch motivierte Menschen ihre Arbeit besser machen und somit auch den Kunden glücklicher machen, müssen wir auch\nentsprechend handeln. Man sollte daher grundsätzlich jede Chance nutzen, um allen Beteiligten am Prozess die\nKonsequenzen ihres Handelns bewusst zu machen. Es ist die Aufgabe des ScrumMasters dem Ansprechpartner des Kunden zu\nspiegeln, wie sein Verhalten sich auf die Motivation des Entwicklungsteams und somit auf das Produkt auswirkt. Genau so\nmuss er dem Team spiegeln, wie sich ihr Verhalten auf das Vertrauen des Kunden in die Arbeit des Teams auswirkt.",[439,29407,29408],{},"Dann werden wir zwar immernoch viele Herausforderungen haben, aber dann ist aus meiner Sicht agiles Arbeiten auch im\nDienstleistungskontext möglich.",{"title":469,"searchDepth":507,"depth":507,"links":29410},[],[1031],"2016-10-18T09:50:57","“Agiles Arbeiten als Dienstleister – Eine besondere Herausforderung oder in letzter Konsequenz nicht möglich?” Diesen\\nTitel haben wir als Aufhänger für die Scrum User Group in Karlsruhe am 5. Oktober gewählt. Als reines\\nDienstleistungsunternehmen ist synyx im Alltag mit vielen Herausforderungen konfrontiert, die aus unserer Sicht oft\\nspezifisch für das Projektgeschäft sind. Daher wollten Janine Bechtold und ich als Vertreter der ScrumMaster bei synyx\\ndie User Group nutzen, um einige Kernprobleme, die unserer Meinung nach die Ursache für die Alltagsprobleme darstellen,\\nin großer Runde zu diskutieren. Zum einen erhofften wir uns konkrete Lösungsansätze, zum anderen interessierte uns aber\\nauch, wie groß tatsächlich der Unterschied zu anderen Arbeitskontexten, wie z.B. eigene Produktentwicklung oder interne\\nIT ist. Denn wie die Leitfrage schon suggeriert, haben wir uns in der Tat schon oft gefragt, ob bestimmte Aspekte agilen\\nArbeitens im Dienstleistungsbereich überhaupt umsetzbar sind.","https://synyx.de/blog/agiles-arbeiten-als-dienstleister/",{},"/blog/agiles-arbeiten-als-dienstleister",{"title":29331,"description":29340},"blog/agiles-arbeiten-als-dienstleister",[9546,29420,29421,9557,29422],"dienstleistung","intrinsische-motivation","vertrauen","“Agiles Arbeiten als Dienstleister – Eine besondere Herausforderung oder in letzter Konsequenz nicht möglich?” Diesen Titel haben wir als Aufhänger für die Scrum User Group in Karlsruhe am 5. Oktober…","UGNT7JAVKXqxlTTXgTRfm9DpJEiCs0Ab20ODaJY16MU",{"id":29426,"title":29427,"author":29428,"body":29429,"category":30870,"date":30871,"description":30872,"extension":1034,"link":30873,"meta":30874,"navigation":916,"path":30875,"seo":30876,"slug":29433,"stem":30878,"tags":30879,"teaser":30881,"__hash__":30882},"blog/blog/javascript-code-refactoring-automatisieren.md","JavaScript Code Refactoring automatisieren",[335],{"type":432,"value":29430,"toc":30861},[29431,29434,29442,29462,29468,29472,29481,29488,29491,29507,29511,29514,29519,29527,29532,29535,29540,29543,29546,29554,29557,29561,29564,29581,29584,29588,29591,29600,29691,29694,29721,29724,29778,29780,29783,29812,29815,29818,29915,29954,29957,29971,29974,29999,30005,30050,30062,30160,30179,30191,30198,30250,30253,30267,30270,30284,30287,30290,30293,30313,30323,30456,30464,30469,30594,30597,30619,30625,30726,30729,30732,30744,30747,30761,30764,30766,30775,30778,30787,30831,30833,30836,30850,30855,30858],[435,29432,29427],{"id":29433},"javascript-code-refactoring-automatisieren",[439,29435,29436,29437,29441],{},"Vor kurzem hatte ich die Muße ein älteres JavaScript Projekt zu refactoren. Unter anderem sollte die Assertion\nBibliothek Jasmine von 1.x auf 2.x aktualisiert werden. Zwei Dinge gab es bei unseren Tests zu refactoren. Einmal die\nArt von asynchronen Specs und einmal die verwendeten Expectations.\nUnter ",[1002,29438,29439],{"href":29439,"rel":29440},"http://jasmine.github.io/2.0/upgrading.html",[1006]," wurde super\nbeschrieben was für Änderungen man genau machen muss beim Umstieg von Jasmine 1.x auf 2.x.",[439,29443,29444,29445,2040,29448,29451,29452,29455,29456,29461],{},"In diesem Artikel möchte ich zeigen, wie ich die Transformation der ",[471,29446,29447],{},"runs",[471,29449,29450],{},"waitsFor"," Blöcke zum neuen ",[471,29453,29454],{},"done","\nCallback Muster mittels ",[1002,29457,29460],{"href":29458,"rel":29459},"https://github.com/facebook/jscodeshift",[1006],"jscodeshift"," automatisiert habe.",[439,29463,29464],{},[2205,29465],{"alt":29466,"src":29467},"jasmine_async_vergleich","https://media.synyx.de/uploads//2016/08/jasmine_async_vergleich-1024x469.png",[3938,29469,29471],{"id":29470},"jscodeshift-recast-esprima-codemods","jscodeshift / recast / esprima / codemods",[439,29473,29474,29475,29480],{},"Jscodeshift ist ein Werkzeug dass von Facebook gebaut wurde und ",[1002,29476,29479],{"href":29477,"rel":29478},"https://github.com/benjamn/recast",[1006],"recast"," erweitert.\nDieses wiederum arbeitet mit dem Esprima Parser. Dieser baut einen abstrakten Syntaxbaum (engl. abstract source tree,\nAST) auf, der traversiert werden kann.",[439,29482,29483,29484,29487],{},"Mit jscodeshift ist es z. B. möglich, alle anonyme Funktionen herauszuholen und mit einem Namen ",[990,29485,29486],{},"ichBinNichtMehrAnonym","\nzu ersetzen. Ein weiteres, nettes Feature wie ich finde, ist die Beibehaltung der originalen Code Formatierung (so weit\nmöglich).",[439,29489,29490],{},"Man baut also ein Codeschnipsel welches anderen Code umschreibt. Dieses Codeschnipsel wird codemod genannt. Eine\nschnelle Google Suche bringt uns zu zwei interessanten Artikeln; die für das Folgende aber nicht unbedingt gelesen\nwerden müssen 🙂",[994,29492,29493,29500],{},[997,29494,29495],{},[1002,29496,29499],{"href":29497,"rel":29498},"https://vramana.github.io/blog/2015/12/21/codemod-tutorial/",[1006],"How to write a codemod",[997,29501,29502],{},[1002,29503,29506],{"href":29504,"rel":29505},"https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb",[1006],"Effective JavaScript Codemods",[3938,29508,29510],{"id":29509},"automatisieren-von-code-refactorings","Automatisieren von Code Refactorings",[439,29512,29513],{},"Warum automatisieren mag sich der ein oder andere denken. Das kann mehrere Gründe haben. Zum einen hat man vielleicht\nkeinen Auszubildenden zur Verfügung der die Tests umschreiben kann, zum anderen… Nein… Auszubildende bitte mit\njscodeshift ersetzen. Also für die Arbeit des Refactorings… Aber zurück zu den Gründen der Automatisierung.",[439,29515,29516],{},[448,29517,29518],{},"Spaß",[439,29520,29521,29522,29526],{},"Ich musste kurz überlegen wann ich ein Refactoring in JavaScript Projekten gemacht habe, bei dem ich auch Spaß hatte.\nMir fiel keines ein. Die Arbeit ",[29523,29524,29525],"del",{},"war"," ist stupide: Suchen und Ersetzen. Für das automatisierte Code Refactoring wird\nein Code Schnipsel geschrieben welches das Suchen und Ersetzen für mich übernimmt! Ich darf Code hacken!",[439,29528,29529],{},[448,29530,29531],{},"Zuverlässigkeit",[439,29533,29534],{},"Ein generelles Argument zum Automatisieren trifft denke ich auch bei Code Refactoring zu. Es wird immer zu jeder Zeit an\njeder Stelle das selbe gemacht. Es gibt keinen Finger der auf der Tastatur um eine Taste verrutscht. Es gibt keine\nUnachtsamkeit die zum Vergessen einer Stelle führt. Die Maschine erledigt zuverlässig was getan werden soll. Immer.\nJederzeit. Überall.",[439,29536,29537],{},[448,29538,29539],{},"Effektivität",[439,29541,29542],{},"Ist etwas automatisiert gilt es nur noch ein Knöpfchen zu drücken. Bezogen auf das Code Refactoring kann das Ergebnis in\nwenigen (Milli-)Sekunden bestaunt werden. Hier spreche ich aber noch nicht konkret von codemods und jscodeshift als\nWerkzeug. Eine Regex kann hier auch schon völlig ausreichen.",[439,29544,29545],{},"Eine Regex zum",[994,29547,29548,29551],{},[997,29549,29550],{},"löschen von Abschnitten wenn Bedingung A zutrifft",[997,29552,29553],{},"verschieben von Code Blöcken",[439,29555,29556],{},"kann entweder einmal geschrieben und nie wieder verstanden werden, oder ist gar unmöglich zu schreiben. Hier kommt\njscodeshift mit codemods zur Rettung.",[3938,29558,29560],{"id":29559},"codemods-zum-upgrade-von-jasmine-1x-auf-2x","Codemods zum Upgrade von Jasmine 1.x auf 2.x",[439,29562,29563],{},"Eine codemod ist ein Codeschnipsel welches vorhandenen Quellcode transformiert. Im unserem Fall der Jasmine Migrierung\nvon Version 1.x zu 2.x müssen transformiert werden:",[994,29565,29566,29569,29572,29575,29578],{},[997,29567,29568],{},"spies",[997,29570,29571],{},"asynchrone Tests",[997,29573,29574],{},"expectations",[997,29576,29577],{},"custom matchers (falls vorhanden)",[997,29579,29580],{},"clock",[439,29582,29583],{},"Wir wenden uns folgend den asynchronen Tests zu.",[1065,29585,29587],{"id":29586},"asynchrone-tests-transformieren","Asynchrone Tests transformieren",[439,29589,29590],{},"Das Projekt das es zu refactored galt hatte überwiegend sehr einfach geschriebene asynchrone Tests. Perfekt für den\nEinstieg in jscodeshift.",[439,29592,29593,29594,29596,29597,29599],{},"Eine Variable die initial ",[471,29595,4768],{}," ist und nach $Aktion auf ",[471,29598,829],{}," gesetzt wird. Die Assertions werden dann erst\nausgeführt, wenn die Variable gesetzt wurde.",[464,29601,29605],{"className":29602,"code":29603,"language":29604,"meta":469,"style":469},"language-js shiki shiki-themes github-light github-dark","it(\"tests something async\", function () {\n var done;\n doSomethingAsync(function callback() {\n // assertions\n done = true;\n });\n waitsFor(function () {\n return done;\n });\n});\n","js",[471,29606,29607,29623,29631,29645,29650,29661,29666,29677,29683,29687],{"__ignoreMap":469},[474,29608,29609,29611,29613,29616,29618,29620],{"class":476,"line":477},[474,29610,8469],{"class":480},[474,29612,1483],{"class":503},[474,29614,29615],{"class":484},"\"tests something async\"",[474,29617,520],{"class":503},[474,29619,14021],{"class":810},[474,29621,29622],{"class":503}," () {\n",[474,29624,29625,29628],{"class":476,"line":507},[474,29626,29627],{"class":810}," var",[474,29629,29630],{"class":503}," done;\n",[474,29632,29633,29636,29638,29640,29643],{"class":476,"line":547},[474,29634,29635],{"class":480}," doSomethingAsync",[474,29637,1483],{"class":503},[474,29639,14021],{"class":810},[474,29641,29642],{"class":480}," callback",[474,29644,15227],{"class":503},[474,29646,29647],{"class":476,"line":584},[474,29648,29649],{"class":2277}," // assertions\n",[474,29651,29652,29655,29657,29659],{"class":476,"line":607},[474,29653,29654],{"class":503}," done ",[474,29656,811],{"class":810},[474,29658,14224],{"class":510},[474,29660,2139],{"class":503},[474,29662,29663],{"class":476,"line":642},[474,29664,29665],{"class":503}," });\n",[474,29667,29668,29671,29673,29675],{"class":476,"line":663},[474,29669,29670],{"class":480}," waitsFor",[474,29672,1483],{"class":503},[474,29674,14021],{"class":810},[474,29676,29622],{"class":503},[474,29678,29679,29681],{"class":476,"line":694},[474,29680,23104],{"class":810},[474,29682,29630],{"class":503},[474,29684,29685],{"class":476,"line":700},[474,29686,29665],{"class":503},[474,29688,29689],{"class":476,"line":913},[474,29690,15357],{"class":503},[439,29692,29693],{},"Für jasmine 2.x müssen wir also",[994,29695,29696,29705,29715],{},[997,29697,29698,29699,29701,29702,29704],{},"einmal der Funktion die dem ",[471,29700,8469],{}," übergeben wird einen Parameter ",[471,29703,29454],{}," hinzufügen",[997,29706,29707,29710,29711,29714],{},[471,29708,29709],{},"done = true;"," mit ",[471,29712,29713],{},"done();"," ersetzen",[997,29716,29717,29718,29720],{},"und den ",[471,29719,29450],{}," Block löschen",[439,29722,29723],{},"Nach dem Refactoring soll das Ganze also wie folgt aussehen:",[464,29725,29727],{"className":29602,"code":29726,"language":29604,"meta":469,"style":469},"it(\"tests something async\", function (done) {\n doSomethingAsync(function callback() {\n // assertions\n done();\n });\n});\n",[471,29728,29729,29747,29759,29763,29770,29774],{"__ignoreMap":469},[474,29730,29731,29733,29735,29737,29739,29741,29743,29745],{"class":476,"line":477},[474,29732,8469],{"class":480},[474,29734,1483],{"class":503},[474,29736,29615],{"class":484},[474,29738,520],{"class":503},[474,29740,14021],{"class":810},[474,29742,15250],{"class":503},[474,29744,29454],{"class":14037},[474,29746,23418],{"class":503},[474,29748,29749,29751,29753,29755,29757],{"class":476,"line":507},[474,29750,29635],{"class":480},[474,29752,1483],{"class":503},[474,29754,14021],{"class":810},[474,29756,29642],{"class":480},[474,29758,15227],{"class":503},[474,29760,29761],{"class":476,"line":547},[474,29762,29649],{"class":2277},[474,29764,29765,29768],{"class":476,"line":584},[474,29766,29767],{"class":480}," done",[474,29769,15271],{"class":503},[474,29771,29772],{"class":476,"line":607},[474,29773,29665],{"class":503},[474,29775,29776],{"class":476,"line":642},[474,29777,15357],{"class":503},[1633,29779,29460],{"id":29460},[439,29781,29782],{},"Bevor wir loslegen können, müssen noch wenige Dinge erledigt werden.",[464,29784,29786],{"className":16895,"code":29785,"language":16897,"meta":469,"style":469},"\n$> npm install -g jscodeshift\n$> mkdir jasmineCodemods && cd jasmineCodemods\n$> git init && git commit -m \"initial commit\" --allow-empty\n$> touch jasmine-async.js\n\n",[471,29787,29788,29792,29797,29802,29807],{"__ignoreMap":469},[474,29789,29790],{"class":476,"line":477},[474,29791,917],{"emptyLinePlaceholder":916},[474,29793,29794],{"class":476,"line":507},[474,29795,29796],{},"$> npm install -g jscodeshift\n",[474,29798,29799],{"class":476,"line":547},[474,29800,29801],{},"$> mkdir jasmineCodemods && cd jasmineCodemods\n",[474,29803,29804],{"class":476,"line":584},[474,29805,29806],{},"$> git init && git commit -m \"initial commit\" --allow-empty\n",[474,29808,29809],{"class":476,"line":607},[474,29810,29811],{},"$> touch jasmine-async.js\n",[439,29813,29814],{},"Der Einfachkeit halber installieren wir jscodeshift global um das binary auf der Konsole ausführen zu können. Und das\nGit Repo zum einfachen hacken, sichern und zurückrollen darf auch nicht fehlen!",[439,29816,29817],{},"Dann legen wir uns eine Datei für der/die/das erste codemod an und schreiben folgenden Inhalt:",[464,29819,29821],{"className":29602,"code":29820,"language":29604,"meta":469,"style":469},"// jasmine-async.js\nmodule.exports = function transformer(file, api) {\n const j = api.jscodeshift;\n const { statement } = j.template;\n const root = j(file.source);\n return root;\n};\n",[471,29822,29823,29828,29858,29871,29889,29903,29910],{"__ignoreMap":469},[474,29824,29825],{"class":476,"line":477},[474,29826,29827],{"class":2277},"// jasmine-async.js\n",[474,29829,29830,29833,29835,29838,29840,29843,29846,29848,29851,29853,29856],{"class":476,"line":507},[474,29831,29832],{"class":510},"module",[474,29834,1402],{"class":503},[474,29836,29837],{"class":510},"exports",[474,29839,1661],{"class":810},[474,29841,29842],{"class":810}," function",[474,29844,29845],{"class":480}," transformer",[474,29847,1483],{"class":503},[474,29849,29850],{"class":14037},"file",[474,29852,520],{"class":503},[474,29854,29855],{"class":14037},"api",[474,29857,23418],{"class":503},[474,29859,29860,29863,29866,29868],{"class":476,"line":547},[474,29861,29862],{"class":810}," const",[474,29864,29865],{"class":510}," j",[474,29867,1661],{"class":810},[474,29869,29870],{"class":503}," api.jscodeshift;\n",[474,29872,29873,29875,29878,29881,29884,29886],{"class":476,"line":584},[474,29874,29862],{"class":810},[474,29876,29877],{"class":503}," { ",[474,29879,29880],{"class":510},"statement",[474,29882,29883],{"class":503}," } ",[474,29885,811],{"class":810},[474,29887,29888],{"class":503}," j.template;\n",[474,29890,29891,29893,29896,29898,29900],{"class":476,"line":607},[474,29892,29862],{"class":810},[474,29894,29895],{"class":510}," root",[474,29897,1661],{"class":810},[474,29899,29865],{"class":480},[474,29901,29902],{"class":503},"(file.source);\n",[474,29904,29905,29907],{"class":476,"line":642},[474,29906,23910],{"class":810},[474,29908,29909],{"class":503}," root;\n",[474,29911,29912],{"class":476,"line":663},[474,29913,29914],{"class":503},"};\n",[439,29916,29917,29918,29921,29922,520,29925,520,29928,520,29931,29934,29935,29938,29939,8351,29944,26462,29949,29953],{},"Auf ",[471,29919,29920],{},"root"," können wir jetzt jscodeshift Methoden aufrufen wie ",[471,29923,29924],{},"find",[471,29926,29927],{},"filter",[471,29929,29930],{},"forEach",[471,29932,29933],{},"replaceWith"," und zuletzt\n",[471,29936,29937],{},"toSource",". Die Methoden machen genau das was der Name sagt, selbsterklärend. Genaueres muss man sich leider selbst\nim ",[1002,29940,29943],{"href":29941,"rel":29942},"https://github.com/facebook/jscodeshift/blob/fe67b121d4c2519c5227a00be3f590e7f7c46d2b/src/Collection.js",[1006],"Source",[1002,29945,29948],{"href":29946,"rel":29947},"https://github.com/facebook/jscodeshift/tree/fe67b121d4c2519c5227a00be3f590e7f7c46d2b/src/collections",[1006],"Code",[1002,29950,27413],{"href":29951,"rel":29952},"https://github.com/facebook/jscodeshift/tree/fe67b121d4c2519c5227a00be3f590e7f7c46d2b",[1006]," zusammenkratzen.",[439,29955,29956],{},"Ausführen können wir das Skript später mit",[464,29958,29960],{"className":16895,"code":29959,"language":16897,"meta":469,"style":469},"\n$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei\n\n",[471,29961,29962,29966],{"__ignoreMap":469},[474,29963,29964],{"class":476,"line":477},[474,29965,917],{"emptyLinePlaceholder":916},[474,29967,29968],{"class":476,"line":507},[474,29969,29970],{},"$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei\n",[439,29972,29973],{},"Doch zuerst müssen Transformationen gecoded werden 😮",[439,29975,29976,29977,29979,29980,29982,29983,29986,29987,29989,29990,29992,29993,29998],{},"Wir wollen fürs erste alle ",[471,29978,8469],{}," Knoten finden und der übergebenen Funktion einen ",[471,29981,29454],{}," Parameter spendieren. Zum Suchen\nvon Ausdrücken verwenden wir die jscodeshift Methode ",[471,29984,29985],{},"root.find",". Diese traversiert den AST und gibt uns eine Collection\nvon passenden Knoten zurück. Als Argument müssen wird dem ",[471,29988,29924],{}," Aufruf eine AST Beschreibung des ",[471,29991,8469],{}," Knotens mitgeben.\nBeim Finden der Beschreibung hilft uns der geniale ",[1002,29994,29997],{"href":29995,"rel":29996},"https://astexplorer.net",[1006],"astexplorer.net",". Wir kopieren den Code den\nwir transformieren wollen in den Editor und bekommen den AST ausgespuckt. Wir können sogar auf jeden beliebigen Knoten\nim Editor klicken und bekommen im AST den enstsprechenden Teil markiert!",[439,30000,30001],{},[2205,30002],{"alt":30003,"src":30004},"astexplorer","https://media.synyx.de/uploads//2016/08/astexplorer-1024x631.png",[464,30006,30008],{"className":29602,"code":30007,"language":29604,"meta":469,"style":469},"// jasmine-async.js\nreturn root.find(j.CallExpression, {\n callee: {\n name: \"it\",\n },\n});\n",[471,30009,30010,30014,30027,30032,30042,30046],{"__ignoreMap":469},[474,30011,30012],{"class":476,"line":477},[474,30013,29827],{"class":2277},[474,30015,30016,30019,30022,30024],{"class":476,"line":507},[474,30017,30018],{"class":810},"return",[474,30020,30021],{"class":503}," root.",[474,30023,29924],{"class":480},[474,30025,30026],{"class":503},"(j.CallExpression, {\n",[474,30028,30029],{"class":476,"line":547},[474,30030,30031],{"class":503}," callee: {\n",[474,30033,30034,30037,30040],{"class":476,"line":584},[474,30035,30036],{"class":503}," name: ",[474,30038,30039],{"class":484},"\"it\"",[474,30041,4715],{"class":503},[474,30043,30044],{"class":476,"line":607},[474,30045,4783],{"class":503},[474,30047,30048],{"class":476,"line":642},[474,30049,15357],{"class":503},[439,30051,30052,30053,30055,30056,30058,30059,30061],{},"Dann wollen wir für alle Knoten die gefunden werden etwas tun. Nämlich den ",[471,30054,29454],{}," Parameter hinzufügen zur eigentlichen\nTestfunktion. Mit ",[471,30057,29930],{}," können wir über die von ",[471,30060,29924],{}," zurückgegebene Collection iterieren und dies tun.",[464,30063,30065],{"className":29602,"code":30064,"language":29604,"meta":469,"style":469},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(p => {\n // p.node.arguments[0] would be the spec description\n const specCallee = p.node.arguments[1];\n // add 'done' parameter\n specCallee.params.push(statment`done`);\n })\n\n",[471,30066,30067,30071,30075,30081,30094,30109,30114,30132,30137,30155],{"__ignoreMap":469},[474,30068,30069],{"class":476,"line":477},[474,30070,917],{"emptyLinePlaceholder":916},[474,30072,30073],{"class":476,"line":507},[474,30074,29827],{"class":2277},[474,30076,30077,30079],{"class":476,"line":547},[474,30078,30018],{"class":810},[474,30080,12806],{"class":503},[474,30082,30083,30086,30088,30090,30092],{"class":476,"line":584},[474,30084,30085],{"class":503}," .",[474,30087,29924],{"class":480},[474,30089,1483],{"class":503},[474,30091,24215],{"class":810},[474,30093,1101],{"class":503},[474,30095,30096,30098,30100,30102,30104,30107],{"class":476,"line":607},[474,30097,30085],{"class":503},[474,30099,29930],{"class":480},[474,30101,1483],{"class":503},[474,30103,439],{"class":14037},[474,30105,30106],{"class":810}," =>",[474,30108,14027],{"class":503},[474,30110,30111],{"class":476,"line":642},[474,30112,30113],{"class":2277}," // p.node.arguments[0] would be the spec description\n",[474,30115,30116,30119,30122,30124,30127,30129],{"class":476,"line":663},[474,30117,30118],{"class":810}," const",[474,30120,30121],{"class":510}," specCallee",[474,30123,1661],{"class":810},[474,30125,30126],{"class":503}," p.node.arguments[",[474,30128,5008],{"class":510},[474,30130,30131],{"class":503},"];\n",[474,30133,30134],{"class":476,"line":694},[474,30135,30136],{"class":2277}," // add 'done' parameter\n",[474,30138,30139,30142,30145,30147,30150,30153],{"class":476,"line":700},[474,30140,30141],{"class":503}," specCallee.params.",[474,30143,30144],{"class":480},"push",[474,30146,1483],{"class":503},[474,30148,30149],{"class":480},"statment",[474,30151,30152],{"class":484},"`done`",[474,30154,1495],{"class":503},[474,30156,30157],{"class":476,"line":913},[474,30158,30159],{"class":503}," })\n",[439,30161,30162,30163,30165,30166,30168,30169,30172,30173,30175,30176,30178],{},"Die Variable ",[471,30164,439],{}," ist der Pfad des gefundenen Knotens. Man könnte die Variable auch ",[471,30167,6853],{}," benennen, würde sich aber\nbeißen mit dem node Modul ",[471,30170,30171],{},"const path = require('path');",". Das importieren dieses Moduls ist keine Seltenheit in\ncodemods denke ich. Und als Konvention nehmen wir einfach ",[471,30174,439],{}," statt ",[471,30177,6853],{},", immer!",[439,30180,30181,30182,30184,30185,30187,30188,30190],{},"Der ASTExplorer zeigt wie wir an die Funktion kommen der wir den ",[471,30183,29454],{}," Parameter hinzufügen möchten. Wir holen uns das\nzweite Element der CallExpression Argumente und fügen dessen Parameter Liste einfach das ",[471,30186,29454],{}," hinzu. Leider (?) können\nwir aber keinen String übergeben. Wir erinnern uns an den AST. Wir brauchen eine Beschreibung des Knotens. Man könnte\njetzt entweder ein komplexes Objekt erstellen, oder man nimmt sich einfach die nützliche ",[471,30189,29880],{}," Funktion zu Hilfe.\nAuf die Funktion machte mich ein Kollege aufmerksam. Sie ist leider nicht in der Doku zu finden sondern nur in codemods\nauf Github… Sagte ich schon, dass die Doku etwas spärlich ist?",[439,30192,30193,30194,30197],{},"Zum Abschluss müssen wir die Änderungen mit ",[471,30195,30196],{},"toSource()"," an jscodeshift zurückgeben um die Datei neu zu schreiben.",[464,30199,30201],{"className":29602,"code":30200,"language":29604,"meta":469,"style":469},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(...)\n .toSource()\n\n",[471,30202,30203,30207,30211,30217,30229,30241],{"__ignoreMap":469},[474,30204,30205],{"class":476,"line":477},[474,30206,917],{"emptyLinePlaceholder":916},[474,30208,30209],{"class":476,"line":507},[474,30210,29827],{"class":2277},[474,30212,30213,30215],{"class":476,"line":547},[474,30214,30018],{"class":810},[474,30216,12806],{"class":503},[474,30218,30219,30221,30223,30225,30227],{"class":476,"line":584},[474,30220,30085],{"class":503},[474,30222,29924],{"class":480},[474,30224,1483],{"class":503},[474,30226,24215],{"class":810},[474,30228,1101],{"class":503},[474,30230,30231,30233,30235,30237,30239],{"class":476,"line":607},[474,30232,30085],{"class":503},[474,30234,29930],{"class":480},[474,30236,1483],{"class":503},[474,30238,24215],{"class":810},[474,30240,1101],{"class":503},[474,30242,30243,30245,30247],{"class":476,"line":642},[474,30244,30085],{"class":503},[474,30246,29937],{"class":480},[474,30248,30249],{"class":503},"()\n",[439,30251,30252],{},"Zum schnellen Testen kann die Transformation auf der Konsole mit",[464,30254,30256],{"className":16895,"code":30255,"language":16897,"meta":469,"style":469},"\n$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei.js\n\n",[471,30257,30258,30262],{"__ignoreMap":469},[474,30259,30260],{"class":476,"line":477},[474,30261,917],{"emptyLinePlaceholder":916},[474,30263,30264],{"class":476,"line":507},[474,30265,30266],{},"$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei.js\n",[439,30268,30269],{},"ausgeführt werden. Nach dem ersten Staunen aber mit",[464,30271,30273],{"className":16895,"code":30272,"language":16897,"meta":469,"style":469},"\n$> git checkout HEAD -- pfad/zur/source/datei.js\n\n",[471,30274,30275,30279],{"__ignoreMap":469},[474,30276,30277],{"class":476,"line":477},[474,30278,917],{"emptyLinePlaceholder":916},[474,30280,30281],{"class":476,"line":507},[474,30282,30283],{},"$> git checkout HEAD -- pfad/zur/source/datei.js\n",[439,30285,30286],{},"zurück gesetzt werden.",[439,30288,30289],{},"Git \\o/",[439,30291,30292],{},"Der erste Punkt ist erledigt.",[994,30294,30295,30303,30309],{},[997,30296,30297],{},[29523,30298,29698,30299,29701,30301,29704],{},[471,30300,8469],{},[471,30302,29454],{},[997,30304,30305,29710,30307,29714],{},[471,30306,29709],{},[471,30308,29713],{},[997,30310,29717,30311,29720],{},[471,30312,29450],{},[439,30314,30315,30316,30318,30319,30322],{},"Ersetzen wir als nächstes ",[471,30317,29709],{}," mit dem ",[471,30320,30321],{},"done()"," Aufruf. Dazu klicken wir im ASTExplorer auf den entsprechenden\nAusdruck und schauen rechts im AST nach der Pfad Beschreibung die wir brauchen.",[464,30324,30326],{"className":29602,"code":30325,"language":29604,"meta":469,"style":469},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(p => {\n // ...\n // replace 'done = true' with done() invocation\n j(p).find(j.ExpressionStatement, {\n expression: {\n type: j.AssignmentExpression.name,\n left: {\n name: 'done'\n }\n }\n }).replaceWith(p => statement`done();`);\n })\n .toSource()\n\n",[471,30327,30328,30332,30336,30342,30354,30368,30373,30378,30391,30396,30401,30406,30414,30418,30423,30444,30448],{"__ignoreMap":469},[474,30329,30330],{"class":476,"line":477},[474,30331,917],{"emptyLinePlaceholder":916},[474,30333,30334],{"class":476,"line":507},[474,30335,29827],{"class":2277},[474,30337,30338,30340],{"class":476,"line":547},[474,30339,30018],{"class":810},[474,30341,12806],{"class":503},[474,30343,30344,30346,30348,30350,30352],{"class":476,"line":584},[474,30345,30085],{"class":503},[474,30347,29924],{"class":480},[474,30349,1483],{"class":503},[474,30351,24215],{"class":810},[474,30353,1101],{"class":503},[474,30355,30356,30358,30360,30362,30364,30366],{"class":476,"line":607},[474,30357,30085],{"class":503},[474,30359,29930],{"class":480},[474,30361,1483],{"class":503},[474,30363,439],{"class":14037},[474,30365,30106],{"class":810},[474,30367,14027],{"class":503},[474,30369,30370],{"class":476,"line":642},[474,30371,30372],{"class":2277}," // ...\n",[474,30374,30375],{"class":476,"line":663},[474,30376,30377],{"class":2277}," // replace 'done = true' with done() invocation\n",[474,30379,30380,30383,30386,30388],{"class":476,"line":694},[474,30381,30382],{"class":480}," j",[474,30384,30385],{"class":503},"(p).",[474,30387,29924],{"class":480},[474,30389,30390],{"class":503},"(j.ExpressionStatement, {\n",[474,30392,30393],{"class":476,"line":700},[474,30394,30395],{"class":503}," expression: {\n",[474,30397,30398],{"class":476,"line":913},[474,30399,30400],{"class":503}," type: j.AssignmentExpression.name,\n",[474,30402,30403],{"class":476,"line":920},[474,30404,30405],{"class":503}," left: {\n",[474,30407,30408,30411],{"class":476,"line":926},[474,30409,30410],{"class":503}," name: ",[474,30412,30413],{"class":484},"'done'\n",[474,30415,30416],{"class":476,"line":932},[474,30417,5704],{"class":503},[474,30419,30420],{"class":476,"line":938},[474,30421,30422],{"class":503}," }\n",[474,30424,30425,30428,30430,30432,30434,30436,30439,30442],{"class":476,"line":944},[474,30426,30427],{"class":503}," }).",[474,30429,29933],{"class":480},[474,30431,1483],{"class":503},[474,30433,439],{"class":14037},[474,30435,30106],{"class":810},[474,30437,30438],{"class":480}," statement",[474,30440,30441],{"class":484},"`done();`",[474,30443,1495],{"class":503},[474,30445,30446],{"class":476,"line":950},[474,30447,30159],{"class":503},[474,30449,30450,30452,30454],{"class":476,"line":956},[474,30451,30085],{"class":503},[474,30453,29937],{"class":480},[474,30455,30249],{"class":503},[439,30457,30458,30459,30461,30462,1402],{},"Da wir wissen, dass ",[471,30460,29709],{}," nur einmal vorkommt, können wir der Collection direkt sagen bitte ersetzen mit dem\nStatement ",[471,30463,29713],{},[439,30465,13082,30466,30468],{},[471,30467,29454],{}," Variable ist jetzt natürlich obsolet und kann komplett entfernt werden. Wieder schauen wir im ASTExplorer\nnach der Beschreibung die wir brauchen um auf folgendes zu kommen:",[464,30470,30472],{"className":29602,"code":30471,"language":29604,"meta":469,"style":469},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(p => {\n // ...\n // get rid of 'var done = false'\n j(p).find(j.VariableDeclaration, {\n declarations: [\n {\n type: j.VariableDeclarator.name,\n id: {\n name: 'done'\n }\n }\n ]\n }).remove()\n })\n .toSource()\n\n",[471,30473,30474,30478,30482,30488,30500,30514,30518,30523,30534,30539,30543,30548,30553,30560,30565,30569,30573,30582,30586],{"__ignoreMap":469},[474,30475,30476],{"class":476,"line":477},[474,30477,917],{"emptyLinePlaceholder":916},[474,30479,30480],{"class":476,"line":507},[474,30481,29827],{"class":2277},[474,30483,30484,30486],{"class":476,"line":547},[474,30485,30018],{"class":810},[474,30487,12806],{"class":503},[474,30489,30490,30492,30494,30496,30498],{"class":476,"line":584},[474,30491,30085],{"class":503},[474,30493,29924],{"class":480},[474,30495,1483],{"class":503},[474,30497,24215],{"class":810},[474,30499,1101],{"class":503},[474,30501,30502,30504,30506,30508,30510,30512],{"class":476,"line":607},[474,30503,30085],{"class":503},[474,30505,29930],{"class":480},[474,30507,1483],{"class":503},[474,30509,439],{"class":14037},[474,30511,30106],{"class":810},[474,30513,14027],{"class":503},[474,30515,30516],{"class":476,"line":642},[474,30517,30372],{"class":2277},[474,30519,30520],{"class":476,"line":663},[474,30521,30522],{"class":2277}," // get rid of 'var done = false'\n",[474,30524,30525,30527,30529,30531],{"class":476,"line":694},[474,30526,30382],{"class":480},[474,30528,30385],{"class":503},[474,30530,29924],{"class":480},[474,30532,30533],{"class":503},"(j.VariableDeclaration, {\n",[474,30535,30536],{"class":476,"line":700},[474,30537,30538],{"class":503}," declarations: [\n",[474,30540,30541],{"class":476,"line":913},[474,30542,4820],{"class":503},[474,30544,30545],{"class":476,"line":920},[474,30546,30547],{"class":503}," type: j.VariableDeclarator.name,\n",[474,30549,30550],{"class":476,"line":926},[474,30551,30552],{"class":503}," id: {\n",[474,30554,30555,30558],{"class":476,"line":932},[474,30556,30557],{"class":503}," name: ",[474,30559,30413],{"class":484},[474,30561,30562],{"class":476,"line":938},[474,30563,30564],{"class":503}," }\n",[474,30566,30567],{"class":476,"line":944},[474,30568,5704],{"class":503},[474,30570,30571],{"class":476,"line":950},[474,30572,5710],{"class":503},[474,30574,30575,30577,30580],{"class":476,"line":956},[474,30576,30427],{"class":503},[474,30578,30579],{"class":480},"remove",[474,30581,30249],{"class":503},[474,30583,30584],{"class":476,"line":962},[474,30585,30159],{"class":503},[474,30587,30588,30590,30592],{"class":476,"line":4876},[474,30589,30085],{"class":503},[474,30591,29937],{"class":480},[474,30593,30249],{"class":503},[439,30595,30596],{},"Zweiter Punkt auch erledigt.",[994,30598,30599,30607,30615],{},[997,30600,30601],{},[29523,30602,29698,30603,29701,30605,29704],{},[471,30604,8469],{},[471,30606,29454],{},[997,30608,30609],{},[29523,30610,30611,29710,30613,29714],{},[471,30612,29709],{},[471,30614,29713],{},[997,30616,29717,30617,29720],{},[471,30618,29450],{},[439,30620,30621,30622,30624],{},"Fehlt nur noch das Entfernen des ",[471,30623,29450],{}," Blocks. Richtig geraten! Der ASTExplorer zeigt uns was wir suchen müssen.",[464,30626,30628],{"className":29602,"code":30627,"language":29604,"meta":469,"style":469},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(p => {\n // ...\n // get rid of obsolete waitsFor block\n j(p).find(j.CallExpression, {\n callee: {\n name: 'waitsFor'\n }\n }).remove()\n })\n .toSource()\n\n",[471,30629,30630,30634,30638,30644,30656,30670,30674,30679,30689,30694,30702,30706,30714,30718],{"__ignoreMap":469},[474,30631,30632],{"class":476,"line":477},[474,30633,917],{"emptyLinePlaceholder":916},[474,30635,30636],{"class":476,"line":507},[474,30637,29827],{"class":2277},[474,30639,30640,30642],{"class":476,"line":547},[474,30641,30018],{"class":810},[474,30643,12806],{"class":503},[474,30645,30646,30648,30650,30652,30654],{"class":476,"line":584},[474,30647,30085],{"class":503},[474,30649,29924],{"class":480},[474,30651,1483],{"class":503},[474,30653,24215],{"class":810},[474,30655,1101],{"class":503},[474,30657,30658,30660,30662,30664,30666,30668],{"class":476,"line":607},[474,30659,30085],{"class":503},[474,30661,29930],{"class":480},[474,30663,1483],{"class":503},[474,30665,439],{"class":14037},[474,30667,30106],{"class":810},[474,30669,14027],{"class":503},[474,30671,30672],{"class":476,"line":642},[474,30673,30372],{"class":2277},[474,30675,30676],{"class":476,"line":663},[474,30677,30678],{"class":2277}," // get rid of obsolete waitsFor block\n",[474,30680,30681,30683,30685,30687],{"class":476,"line":694},[474,30682,30382],{"class":480},[474,30684,30385],{"class":503},[474,30686,29924],{"class":480},[474,30688,30026],{"class":503},[474,30690,30691],{"class":476,"line":700},[474,30692,30693],{"class":503}," callee: {\n",[474,30695,30696,30699],{"class":476,"line":913},[474,30697,30698],{"class":503}," name: ",[474,30700,30701],{"class":484},"'waitsFor'\n",[474,30703,30704],{"class":476,"line":920},[474,30705,30422],{"class":503},[474,30707,30708,30710,30712],{"class":476,"line":926},[474,30709,30427],{"class":503},[474,30711,30579],{"class":480},[474,30713,30249],{"class":503},[474,30715,30716],{"class":476,"line":932},[474,30717,30159],{"class":503},[474,30719,30720,30722,30724],{"class":476,"line":938},[474,30721,30085],{"class":503},[474,30723,29937],{"class":480},[474,30725,30249],{"class":503},[439,30727,30728],{},"Fertig!",[439,30730,30731],{},"Schnell noch testen obs auch wirklich tut:",[464,30733,30734],{"className":16895,"code":30255,"language":16897,"meta":469,"style":469},[471,30735,30736,30740],{"__ignoreMap":469},[474,30737,30738],{"class":476,"line":477},[474,30739,917],{"emptyLinePlaceholder":916},[474,30741,30742],{"class":476,"line":507},[474,30743,30266],{},[439,30745,30746],{},"Und ab damit ins Repo",[464,30748,30750],{"className":16895,"code":30749,"language":16897,"meta":469,"style":469},"\n$> git commit -am \"jasmine async test upgrade from 1.x to 2.x; automated 🎉\"\n\n",[471,30751,30752,30756],{"__ignoreMap":469},[474,30753,30754],{"class":476,"line":477},[474,30755,917],{"emptyLinePlaceholder":916},[474,30757,30758],{"class":476,"line":507},[474,30759,30760],{},"$> git commit -am \"jasmine async test upgrade from 1.x to 2.x; automated 🎉\"\n",[439,30762,30763],{},"Mit dieser codemod können wir ab sofort mit einem Befehl zig JavaScript Dateien transformieren lassen \\o/ Wobei ich\ntrotzdem ein Code Review empfehlen würde. Und nicht blind auf den Master pushen.",[3938,30765,3783],{"id":3782},[439,30767,30768,30769,30771,30772,30774],{},"Zugegeben. Ich habe hier ein wirklich einfaches Beispiel gewählt. Aber trotzdem hat das viele Tests unseres Projektes\nabgedeckt. Ich habe noch ein paar Bedingungen hinzugefügt zur codemod wie z. B. bitte abbrechen, wenn Anzahl der\n",[471,30770,29450],{}," Blöcke != 1. Das ignoriert dann alles specs die synchron sind und asynchrone Tests die ein anderes Muster\naufweisen. Bei uns waren das z. B. Integrations Tests die mehrere ",[471,30773,29450],{}," Blöcke hatten weil mehrere Klicks simuliert\nwurden.",[439,30776,30777],{},"Es fehlen auch noch die restlichen jasmine Transformationen für Matchers und Spies. Aber ich denke es sollte recht klar\ngeworden sein wie auch dafür codemods geschrieben werden können.",[439,30779,30780,30781,30786],{},"Werden codemods komplexer kann man über Unit Tests nachdenken. Hier gibt es Hilfe von jscodeshift. Als Beispiel\nhilft ",[1002,30782,30785],{"href":30783,"rel":30784},"https://github.com/cpojer/js-codemod/blob/82af3089f22fa0687159f64177b73908b82d074f/transforms/__tests__/arrow-function-test.js",[1006],"diese Stelle hier",".\nKurzer Ablauf: Als TestRunner wird jest benötigt. Der Quellcode der transformiert und der Quellcode der herausspringen\nsoll, werden jeweils als Datei im Verzeichnis ‘__testfixtures__’ abgelegt. Der Test liegt im Verzeichnis\n‘__tests__’ und definiert mittels den TestUtils einfach nur den Test. Codemods testgetrieben entwickeln steht nichts\nmehr im Weg.",[464,30788,30790],{"className":29602,"code":30789,"language":29604,"meta":469,"style":469},"// jasmine-async.spec.js\nconst defineTest = require(\"jscodeshift/dist/testUtils\").defineTest;\ndefineTest(__dirname, \"jasmine-async\");\n",[471,30791,30792,30797,30818],{"__ignoreMap":469},[474,30793,30794],{"class":476,"line":477},[474,30795,30796],{"class":2277},"// jasmine-async.spec.js\n",[474,30798,30799,30802,30805,30807,30810,30812,30815],{"class":476,"line":507},[474,30800,30801],{"class":810},"const",[474,30803,30804],{"class":510}," defineTest",[474,30806,1661],{"class":810},[474,30808,30809],{"class":480}," require",[474,30811,1483],{"class":503},[474,30813,30814],{"class":484},"\"jscodeshift/dist/testUtils\"",[474,30816,30817],{"class":503},").defineTest;\n",[474,30819,30820,30823,30826,30829],{"class":476,"line":547},[474,30821,30822],{"class":480},"defineTest",[474,30824,30825],{"class":503},"(__dirname, ",[474,30827,30828],{"class":484},"\"jasmine-async\"",[474,30830,1495],{"class":503},[3938,30832,1385],{"id":1384},[439,30834,30835],{},"Trotz spärlicher Doku findet man sich nach gewisser Zeit zurecht. Vor allem der ASTExplorer ist eine große Hilfe dabei!\nFür der/die/das erste codemod habe ich vielleicht 3x länger gebraucht, als im Projekt alles per Hand zu ersetzen. Aber\nkennt man das Vorgehen mit jscodeshift gleicht sich das schnell wieder aus. Und der/die/das codemod kann wiederverwendet\nwerden! Wenn auch mit kleinen Anpassungen für andere Gegebenheiten.",[439,30837,30838,30839,20628,30844,30849],{},"Oft verwendet habe ich bisher die ",[1002,30840,30843],{"href":30841,"rel":30842},"https://github.com/cpojer/js-codemod",[1006],"js-codemods",[1002,30845,30848],{"href":30846,"rel":30847},"https://twitter.com/cpojer",[1006],"@cpojer"," zum transformieren zu neuen es2015 Sprachfeatures.",[439,30851,30852],{},[448,30853,30854],{},"Meine Empfehlung:",[439,30856,30857],{},"Einmal reinknien und machen! Vielleicht sogar für kleinere Projekte.",[1024,30859,30860],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":469,"searchDepth":507,"depth":507,"links":30862},[30863,30864,30865,30868,30869],{"id":29470,"depth":507,"text":29471},{"id":29509,"depth":507,"text":29510},{"id":29559,"depth":507,"text":29560,"children":30866},[30867],{"id":29586,"depth":547,"text":29587},{"id":3782,"depth":507,"text":3783},{"id":1384,"depth":507,"text":1385},[1030],"2016-08-25T09:56:03","Vor kurzem hatte ich die Muße ein älteres JavaScript Projekt zu refactoren. Unter anderem sollte die Assertion\\nBibliothek Jasmine von 1.x auf 2.x aktualisiert werden. Zwei Dinge gab es bei unseren Tests zu refactoren. Einmal die\\nArt von asynchronen Specs und einmal die verwendeten Expectations.\\nUnter http://jasmine.github.io/2.0/upgrading.html wurde super\\nbeschrieben was für Änderungen man genau machen muss beim Umstieg von Jasmine 1.x auf 2.x.","https://synyx.de/blog/javascript-code-refactoring-automatisieren/",{},"/blog/javascript-code-refactoring-automatisieren",{"title":29427,"description":30877},"Vor kurzem hatte ich die Muße ein älteres JavaScript Projekt zu refactoren. Unter anderem sollte die Assertion\nBibliothek Jasmine von 1.x auf 2.x aktualisiert werden. Zwei Dinge gab es bei unseren Tests zu refactoren. Einmal die\nArt von asynchronen Specs und einmal die verwendeten Expectations.\nUnter http://jasmine.github.io/2.0/upgrading.html wurde super\nbeschrieben was für Änderungen man genau machen muss beim Umstieg von Jasmine 1.x auf 2.x.","blog/javascript-code-refactoring-automatisieren",[3182,15201,30880],"refactoring","Vor kurzem hatte ich die Muße ein älteres JavaScript Projekt zu refactoren. Unter anderem sollte die Assertion Bibliothek Jasmine von 1.x auf 2.x aktualisiert werden. Zwei Dinge gab es bei…","4CqNp3PtcESfHYCfeps_RN_QH0rmwyZo4h2jfvcPktk",{"id":30884,"title":30885,"author":30886,"body":30887,"category":31616,"date":31617,"description":31618,"extension":1034,"link":31619,"meta":31620,"navigation":916,"path":31621,"seo":31622,"slug":30891,"stem":31623,"tags":31624,"teaser":31629,"__hash__":31630},"blog/blog/android-building-apks-for-different-environments-using-build-types-and-product-flavors.md","Android: Building APKs for different environments using build types and product flavors",[190],{"type":432,"value":30888,"toc":31606},[30889,30892,30895,30898,30901,30915,30918,30953,30956,30960,30963,30966,30969,30972,30989,30993,30996,30999,31002,31005,31008,31011,31144,31147,31156,31160,31163,31166,31169,31173,31176,31212,31215,31218,31221,31300,31303,31306,31314,31317,31325,31329,31332,31356,31359,31401,31404,31407,31413,31447,31452,31494,31499,31557,31560,31566,31574,31576,31587,31590,31593,31601,31604],[435,30890,30885],{"id":30891},"android-building-apks-for-different-environments-using-build-types-and-product-flavors",[439,30893,30894],{},"Different build types in android can be used to build the same application with different configurations. This can be\npredefined config values like ‘debuggable’, but you can also define your own config values that will be accessible in\nyour application. This post will show you some ways in which you can use this functionality to easily build your app for\ndifferent environments of remote services and for better local development.",[3938,30896,30897],{"id":28668},"Build types:",[439,30899,30900],{},"Each android project comes with some default build types:",[994,30902,30903,30906,30909,30912],{},[997,30904,30905],{},"debug -> for development and debugging on devices",[997,30907,30908],{},"test -> for running unit tests",[997,30910,30911],{},"androidTest -> for running device tests",[997,30913,30914],{},"release -> for the actual release version of the app",[439,30916,30917],{},"A freshly created build.gradle, has the following build types defined:",[464,30919,30921],{"className":28456,"code":30920,"language":28458,"meta":469,"style":469},"\n buildTypes {\n release {\n minifyEnabled false\n proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n }\n }\n\n",[471,30922,30923,30927,30931,30935,30940,30945,30949],{"__ignoreMap":469},[474,30924,30925],{"class":476,"line":477},[474,30926,917],{"emptyLinePlaceholder":916},[474,30928,30929],{"class":476,"line":507},[474,30930,28465],{},[474,30932,30933],{"class":476,"line":547},[474,30934,28543],{},[474,30936,30937],{"class":476,"line":584},[474,30938,30939],{}," minifyEnabled false\n",[474,30941,30942],{"class":476,"line":607},[474,30943,30944],{}," proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n",[474,30946,30947],{"class":476,"line":642},[474,30948,5704],{},[474,30950,30951],{"class":476,"line":663},[474,30952,1276],{},[439,30954,30955],{},"The default build types don’t need to be declared, but you can declare them in order to extend them with custom config.",[3938,30957,30959],{"id":30958},"different-environments","Different environments",[439,30961,30962],{},"Let’s say we are developing a web service and want users to interact with this service via our android app. We have\nmultiple environments for this service to be able to test it before deploying to production. Namely, we have a test\nsystem, a staging system, and a production system. We can also start this service on our local machine, to test the\nlocal changes we made on the service or the app.",[439,30964,30965],{},"Furthermore, we don’t want to be dependent on a remotely running system or on a locally running service, if we are only\nimproving the android app on parts that don’t interact with the web service.",[439,30967,30968],{},"In order to let some people test the application before releasing it, we want to be able to distribute versions for the\ntest and stage systems as a beta app via google play.",[439,30970,30971],{},"The above requirements result in the following build types:",[994,30973,30974,30977,30980,30983,30986],{},[997,30975,30976],{},"test system",[997,30978,30979],{},"stage system",[997,30981,30982],{},"production sytem -> we can just use the default release build type",[997,30984,30985],{},"local service",[997,30987,30988],{},"no service at all -> mocked services",[3938,30990,30992],{"id":30991},"different-urls-based-on-the-build-type","Different URLs based on the build type",[439,30994,30995],{},"The first three are the most trivial to achieve. The build types allow us to define custom fields (buildConfigField) for\nour application, which we can use to have a differnt URL for each of the environments. A buildConfigField receives 3\nparameters: the type of the field, the name of the field, and the value of the field. This field will then be put into a\nBuildConfig java class that is generated on build time.",[439,30997,30998],{},"Note, that the exact value of the buildConfigField is copied into the generated java class, so if we define Strings, we\nhave to put the double quotes around the String for it to be correct. So the values of the String buildConfigFields in\nthe build.gradle are wrapped by single quotes, as well as double quotes – single quotes for the gradle file and double\nquotes for the resulting java class.",[439,31000,31001],{},"The local service can be configured in a very similar way, we just can’t use a static URL or IP, since we want it to\npoint to the host of the developer that built the app, and not just a single host. Luckily, we can use some code in the\ngradle file 🙂 If we use ‘InetAddress.getLocalHost().getCanonicalHostName()’ in the build, we get the host that ran the\nbuild.",[439,31003,31004],{},"To be sure which app is currently running, we also put an ‘environment’ field to each build type, which can be appended\nto the title of the app (except for the release build) to avoid mistakenly testing on the wrong environment.",[439,31006,31007],{},"The builds for test, stage and production should be all built with the release certificate, since we want to upload them\nto the playstore.",[439,31009,31010],{},"With these additions applied, our builtTypes look like this:",[464,31012,31014],{"className":28456,"code":31013,"language":28458,"meta":469,"style":469},"\n buildTypes {\n local{\n debuggable true\n signingConfig signingConfigs.debug\n buildConfigField 'String', 'ENVIRONMENT', '\"LOCAL\"';\n buildConfigField 'String', 'API_URL', '\"http://'+ InetAddress.getLocalHost().getCanonicalHostName() + ':8080/\"';\n }\n tst{ // build types may not start with 'test'\n debuggable true\n signingConfig signingConfigs.release\n buildConfigField 'String', 'ENVIRONMENT', '\"TEST\"';\n buildConfigField 'String', 'API_URL', '\"https://my-service-test.mydomain/\"';\n }\n stage{\n debuggable true\n signingConfig signingConfigs.release\n buildConfigField 'String', 'ENVIRONMENT', '\"STAGE\"';\n buildConfigField 'String', 'API_URL', '\"https://my-service-stage.mydomain/\"';\n }\n release {\n debuggable false\n minifyEnabled false\n signingConfig signingConfigs.release\n proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n buildConfigField 'String', 'ENVIRONMENT', '\"PROD\"';\n buildConfigField 'String', 'API_URL', '\"https://my-service.mydomain/\"';\n }\n }\n\n",[471,31015,31016,31020,31024,31028,31033,31038,31043,31048,31052,31056,31060,31065,31070,31075,31079,31083,31087,31091,31096,31101,31105,31109,31114,31118,31122,31126,31131,31136,31140],{"__ignoreMap":469},[474,31017,31018],{"class":476,"line":477},[474,31019,917],{"emptyLinePlaceholder":916},[474,31021,31022],{"class":476,"line":507},[474,31023,28465],{},[474,31025,31026],{"class":476,"line":547},[474,31027,28489],{},[474,31029,31030],{"class":476,"line":584},[474,31031,31032],{}," debuggable true\n",[474,31034,31035],{"class":476,"line":607},[474,31036,31037],{}," signingConfig signingConfigs.debug\n",[474,31039,31040],{"class":476,"line":642},[474,31041,31042],{}," buildConfigField 'String', 'ENVIRONMENT', '\"LOCAL\"';\n",[474,31044,31045],{"class":476,"line":663},[474,31046,31047],{}," buildConfigField 'String', 'API_URL', '\"http://'+ InetAddress.getLocalHost().getCanonicalHostName() + ':8080/\"';\n",[474,31049,31050],{"class":476,"line":694},[474,31051,5704],{},[474,31053,31054],{"class":476,"line":700},[474,31055,28507],{},[474,31057,31058],{"class":476,"line":913},[474,31059,31032],{},[474,31061,31062],{"class":476,"line":920},[474,31063,31064],{}," signingConfig signingConfigs.release\n",[474,31066,31067],{"class":476,"line":926},[474,31068,31069],{}," buildConfigField 'String', 'ENVIRONMENT', '\"TEST\"';\n",[474,31071,31072],{"class":476,"line":932},[474,31073,31074],{}," buildConfigField 'String', 'API_URL', '\"https://my-service-test.mydomain/\"';\n",[474,31076,31077],{"class":476,"line":938},[474,31078,5704],{},[474,31080,31081],{"class":476,"line":944},[474,31082,28525],{},[474,31084,31085],{"class":476,"line":950},[474,31086,31032],{},[474,31088,31089],{"class":476,"line":956},[474,31090,31064],{},[474,31092,31093],{"class":476,"line":962},[474,31094,31095],{}," buildConfigField 'String', 'ENVIRONMENT', '\"STAGE\"';\n",[474,31097,31098],{"class":476,"line":4876},[474,31099,31100],{}," buildConfigField 'String', 'API_URL', '\"https://my-service-stage.mydomain/\"';\n",[474,31102,31103],{"class":476,"line":4888},[474,31104,5704],{},[474,31106,31107],{"class":476,"line":4900},[474,31108,28543],{},[474,31110,31111],{"class":476,"line":4913},[474,31112,31113],{}," debuggable false\n",[474,31115,31116],{"class":476,"line":4921},[474,31117,30939],{},[474,31119,31120],{"class":476,"line":4932},[474,31121,31064],{},[474,31123,31124],{"class":476,"line":4938},[474,31125,30944],{},[474,31127,31128],{"class":476,"line":4946},[474,31129,31130],{}," buildConfigField 'String', 'ENVIRONMENT', '\"PROD\"';\n",[474,31132,31133],{"class":476,"line":4952},[474,31134,31135],{}," buildConfigField 'String', 'API_URL', '\"https://my-service.mydomain/\"';\n",[474,31137,31138],{"class":476,"line":4957},[474,31139,5704],{},[474,31141,31142],{"class":476,"line":4969},[474,31143,1276],{},[439,31145,31146],{},"After syncing the gradle file, we can access the URL in the java code through the BuildConfig class and don’t need any\ncode to differentiate between the environments, as this URL is based on the buildType that we selected.",[464,31148,31150],{"className":16895,"code":31149,"language":16897,"meta":469,"style":469},"BuildConfig.API_URL\n",[471,31151,31152],{"__ignoreMap":469},[474,31153,31154],{"class":476,"line":477},[474,31155,31149],{},[3938,31157,31159],{"id":31158},"mocked-services","Mocked Services",[439,31161,31162],{},"For the mocked services, I’ll provide you with two different approaches, which both have their upsides and downsides.",[439,31164,31165],{},"The first approach is to use more build types.",[439,31167,31168],{},"The second approach is using two product flavors.",[1065,31170,31172],{"id":31171},"mocked-services-via-build-type","Mocked Services via build type",[439,31174,31175],{},"We add the config we need to the ‘debug’ buildType:",[464,31177,31179],{"className":28456,"code":31178,"language":28458,"meta":469,"style":469}," buildTypes {\n debug{\n buildConfigField 'String', 'ENVIRONMENT', '\"DEV\"';\n buildConfigField 'String', 'API_URL', '\"https://not.really.needed/\"';\n }\n ...\n }\n\n",[471,31180,31181,31185,31189,31194,31199,31203,31208],{"__ignoreMap":469},[474,31182,31183],{"class":476,"line":477},[474,31184,28465],{},[474,31186,31187],{"class":476,"line":507},[474,31188,28470],{},[474,31190,31191],{"class":476,"line":547},[474,31192,31193],{}," buildConfigField 'String', 'ENVIRONMENT', '\"DEV\"';\n",[474,31195,31196],{"class":476,"line":584},[474,31197,31198],{}," buildConfigField 'String', 'API_URL', '\"https://not.really.needed/\"';\n",[474,31200,31201],{"class":476,"line":607},[474,31202,5704],{},[474,31204,31205],{"class":476,"line":642},[474,31206,31207],{}," ...\n",[474,31209,31210],{"class":476,"line":663},[474,31211,1276],{},[439,31213,31214],{},"As we’ve already defined the ‘environment’ as a buildConfigField, we can just use it in our code to decide whether we\nshould use the real service calls or the mocked services.",[439,31216,31217],{},"Personally, I like to use a custom Application class in order to provide singleton instances of the Services I use in an\nandroid app, so I’ll just use this approach to illustrate the use of the mocked services.",[439,31219,31220],{},"We create a DemoService interface with two implementations, one for calling the web api, and one mocked implementation,\nreturning only static data. Depending on the set ‘environment’, we use the appropriate implementation of the service.",[464,31222,31224],{"className":709,"code":31223,"language":711,"meta":469,"style":469},"public class DemoApplication extends Application {\n private DemoService demoService;\n public DemoService getDemoService() {\n if (demoService == null) {\n demoService = createDemoService();\n }\n return demoService;\n }\n private DemoService createDemoService() {\n if (\"DEV\".equals(BuildConfig.ENVIRONMENT)) {\n return new DemoServiceMockImpl();\n } else {\n return new DemoServiceWebApiImpl();\n }\n }\n}\n\n",[471,31225,31226,31231,31236,31241,31246,31251,31255,31260,31264,31269,31274,31279,31283,31288,31292,31296],{"__ignoreMap":469},[474,31227,31228],{"class":476,"line":477},[474,31229,31230],{},"public class DemoApplication extends Application {\n",[474,31232,31233],{"class":476,"line":507},[474,31234,31235],{}," private DemoService demoService;\n",[474,31237,31238],{"class":476,"line":547},[474,31239,31240],{}," public DemoService getDemoService() {\n",[474,31242,31243],{"class":476,"line":584},[474,31244,31245],{}," if (demoService == null) {\n",[474,31247,31248],{"class":476,"line":607},[474,31249,31250],{}," demoService = createDemoService();\n",[474,31252,31253],{"class":476,"line":642},[474,31254,5704],{},[474,31256,31257],{"class":476,"line":663},[474,31258,31259],{}," return demoService;\n",[474,31261,31262],{"class":476,"line":694},[474,31263,1276],{},[474,31265,31266],{"class":476,"line":700},[474,31267,31268],{}," private DemoService createDemoService() {\n",[474,31270,31271],{"class":476,"line":913},[474,31272,31273],{}," if (\"DEV\".equals(BuildConfig.ENVIRONMENT)) {\n",[474,31275,31276],{"class":476,"line":920},[474,31277,31278],{}," return new DemoServiceMockImpl();\n",[474,31280,31281],{"class":476,"line":926},[474,31282,21063],{},[474,31284,31285],{"class":476,"line":932},[474,31286,31287],{}," return new DemoServiceWebApiImpl();\n",[474,31289,31290],{"class":476,"line":938},[474,31291,5704],{},[474,31293,31294],{"class":476,"line":944},[474,31295,1276],{},[474,31297,31298],{"class":476,"line":950},[474,31299,703],{},[439,31301,31302],{},"With this approach, we have to make sure that the DemoApplication is the only place, where an instance of this service\nis created.",[439,31304,31305],{},"Upsides:",[994,31307,31308,31311],{},[997,31309,31310],{},"Easy configuration",[997,31312,31313],{},"Fast build speed (+1 build type = +1 APK to build)",[439,31315,31316],{},"Downsides:",[994,31318,31319,31322],{},[997,31320,31321],{},"We need to check the environment in the code",[997,31323,31324],{},"We ship the mock code in the production APK",[1065,31326,31328],{"id":31327},"mocked-services-via-product-flavors","Mocked Services via product flavors",[439,31330,31331],{},"In this approach, instead of adding a new buildType, we add a build flavor:",[464,31333,31335],{"className":28456,"code":31334,"language":28458,"meta":469,"style":469}," productFlavors {\n webApi {}\n mock {}\n }\n\n",[471,31336,31337,31342,31347,31352],{"__ignoreMap":469},[474,31338,31339],{"class":476,"line":477},[474,31340,31341],{}," productFlavors {\n",[474,31343,31344],{"class":476,"line":507},[474,31345,31346],{}," webApi {}\n",[474,31348,31349],{"class":476,"line":547},[474,31350,31351],{}," mock {}\n",[474,31353,31354],{"class":476,"line":584},[474,31355,1276],{},[439,31357,31358],{},"We can still use the application to provide a singleton of our service, but we don’t have to check the environment\nanymore, since with product flavors, we can place different implementations with the same filename in each product\nflavor.",[464,31360,31362],{"className":709,"code":31361,"language":711,"meta":469,"style":469},"public class DemoApplication extends Application {\n private DemoService demoService;\n public DemoService getDemoService() {\n if (demoService == null) {\n demoService = new DemoServiceImpl();\n }\n return demoService;\n }\n}\n\n",[471,31363,31364,31368,31372,31376,31380,31385,31389,31393,31397],{"__ignoreMap":469},[474,31365,31366],{"class":476,"line":477},[474,31367,31230],{},[474,31369,31370],{"class":476,"line":507},[474,31371,31235],{},[474,31373,31374],{"class":476,"line":547},[474,31375,31240],{},[474,31377,31378],{"class":476,"line":584},[474,31379,31245],{},[474,31381,31382],{"class":476,"line":607},[474,31383,31384],{}," demoService = new DemoServiceImpl();\n",[474,31386,31387],{"class":476,"line":642},[474,31388,5704],{},[474,31390,31391],{"class":476,"line":663},[474,31392,31259],{},[474,31394,31395],{"class":476,"line":694},[474,31396,1276],{},[474,31398,31399],{"class":476,"line":700},[474,31400,703],{},[439,31402,31403],{},"We’ll put one DemoServiceImpl into the ‘src/main/webApi/’ directory and another one into the ‘src/main/mock/’ directory,\nas those directories represent the specific code for the product flavor.",[439,31405,31406],{},"Note that you can’t put two java classes, which have the same name, into main as well as into a product flavor. Java\nclasses can’t be overridden by product flavors, only resources can.",[439,31408,31409],{},[2205,31410],{"alt":31411,"src":31412},"productFlavors","https://media.synyx.de/uploads//2016/08/productFlavors-300x265.png",[464,31414,31416],{"className":709,"code":31415,"language":711,"meta":469,"style":469},"public interface DemoService {\n /**\n * @return list of stuff, may be empty\n */\n List\u003CString> getStuff();\n}\n\n",[471,31417,31418,31423,31428,31433,31438,31443],{"__ignoreMap":469},[474,31419,31420],{"class":476,"line":477},[474,31421,31422],{},"public interface DemoService {\n",[474,31424,31425],{"class":476,"line":507},[474,31426,31427],{}," /**\n",[474,31429,31430],{"class":476,"line":547},[474,31431,31432],{}," * @return list of stuff, may be empty\n",[474,31434,31435],{"class":476,"line":584},[474,31436,31437],{}," */\n",[474,31439,31440],{"class":476,"line":607},[474,31441,31442],{}," List\u003CString> getStuff();\n",[474,31444,31445],{"class":476,"line":642},[474,31446,703],{},[439,31448,31449],{},[448,31450,31451],{},"webApi:",[464,31453,31455],{"className":709,"code":31454,"language":711,"meta":469,"style":469},"public class DemoServiceImpl implements DemoService {\n @Override\n public List\u003CString> getStuff() {\n List\u003CString> stuff = new ArrayList\u003C>();\n // do web request and put the results into 'stuff'\n return stuff;\n }\n}\n\n",[471,31456,31457,31462,31466,31471,31476,31481,31486,31490],{"__ignoreMap":469},[474,31458,31459],{"class":476,"line":477},[474,31460,31461],{},"public class DemoServiceImpl implements DemoService {\n",[474,31463,31464],{"class":476,"line":507},[474,31465,21043],{},[474,31467,31468],{"class":476,"line":547},[474,31469,31470],{}," public List\u003CString> getStuff() {\n",[474,31472,31473],{"class":476,"line":584},[474,31474,31475],{}," List\u003CString> stuff = new ArrayList\u003C>();\n",[474,31477,31478],{"class":476,"line":607},[474,31479,31480],{}," // do web request and put the results into 'stuff'\n",[474,31482,31483],{"class":476,"line":642},[474,31484,31485],{}," return stuff;\n",[474,31487,31488],{"class":476,"line":663},[474,31489,1276],{},[474,31491,31492],{"class":476,"line":694},[474,31493,703],{},[439,31495,31496],{},[448,31497,31498],{},"mock:",[464,31500,31502],{"className":709,"code":31501,"language":711,"meta":469,"style":469},"public class DemoServiceImpl implements DemoService {\n private static List\u003CString> stuff = new ArrayList\u003C>();\n public DemoServiceImpl() {\n stuff.add(\"foo\");\n stuff.add(\"bar\");\n stuff.add(\"baz\");\n }\n @Override\n public List\u003CString> getStuff() {\n return stuff;\n }\n}\n\n",[471,31503,31504,31508,31513,31518,31523,31528,31533,31537,31541,31545,31549,31553],{"__ignoreMap":469},[474,31505,31506],{"class":476,"line":477},[474,31507,31461],{},[474,31509,31510],{"class":476,"line":507},[474,31511,31512],{}," private static List\u003CString> stuff = new ArrayList\u003C>();\n",[474,31514,31515],{"class":476,"line":547},[474,31516,31517],{}," public DemoServiceImpl() {\n",[474,31519,31520],{"class":476,"line":584},[474,31521,31522],{}," stuff.add(\"foo\");\n",[474,31524,31525],{"class":476,"line":607},[474,31526,31527],{}," stuff.add(\"bar\");\n",[474,31529,31530],{"class":476,"line":642},[474,31531,31532],{}," stuff.add(\"baz\");\n",[474,31534,31535],{"class":476,"line":663},[474,31536,1276],{},[474,31538,31539],{"class":476,"line":694},[474,31540,21043],{},[474,31542,31543],{"class":476,"line":700},[474,31544,31470],{},[474,31546,31547],{"class":476,"line":913},[474,31548,31485],{},[474,31550,31551],{"class":476,"line":920},[474,31552,1276],{},[474,31554,31555],{"class":476,"line":926},[474,31556,703],{},[439,31558,31559],{},"For a class in the respective product flavor to be included in the class path of the project in the IDE (thus enabling\nall the nice and fancy IDE features for it), a build type that uses this product flavor needs to be selected. As you may\nhave noticed, each of the defined buildTypes is now present twice in the build types dropdown, once for each product\nflavor. This also means that if you build the app via ‘./gradlew build’, each of the buildTypes will be built once for\nevery product flavor, effectively doubling the build time. If you only selectively build single build types, this should\nbe no problem.",[439,31561,31562],{},[2205,31563],{"alt":31564,"src":31565},"buildTypes","https://media.synyx.de/uploads//2016/08/buildTypes-1-300x266.png",[994,31567,31568,31571],{},[997,31569,31570],{},"No code checks needed (for this usecase of product flavors)",[997,31572,31573],{},"Clean separation of release code and mock code",[439,31575,31316],{},[994,31577,31578,31581,31584],{},[997,31579,31580],{},"Configuration and development are a bit trickier",[997,31582,31583],{},"Slower build speed (For each product flavor, an APK for every buildType is built)",[997,31585,31586],{},"Compilation errors in currently unused flavors may go unnoticed, as the classes in unused flavors aren’t compiled",[1065,31588,31589],{"id":17245},"That’s it",[439,31591,31592],{},"Which of the two methods you want to use, is up to you. I prefer the former approach, but in an app that makes more use\nof product flavors as of buildTypes, I’d use the latter one.",[439,31594,31595,31596,1402],{},"More details on the build types and product flavors can be found in\nthe ",[1002,31597,31600],{"href":31598,"rel":31599},"https://developer.android.com/studio/build/build-variants.html",[1006],"official documentation",[439,31602,31603],{},"As always, feel free to share your questions, thoughts and better approaches in the comments 🙂",[1024,31605,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":31607},[31608,31609,31610,31611],{"id":28668,"depth":507,"text":30897},{"id":30958,"depth":507,"text":30959},{"id":30991,"depth":507,"text":30992},{"id":31158,"depth":507,"text":31159,"children":31612},[31613,31614,31615],{"id":31171,"depth":547,"text":31172},{"id":31327,"depth":547,"text":31328},{"id":17245,"depth":547,"text":31589},[1031],"2016-08-17T12:02:04","Different build types in android can be used to build the same application with different configurations. This can be\\npredefined config values like ‘debuggable’, but you can also define your own config values that will be accessible in\\nyour application. This post will show you some ways in which you can use this functionality to easily build your app for\\ndifferent environments of remote services and for better local development.","https://synyx.de/blog/android-building-apks-for-different-environments-using-build-types-and-product-flavors/",{},"/blog/android-building-apks-for-different-environments-using-build-types-and-product-flavors",{"title":30885,"description":30894},"blog/android-building-apks-for-different-environments-using-build-types-and-product-flavors",[11132,28668,31625,28669,14132,31626,31627,31628],"buildconfigfield","flavor","mock","product-flavor","Different build types in android can be used to build the same application with different configurations. This can be predefined config values like ‘debuggable’, but you can also define your…","6oUymYXTFf13mmfpUHQe3ZoLmEGJ1OWeQugBRA0ZGCs",{"id":31632,"title":31633,"author":31634,"body":31635,"category":31759,"date":31760,"description":469,"extension":1034,"link":31761,"meta":31762,"navigation":916,"path":31763,"seo":31764,"slug":31639,"stem":31765,"tags":31766,"teaser":31769,"__hash__":31770},"blog/blog/feedback-nach-dem-bahn-modell.md","Feedback nach dem BAHN-Modell",[244],{"type":432,"value":31636,"toc":31757},[31637,31640,31650,31653,31656,31659,31662,31673,31676,31679,31693,31696,31699,31702,31705,31708,31711,31714,31717,31720,31723,31726,31729,31732,31735,31738,31741,31744,31747],[435,31638,31633],{"id":31639},"feedback-nach-dem-bahn-modell",[439,31641,31642],{},[1002,31643,31646],{"href":31644,"rel":31645},"https://media.synyx.de/uploads//2016/08/IMG_4095-1.jpg",[1006],[2205,31647],{"alt":31648,"src":31649},"IMG_4095","https://media.synyx.de/uploads//2016/08/IMG_4095-1-218x300.jpg",[439,31651,31652],{},"Die erste Assoziation mit dem Namen könnte nahelegen, dass im Folgenden ein Monolog über die Zuverlässigkeit oder\nPünktlichkeit der „Deutschen Bahn“ folgt. Jedoch handelt es sich stattdessen beim BAHN-Modell um ein einfaches\nFeedback-Modell, wobei BAHN für „Beobachtung, Auswirkung, Hintergrund und Nachfrage“ steht.",[439,31654,31655],{},"Bevor ich das BAHN-Modell näher erläutere, möchte ich zwei wichtige Fragen aufgreifen: Welche Bedeutung kommt dem\nFeedback im agilen Umfeld zu? Welche Fallstricke können beim Feedbackgeben auftreten?",[439,31657,31658],{},"Im agilen Kontext spielt Feedback eine besondere Rolle. Dabei geht es nicht alleine um das produktbezogene Feedback von\nKunden und Anwender in den Meetings. Sondern auch um das Feedback in den Retrospektiven und besonders in allen weiteren\nalltäglichen Situationen im Beruf. Denn im agilen Umfeld steht die Teamarbeit im Mittelpunkt. Das hat zur Folge, dass\nüberall dort, wo Menschen eng miteinander zusammenarbeiten, die Wahrscheinlichkeit höher ist, dass Konflikte auftreten\nkönnen. Sich gegenseitig Feedback zu geben, kann helfen diese Konflikte zu klären.",[439,31660,31661],{},"Beim konstruktiven Feedback gibt es einige Fallstricke zu beachten:",[8310,31663,31664,31667,31670],{},[997,31665,31666],{},"Der Feedbackempfänger sollte zuvor gefragt werden, ob er Feedback haben möchte. Denn möglicherweise ist dieser gerade\nnicht in der emotionalen Verfassung für Feedback.",[997,31668,31669],{},"Das Feedback sollte in einem passenden Rahmen erfolgen. Feedback auf dem Flur, zwischen „Tür und Angel“ kann dazu\nführen, dass dieses für den Empfänger wie ein Überfall wirkt. Damit der Empfänger sich nicht vorgeführt fühlt und die\nVertraulichkeit gewahrt bleibt, sollte konstruktives Feedback unter 4 Augen (bzw. den Beteiligten) stattfinden.",[997,31671,31672],{},"Damit das Wesentliche einer Botschaft ankommt, sollte das Feedback kurz und konkret sein. Das Feedback wird dann\nkonkret, wenn es sich auf einzelne Beispiele von Beobachtungen oder Gesprochenem bezieht. Eine klare Struktur des\nFeedbacks ohne Füllwörter und Wiederholungen ist kurz und verständlich.",[439,31674,31675],{},"Das BAHN-Modell:",[439,31677,31678],{},"Das BAHN-Modell ist ein Feedback-Modell unter vielen. Was es jedoch attraktiv und interessant macht, ist der einfache\nund klar strukturierte Aufbau:",[8310,31680,31681,31684,31687,31690],{},[997,31682,31683],{},"B = Beobachtung: der Feedbackgeber schildert anhand seiner Beobachtung oder einer Aussage, was ihm aufgefallen ist:\n„Mir ist aufgefallen, dass…“. Somit beginnt das Feedback mit einem konkreten Beispiel. Dem Empfänger wird an dieser\nStelle ermöglicht, falls von seiner Seite Einwände bestehen, einzuhaken. Vielleicht liegt eine Verwechselung oder ein\nMissverständnis in der Beobachtung vor.",[997,31685,31686],{},"A = Auswirkung: an dieser Stelle werden die eigenen Emotionen des Feedbackgebers im Zusammenhang mit der zuvor\nbeschriebenen Beobachtung adressiert: „In mir hat das ausgelöst…“. Der Empfänger erfährt somit etwas über die\nGefühlslage des Gegenübers. Dadurch bekommt das Feedback eine Gewichtung.",[997,31688,31689],{},"H = Hintergrund: hier findet eine Bedeutungsklärung statt: „warum erzähle ich dir das?“. Der Feedbackgeber macht\ndeutlich warum es ihm wichtig ist, das Feedback zu geben",[997,31691,31692],{},"N = Nachfrage: als letzter Punkt folgt eine Verständigungsfrage: „da mir deine Sichtweise wichtig ist, könntest du\ndir vorstellen, dass wir über … sprechen?“. Der Empfänger wird hierbei, auf respektvoller Art und Weise, zum\ngemeinsamen Dialog eingeladen. Er hat die Chance sich zu dem Gesagten zu äußern. Im Gegensatz zu anderen Modellen,\nfolgt keine konkrete Aufforderung oder ein Wunsch zu einer Verhaltensänderung.",[439,31694,31695],{},"Um das BAHN-Modell greifbarer zu machen, folgt ein Beispiel aus dem Arbeitsalltag:",[439,31697,31698],{},"(Der Einstieg in das Feedback-Modell)",[439,31700,31701],{},"Feedbackgeber: „Darf ich dir Feedback geben?“",[439,31703,31704],{},"Feedbackempfänger: „Ja.“",[439,31706,31707],{},"Feedbackgeber: „Mir ist in den letzten fünf bis sechs Meetings aufgefallen, dass du regelmäßig auf dein Handy schaust (\nBeobachtung). Das stört und verärgert mich (Auswirkung). Dadurch habe ich den Eindruck, dass einige wichtige Punkte an\ndir vorbei gehen. Da mir eine gute Zusammenarbeit mit dir am Herzen liegt, interessiert mich deine Sicht zu dem Thema (\nHintergrund). Könntest du dir vorstellen, dass wir über dieses Thema nochmal sprechen? (Nachfrage)“",[439,31709,31710],{},"(Der Ausstieg aus dem Feedback-Modell)",[439,31712,31713],{},"Feedbackempfänger: „Danke für das Feedback.“",[439,31715,31716],{},"Wie anhand des Beispiels zu sehen ist, beginnt das Feedback mit der Frage, ob Feedback in diesem Moment erwünscht ist.\nDer Empfänger könnte immer noch sagen, dass er jetzt keine Zeit mehr habe oder heute kein Feedback haben möchte. Es\nkönnte dann ein andere Termin vereinbart werden.",[439,31718,31719],{},"Der Feedbackgeber steigt anschließend mit einer konkreten Beobachtung in das Feedback ein. Dabei beschreibt er ein\nVerhalten, dass er in der Vergangenheit wahrgenommen hatte. Die Beobachtung sollte keine Wörter enthalten wie\n„immer/nie/jedes Mal/andauernd/ect. hast du…“. Denn dadurch könnte es sein das der Empfänger gedanklich abschweift oder\nsagt: „Nein. Das stimmt nicht.“ oder „Immer ist nicht zutreffend.“. Eine weitere Schwierigkeit in der Beobachtung\nbesteht darin, dass sie gar keine sein könnte. Oftmals wird anstatt einer Beobachtung eine Interpretation\ngenannt: „[…], dass du aus Langeweile regelmäßig auf dein Handy schaust.“ Die „Langeweile“ ist eine Interpretation\neines beobachtbaren Verhaltens. Sie beruht lediglich auf der Deutung von verbaler und nonverbaler Kommunikation. Sie\nmuss jedoch nicht zutreffend sein. Sollte die Interpretation falsch sein, könnte der Feedbackempfänger direkt entgegnen,\ndass der Feedbackgeber falsch liegt. Oder er könnte verärgert sein: „Woher willst du wissen, ob mir langweilig war oder\nnicht?“. Das Feedbackgespräch wäre dann vermutlich zu Ende.",[439,31721,31722],{},"Die Auswirkung „das stört und verärgert mich“ schildert dem Empfänger welche Emotion die Beobachtung beim Feedbackgeber\nausgelöst hat. In der Regel wünschen sich beide einen respektvollen Umgang, ohne das einer von beiden sich ärgern muss.\nDer Empfänger reagiert daher zumeist überrascht und hört dem Feedbackgeber weiter zu.",[439,31724,31725],{},"Der darauf folgende Hintergrund macht dem Empfänger klar, welche Folgen sein Verhalten aus der Sicht („Dadurch habe ich\nden Eindruck“…) des Feedbackgebers haben könnte.",[439,31727,31728],{},"Mit der Betonung, dass dem Feedbackgeber die „Zusammenarbeit am Herzen liegt“, eröffnet er die Nachfrage mit der Bitte\num ein weiteres Gespräch.",[439,31730,31731],{},"Abschließend wird das Feedback-Modell mit „Danke für das Feedback.“ von Seiten des Empfängers beendet. Das „Danke“\nbezieht sich dabei auf den Mut, die Mühe und den Willen Feedback zu geben. Es impliziert jedoch keine Zustimmung zum\nInhalt.",[439,31733,31734],{},"Warum ist im BAHN-Modell keine Aufforderung oder Wunsch zur Verhaltensänderung enthalten?",[439,31736,31737],{},"Die Antwort liegt in der Frage, was das Ziel des Feedbackgebers ist. Möchte dieser in Erfahrung bringen, warum der\nEmpfänger regelmäßig sein Handy in den Meetings benutzt, dann wird er wahrscheinlich mit einer Aufforderung oder einem\nWunsch zur Verhaltensänderung weniger Erfolg haben. Denn es ist ein Unterschied, ob der Feedbackgeber fragt: „Könntest\ndu dir vorstellen, dass wir über dieses Thema nochmal sprechen?“ oder dem Empfänger sagt, wie er sich in Zukunft\nverhalten soll. Eine Nachfrage zeugt von Empathie, zeigt Interesse am Empfänger und eröffnet einen großen\nHandlungsspielraum. Möglicherweise liefert der Empfänger eine sinnvolle Begründung, wie: „Ich warte auf einen wichtigen\nAnruf, weil mein Kind im Krankenhaus liegt.“ oder „Mir ist tatsächlich langweilig, weil ich nichts zum Meeting beitragen\nkann.“. Beides sind wertvolle Informationen.",[439,31739,31740],{},"Eine Aufforderung hingegen lädt nicht zwangsläufig zum Dialog ein. Es könnte sogar sein, dass sich der Empfänger gerne\nerklärt hätte, aber durch das fehlende Interesse und Verständnis enttäuscht ist. Je nach Grund wird er der Aufforderung\nfolgen, oder eben nicht. Der Handlungsspielraum für Verbesserung der Zusammenarbeit wird dadurch kleiner. Wenn der\nFeedbackgeber jedoch zum Ziel hat so schnell wie möglich eine Verhaltensänderung zu erzielen, dann ist er eventuell\nnicht an den Gründen interessiert. Er kann sich diesen Punkt zeitlich sparen. Ob diese Strategie zum erwünschten\nErgebnis führt, steht auf einem anderen Blatt geschrieben.",[439,31742,31743],{},"Trotz der einfachen Struktur empfiehlt es sich zuvor das Modell mit Kollegen zu üben. Denn für den realen Einsatz kann\neine Routine im Umgang mit dem BAHN-Modell sehr hilfreich sein, um die oben genannten Fallstricke zu vermeiden.",[439,31745,31746],{},"Ich wünsche viel Spaß beim Feedback geben!",[439,31748,31749],{},[1002,31750,31753],{"href":31751,"rel":31752},"https://media.synyx.de/uploads//2016/08/IMG_4094-1.jpg",[1006],[2205,31754],{"alt":31755,"src":31756},"IMG_4094","https://media.synyx.de/uploads//2016/08/IMG_4094-1-218x300.jpg",{"title":469,"searchDepth":507,"depth":507,"links":31758},[],[1031],"2016-08-16T14:40:26","https://synyx.de/blog/feedback-nach-dem-bahn-modell/",{},"/blog/feedback-nach-dem-bahn-modell",{"title":31633,"description":469},"blog/feedback-nach-dem-bahn-modell",[31767,31768,9557,389,9159],"agil","feedback","Die erste Assoziation mit dem Namen könnte nahelegen, dass im Folgenden ein Monolog über die Zuverlässigkeit oder Pünktlichkeit der „Deutschen Bahn“ folgt. Jedoch handelt es sich stattdessen beim BAHN-Modell um…","pYt6xg6QC0M1KQ0YHV07ZvHkuDY72c1hCQq26LskY2g",{"id":31772,"title":31773,"author":31774,"body":31775,"category":32651,"date":32652,"description":31784,"extension":1034,"link":32653,"meta":32654,"navigation":916,"path":32655,"seo":32656,"slug":31779,"stem":32657,"tags":32658,"teaser":32662,"__hash__":32663},"blog/blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben.md","Verstehen und verstanden werden – Das Gordon-Modell im Berufsleben",[30],{"type":432,"value":31776,"toc":32628},[31777,31780,31785,31788,31793,31796,31801,31815,31824,31827,31831,31834,31837,31842,31845,31850,31855,31858,31863,31868,31871,31876,31885,31888,31892,31902,31907,31910,31914,31929,31936,31941,31944,31947,31952,31962,31965,31970,31973,31983,31990,31999,32002,32030,32033,32037,32042,32045,32048,32051,32054,32057,32061,32064,32069,32080,32084,32101,32106,32109,32113,32235,32239,32248,32253,32256,32259,32274,32289,32294,32305,32314,32317,32337,32340,32345,32355,32360,32363,32368,32371,32376,32379,32384,32387,32391,32400,32405,32408,32411,32414,32481,32484,32488,32493,32499,32511,32518,32524,32533,32537,32540,32544,32551,32558,32565,32572],[435,31778,31773],{"id":31779},"verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben",[439,31781,31782],{},[448,31783,31784],{},"Wir alle haben Beziehungen.",[439,31786,31787],{},"Manche Beziehungen sind enger, etwa zu Familienangehörigen und Freunden, andere lockerer, etwa zu Arbeitskollegen oder\nNachbarn. Mit manchen Menschen unterhalte ich mich, lerne sie aber nie wirklich kennen.",[11947,31789,31790],{},[439,31791,31792],{},"Egal, ob ich Menschen nahe komme oder Abstand halte, ich habe eine Beziehung zu ihnen.",[439,31794,31795],{},"Kommunikation erlaubt uns, die Form unserer Beziehungen zu verändern. Durch gute Kommunikation verbessern wir\nBeziehungen, durch schlechte Kommunikation verschlechtern wir sie.",[439,31797,31798],{},[448,31799,31800],{},"Doch wodurch zeichnet sich gute und wodurch schlechte Kommunikation aus?",[439,31802,31803,31808,31809,31814],{},[1002,31804,31807],{"href":31805,"rel":31806},"https://de.wikipedia.org/wiki/Thomas_Gordon_(Psychologe)",[1006],"Thomas Gordon",", ein amerikanischer Psychologe, entwickelte\ndas sogenannte ",[1002,31810,31813],{"href":31811,"rel":31812},"https://de.wikipedia.org/wiki/Gordon-Modell",[1006],"Gordon-Modell",", ein Kommunikationsmodell zur Lösung von\nKonflikten.",[439,31816,31817,31818,31823],{},"In seinem\nBuch ",[1002,31819,31822],{"href":31820,"rel":31821},"https://www.amazon.de/Familienkonferenz-L%C3%B6sung-Konflikten-zwischen-Eltern/dp/3453602323/ref=sr_1_1?ie=UTF8&qid=1468915359&sr=8-1&keywords=familienkonferenz",[1006],"Familienkonferenz",",\ndas im Jahre 1970 erschien, beschreibt Gordon erstmals dieses Kommunikationsmodell zur Lösung von Konflikten zwischen\nEltern und Kind.",[439,31825,31826],{},"Das Gordon-Modell lässt sich jedoch auf jegliche Formen von Beziehungen anwenden, sowohl im privaten, als auch auch im\nberuflichen Bereich.",[3938,31828,31830],{"id":31829},"verhaltensfenster","Verhaltensfenster",[439,31832,31833],{},"Das Gordon-Modell bietet verschiedene Kommunikationswerkzeuge zur Lösung von Konflikten an. Welches Werkzeug wann\nsinnvoll ist, hängt dabei ganz von der Situation ab. Es ist wichtig zu erkennen, welcher der Beteiligten das Problem\nbesitzt, um zu entscheiden, was für die Situation angemessen ist.",[439,31835,31836],{},"Das sogenannte Verhaltensfenster veranschaulicht dies in drei unterschiedlichen Zuständen:",[439,31838,31839],{},[448,31840,31841],{},"Jemand anderes besitzt ein Problem",[439,31843,31844],{},"Mir geht es gut, ich kann aber wahrnehmen, dass jemand anderes ein Problem hat. Für mich ist das Problem nicht greifbar,\nes hat nichts mit mir zu tun und schadet mir nicht.",[439,31846,31847],{},[990,31848,31849],{},"Beispiel: Arbeitskollege erzählt, dass er sich vom Chef ungerecht behandelt fühlt.",[439,31851,31852],{},[448,31853,31854],{},"Problemfreie Zone",[439,31856,31857],{},"Keiner hat ein Problem.",[439,31859,31860],{},[990,31861,31862],{},"Beispiel: Arbeitskollege erzählt von seinem Sommerurlaub.",[439,31864,31865],{},[448,31866,31867],{},"Ich besitze ein Problem",[439,31869,31870],{},"Ich habe ein Problem, das mir schadet oder weh tut – und das liegt möglicherweise an der Verhaltensweise einer anderen\nPerson.",[439,31872,31873],{},[990,31874,31875],{},"Beispiel: Arbeitskollege surft öfters privat im Internet statt seine Arbeit zu erledigen, die dann an mir hängen\nbleibt.",[439,31877,31878],{},[1002,31879,31882],{"href":31880,"rel":31881},"https://media.synyx.de/uploads//2016/07/verhaltensfenster4-1.png",[1006],[2205,31883],{"alt":31830,"src":31884},"https://media.synyx.de/uploads//2016/07/verhaltensfenster4-1-300x234.png",[439,31886,31887],{},"Die oberen beiden Bereiche des Verhaltensfensters beinhalten Verhaltensweisen, die für mich akzeptabel sind, während der\nuntere Bereich die für mich nicht akzeptablen Verhaltensweisen enthält. Ob etwas als akzeptabel oder als nicht\nakzeptabel empfunden wird, kann sich allerdings je nach Kontext oder Zeit verändern.",[3938,31889,31891],{"id":31890},"aktives-zuhören","Aktives Zuhören",[11947,31893,31894,31897],{},[439,31895,31896],{},"In der zwischenmenschlichen Kommunikation geht es darum, zu verstehen und verstanden zu werden.",[439,31898,31899,31900],{},"– ",[990,31901,31807],{},[439,31903,31904],{},[448,31905,31906],{},"Wer ein Problem hat, empfindet negative Gefühle.",[439,31908,31909],{},"Nur diese Person besitzt diese Gefühle und Gedanken, daher kann nur er/sie dieses Problem lösen. Wenn man versucht die\nProbleme eines anderen zu lösen, signalisiert man demjenigen womöglich nur, dass man das Gegenüber für unfähig hält,\nselbst eine Lösung zu finden.",[1065,31911,31913],{"id":31912},"beispiel","Beispiel",[11947,31915,31916,31919,31924],{},[439,31917,31918],{},"Gespräch zwischen zwei Kollegen beim Mittagessen:",[439,31920,31921],{},[990,31922,31923],{},"„XY hat vorhin einen Workshop gehalten zu dem neuen Framework, das wir demnächst in unserem neuen Projekt\nausprobieren wollen. Ich konnte nur schwer folgen und habe gefühlt die Hälfte nicht verstanden.“",[439,31925,31926],{},[990,31927,31928],{},"„Ja, es kann manchmal durchaus herausfordernd sein, XY zu folgen. Er kann teilweise echt hektisch sein. Aber warum\nhast du denn nicht einfach nachgefragt, wenn du etwas nicht verstanden hast?“",[439,31930,31931,31932,31935],{},"Einerseits erfährt derjenige mit dem Problem Zustimmung ",[990,31933,31934],{},"„Ja, es kann manchmal durchaus herausfordernd sein, XY zu\nfolgen“",", gleichzeitig aber wird er belehrt und darauf hingewiesen, warum er denn nicht einfach nachgefragt habe; als ob\ner nicht in der Lage sei, selbst auf diese Idee zu kommen.",[439,31937,31938],{},[448,31939,31940],{},"Hilfe zur Selbsthilfe",[439,31942,31943],{},"Besser ist es, wenn ich als Hörer versuche, meinem Gegenüber dabei zu helfen, seine Probleme selbst zu erkennen, zu\nanalysieren und eigene Lösungswege zu finden.",[439,31945,31946],{},"Ein Werkzeug dafür ist das aktive Zuhören. Dadurch wird Vertrauen und Zuwendung ausgedrückt und dem Problembesitzer\ndabei geholfen, sich selbst zu helfen und dadurch selbstbestimmter und unabhängiger zu werden.",[439,31948,31949],{},[448,31950,31951],{},"Verschlüsselte Botschaften",[439,31953,31954,31955,31958,31959,1402],{},"Teilweise können Probleme ganz klar geäußert werden, durch Äußerungen wie ",[990,31956,31957],{},"„Das Geräusch hat mich erschreckt“"," oder\n",[990,31960,31961],{},"„Ich bin müde und deshalb nicht so gut drauf“",[439,31963,31964],{},"Manchmal können Gedanken und Gefühle aber nicht direkt mitgeteilt werden. Man kann beispielsweise traurig sein, aber\ndiese Erfahrung nicht sprachlich vermitteln. Je nach Gefühlszustand kann es schwierig sein, das innere Erleben in Worte\nzu fassen. Vielmehr ist das, was über Gedanken und Gefühle mitgeteilt wird dann eher als eine Art verschlüsselte\nBotschaft zu verstehen.",[439,31966,31967],{},[448,31968,31969],{},"Botschaften entschlüsseln",[439,31971,31972],{},"Hier kommt das aktive Zuhören zum Einsatz. Ich als Hörer habe die Aufgabe, die Botschaft zu entschlüsseln, indem ich\ndurch Wiedergabe des Gesagten in meinen eigenen Worten beim Sprecher nachfrage, ob ich die Botschaft richtig\nentschlüsselt habe. Dabei sind eigene Botschaften wie Urteile oder Ratschläge tunlichst zu vermeiden.",[439,31974,31975,31976,8764,31979,31982],{},"Wenn der Sinn richtig wiedergegeben wurde, wird der Sprecher zustimmen – und sei es nur durch ein kurzes ",[990,31977,31978],{},"„richtig“",[990,31980,31981],{},"„ja“"," – und fortfahren.",[439,31984,31985,31986,31989],{},"Wenn der Sinn nicht korrekt entschlüsselt wurde, wird der Sprecher korrigieren ",[990,31987,31988],{},"„Nein, ich meine eher …“"," und erst\ndanach fortfahren.",[439,31991,31992],{},[1002,31993,31996],{"href":31994,"rel":31995},"https://media.synyx.de/uploads//2016/07/entschluesseln.jpg",[1006],[2205,31997],{"alt":31969,"src":31998},"https://media.synyx.de/uploads//2016/07/entschluesseln-300x129.jpg",[1065,32000,31913],{"id":32001},"beispiel-1",[11947,32003,32004,32006,32010,32015,32020,32025],{},[439,32005,31918],{},[439,32007,32008],{},[990,32009,31923],{},[439,32011,32012],{},[990,32013,32014],{},"„Meinst du, XY hat zu schnell geredet, sodass es dir schwer fiel, gedanklich zu folgen?“",[439,32016,32017],{},[990,32018,32019],{},"„Nein, eigentlich war der Workshop wirklich gut vorbereitet und XY hat sich alle Mühe gegeben, nicht zu hektisch zu\nsprechen. Es ist eher, dass ich schon länger nicht mehr mit dieser Programmiersprache zu tun hatte, die anderen\nWorkshop-Teilnehmer aber schon.“",[439,32021,32022],{},[990,32023,32024],{},"„Du hast Angst das neue Wissen nicht so schnell aufsaugen zu können wie deine Kollegen.“",[439,32026,32027],{},[990,32028,32029],{},"„Richtig. Ich fühle mich manchmal regelrecht abgehängt von meinen Kollegen. Ich habe mir schon länger vorgenommen,\nwieder mehr Fachliteratur zu lesen. In letzter Zeit habe ich das einfach zu sehr schleifen lassen. Ich sollte das neue\nFramework zum Anlass nehmen und wieder damit beginnen.“",[439,32031,32032],{},"In diesem Gespräch erfährt derjenige mit dem Problem Verständnis vom Zuhörer und findet gleichzeitig selbst zur Lösung\nseines Problems.",[1065,32034,32036],{"id":32035},"best-practices","Best Practices",[439,32038,32039],{},[448,32040,32041],{},"Ich kann nur richtig zuhören…",[439,32043,32044],{},"… wenn ich aufmerksam bin. Ich kann nicht aufmerksam sein, wenn ich gerade eigentlich keine Zeit oder Lust zum Zuhören\nhabe.",[439,32046,32047],{},"… wenn ich den anderen annehmen kann wie er ist, ohne seine Aussagen direkt zu bewerten oder zu beurteilen.",[439,32049,32050],{},"… wenn ich still bin. Wenn ich gleichzeitig rede oder überlege, was ich als nächstes sage, kann ich nicht wirklich\nkonzentriert zuhören.",[439,32052,32053],{},"… wenn ich ernsthaft versuche mich in das Gesagte einzufühlen und die entschlüsselte Botschaft in meinen Worten\nzurückzumelden.",[439,32055,32056],{},"… wenn ich vermeide, den Retter zu spielen, indem ich dem Problembesitzer Lösungsvorschläge vorsetze.",[3938,32058,32060],{"id":32059},"türöffner-vs-kommunikationssperren","Türöffner vs. Kommunikationssperren",[439,32062,32063],{},"Um mit jemandem ins Gespräch zu kommen und mehr über ihn und seine Gedanken und Gefühle zu erfahren, sollten sogenannte\nTüröffner benutzt und Kommunikationssperren vermieden werden.",[439,32065,32066],{},[448,32067,32068],{},"Gespräche einleiten und fortführen",[439,32070,32071,32072,32075,32076,32079],{},"Türöffner sind offene Fragen, die nicht mit einem einfachen ",[990,32073,32074],{},"„Ja“"," oder ",[990,32077,32078],{},"„Nein“"," beantwortet werden können und dadurch\nein Gespräch initiieren.",[1065,32081,32083],{"id":32082},"beispiele","Beispiele",[11947,32085,32086,32091,32096],{},[439,32087,32088],{},[990,32089,32090],{},"„Mich würde deine Meinung dazu interessieren.“",[439,32092,32093],{},[990,32094,32095],{},"„Stimmt etwas nicht?“",[439,32097,32098],{},[990,32099,32100],{},"„Was denkst du darüber?“",[439,32102,32103],{},[448,32104,32105],{},"Gespräche im Keim ersticken",[439,32107,32108],{},"Kommunikationssperren hingegen können den Gesprächspartner in die Defensive treiben oder das Gespräch direkt in\nSchweigen ersticken. Keine der Sperren vermittelt Verständnis für denjenigen, der das Problem hat.",[1065,32110,32112],{"id":32111},"die-12-kommunikationssperren-nach-gordon","Die 12 Kommunikationssperren nach Gordon",[11947,32114,32115,32120,32125,32130,32135,32140,32145,32150,32155,32160,32165,32170,32175,32180,32185,32190,32195,32200,32205,32210,32215,32220,32225,32230],{},[439,32116,32117],{},[448,32118,32119],{},"Befehlen, Anordnen, Bestimmen",[439,32121,32122],{},[990,32123,32124],{},"„Das muss heute noch getan werden, da gibt es nichts zu diskutieren.“",[439,32126,32127],{},[448,32128,32129],{},"Drohen, Warnen",[439,32131,32132],{},[990,32133,32134],{},"„Wenn das bis nächste Woche nicht erledigt ist, dann haben wir ein Problem miteinander.“",[439,32136,32137],{},[448,32138,32139],{},"Moralisieren, Predigen",[439,32141,32142],{},[990,32143,32144],{},"„Stell dich nicht so an, da muss jeder mal durch.“",[439,32146,32147],{},[448,32148,32149],{},"Ratschläge erteilen, Lösungen vorgeben",[439,32151,32152],{},[990,32153,32154],{},"„Also an deiner Stelle würde ich ja …“",[439,32156,32157],{},[448,32158,32159],{},"Vorträge halten, belehren, Fakten liefern",[439,32161,32162],{},[990,32163,32164],{},"„Also eigentlich sind die Fakten ja folgendermaßen …“",[439,32166,32167],{},[448,32168,32169],{},"Urteile fällen, Vorwürfe machen, kritisieren",[439,32171,32172],{},[990,32173,32174],{},"„Das ist doch Schwachsinn, was du da sagst!“",[439,32176,32177],{},[448,32178,32179],{},"Loben, schmeicheln",[439,32181,32182],{},[990,32183,32184],{},"„Das kriegst du schon hin!“",[439,32186,32187],{},[448,32188,32189],{},"Beschimpfen, lächerlich machen",[439,32191,32192],{},[990,32193,32194],{},"„Tu doch nicht so, als ob du jetzt das erste Mal davon hörst!“",[439,32196,32197],{},[448,32198,32199],{},"Interpretieren, diagnostizieren, analysieren",[439,32201,32202],{},[990,32203,32204],{},"„Das sagst du doch jetzt nur, weil …“",[439,32206,32207],{},[448,32208,32209],{},"Trösten, Sympathie bekunden",[439,32211,32212],{},[990,32213,32214],{},"„Nimm dir das nicht so zu Herzen.“",[439,32216,32217],{},[448,32218,32219],{},"Forschen, fragen, verhören",[439,32221,32222],{},[990,32223,32224],{},"„Was hast du getan, um eine Lösung zu finden?“",[439,32226,32227],{},[448,32228,32229],{},"Zurückziehen, sarkastisch reagieren, ausweichen",[439,32231,32232],{},[990,32233,32234],{},"„Da musst du einfach drüber stehen. Komm, lass uns zusammen einen Kaffee holen gehen, dann kommst du auf andere\nGedanken.“",[3938,32236,32238],{"id":32237},"ich-botschaften","Ich-Botschaften",[11947,32240,32241,32244],{},[439,32242,32243],{},"Wenn ich möchte, dass andere Menschen mich verstehen, muss ich über mich selbst sprechen.",[439,32245,31899,32246],{},[990,32247,31807],{},[439,32249,32250],{},[448,32251,32252],{},"Wenn ich selbst ein Problem habe, bin ich in der Verantwortung, etwas zu unternehmen und die Person mit den für mich\nnicht akzeptablen Verhaltensweisen zu konfrontieren.",[439,32254,32255],{},"Ich muss genau beschreiben, welche Handlungen oder Äußerungen mir Probleme verursachen. Damit mein Gegenüber meine\nGedanken und Gefühle nachvollziehen kann, eignen sich Ich-Botschaften besser als Du-Botschaften. Einerseits bekomme\nich selbst mehr Klarheit über mich und meine Bedürfnisse, andererseits erfährt die konfrontierte Person etwas über meine\ntatsächlichen Bedürfnisse und Gefühle. Außerdem muss mein Gesprächspartner nicht in Verteidigungshaltung gehen, da er\nnicht angegriffen wird.",[1065,32257,32083],{"id":32258},"beispiele-1",[11947,32260,32261,32266,32271],{},[439,32262,32263],{},[448,32264,32265],{},"Du-Botschaft",[439,32267,32268],{},[990,32269,32270],{},"„Du bist unhöflich“",[439,32272,32273],{},"Hier muss die konfrontierte Person mehr oder weniger raten, was genau ich als unhöflich in ihrem Verhalten empfinde.",[11947,32275,32276,32281,32286],{},[439,32277,32278],{},[448,32279,32280],{},"Ich-Botschaft",[439,32282,32283],{},[990,32284,32285],{},"„Wenn du mich unterbrichst und nicht ausreden lässt, habe ich das Gefühl, dass meine Teilnahme in diesem Meeting\nnicht erwünscht ist.“",[439,32287,32288],{},"Hier wird konkret beschrieben, was am Verhalten der konfrontierten Person als unhöflich empfunden wird.",[439,32290,32291],{},[448,32292,32293],{},"Eine Ich-Botschaft setzt sich aus folgenden drei Komponenten zusammen:",[8310,32295,32296,32299,32302],{},[997,32297,32298],{},"Eine neutrale, vorwurfsfreie Beschreibung, die keine Verallgemeinerungen oder Interpretationen enthält",[997,32300,32301],{},"Greifbare, konkrete Folgen des Verhaltens der konfrontierten Person für mich",[997,32303,32304],{},"Meine persönlichen Empfindungen angesichts der Situation",[439,32306,32307],{},[1002,32308,32311],{"href":32309,"rel":32310},"https://media.synyx.de/uploads//2016/07/ich-botschaft.jpg",[1006],[2205,32312],{"alt":32280,"src":32313},"https://media.synyx.de/uploads//2016/07/ich-botschaft-300x106.jpg",[1065,32315,31913],{"id":32316},"beispiel-2",[11947,32318,32319,32325,32331],{},[439,32320,32321,32324],{},[990,32322,32323],{},"„Wenn du nicht pünktlich zum vereinbarten Termin erscheinst,"," [neutrale Beschreibung]",[439,32326,32327,32330],{},[990,32328,32329],{},"müssen wir auf dich warten und können nicht pünktlich beginnen, weshalb der Termin dann länger dauert."," [greifbare\nWirkung]",[439,32332,32333,32336],{},[990,32334,32335],{},"Da ich häufig Folgetermine habe, muss ich direkt zum nächsten Termin hetzen. Dadurch fühle ich mich oft in Stress\nversetzt.“"," [persönliche Empfindung]",[1065,32338,32036],{"id":32339},"best-practices-1",[439,32341,32342],{},[448,32343,32344],{},"Vermeiden von Verallgemeinerungen und Interpretationen",[439,32346,32347,32348,32075,32351,32354],{},"Beim Beschreiben von Verhalten sollte auf Verallgemeinerungen wie ",[990,32349,32350],{},"„immer“",[990,32352,32353],{},"„nie“"," verzichtet werden.\nInterpretationen oder Urteile sind ebenfalls fehl am Platz. Es geht darum, Verhaltensweisen so neutral wie möglich zu\nbeschreiben und sich darauf zu beschränken, was ich sehen und hören kann. Unverschämtheit, Nervosität oder schlechte\nLaune sind zum Beispiel keine Verhaltensweisen.",[439,32356,32357],{},[448,32358,32359],{},"Zeitnah und situationsbezogen klären",[439,32361,32362],{},"Die nicht akzeptablen Verhaltensweisen sollten zeitnah und situationsbezogen angesprochen werden. Viele negative\nEmotionen bei sich aufstauen zu lassen und die Person später mit mehreren Problemen auf einmal zu konfrontieren ist\nnicht hilfreich.",[439,32364,32365],{},[448,32366,32367],{},"Angemessene Ich-Botschaften",[439,32369,32370],{},"Ich-Botschaften müssen ehrlich und angemessen sein. Wenn ich eine zu nett formulierte Ich-Botschaft gebe, meine\nKörpersprache aber signalisiert, dass ich unheimlich wütend bin, ist die Ich-Botschaft nicht angemessen. Ebenfalls\nnicht angemessen ist eine Ich-Botschaft mit heftiger Übertreibung der persönlichen Empfindung über geringfügige\nEreignisse.",[439,32372,32373],{},[448,32374,32375],{},"Ärger ist kein primäres Gefühl",[439,32377,32378],{},"Wenn ich Ärger empfinde, sollte ich mich fragen, welche Emotion vor dem Ärger präsent war. Was geschah, bevor ich\närgerlich wurde? War ich erschreckt, enttäuscht, verletzt …? Ärger ist kein primäres Gefühl und kann als feindselig und\nvorwurfsvoll interpretiert werden. Dies wiederum kann dazu führen, dass die konfrontierte Person sich angegriffen fühlt\nund defensiv reagiert.",[439,32380,32381],{},[448,32382,32383],{},"Lösung offen halten",[439,32385,32386],{},"Eine Ich-Botschaft sollte keine Lösung enthalten, sondern dem Gegenüber die Chance geben, sein Verhalten selbst zu\nändern.",[3938,32388,32390],{"id":32389},"umschalten","Umschalten",[11947,32392,32393,32396],{},[439,32394,32395],{},"Wenn ich Hilfe haben will, muss ich auch bereit sein, Hilfe zu leisten.",[439,32397,31899,32398],{},[990,32399,31807],{},[439,32401,32402],{},[448,32403,32404],{},"Menschliche Beziehungen und menschliches Verhalten sind komplex.",[439,32406,32407],{},"Ich-Botschaften sind ein praktisches Werkzeug, um jemanden darüber zu informieren, dass sein Verhalten bei mir ein\nProblem hervorruft. Allerdings sind sie keine Garantie dafür, dass sich die konfrontierte Person auf der Stelle und\nbereitwillig ändert.",[439,32409,32410],{},"Die Mitteilung, dass das eigene Verhalten in irgendeiner Form nicht akzeptabel sei, kann ziemlich unerfreulich sein. In\ndiesem Fall muss ich umschalten, das heißt es darf mir nicht mehr darum gehen, mir selbst zu helfen, indem ich mein\nGegenüber konfrontiere, sondern ich muss meinem Gegenüber helfen, mit der Konfrontation umzugehen.",[1065,32412,31913],{"id":32413},"beispiel-3",[11947,32415,32416,32419,32426,32432,32435,32441,32446,32451,32456,32461,32466,32471,32476],{},[439,32417,32418],{},"Ich warte seit drei Tagen auf eine E-Mail von meinem Arbeitskollegen mit Daten, die ich zur Erstellung einer\nwichtigen Präsentation brauche.",[439,32420,32421,32422,32425],{},"Ich konfrontiere ihn: ",[990,32423,32424],{},"„Du hast mir die E-Mail mit den Daten, die ich für die Präsentation brauche, noch nicht\ngeschickt. Ich warte nun schon seit drei Tagen darauf. Die Präsentation ist zwar erst nächste Woche, aber da ich vor\nsolchen wichtigen Präsentationen immer sehr aufgeregt bin, will ich mich zeitnah vorbereiten können. Daher versetzt es\nmich in Zeitdruck und Stress, dass ich die Daten von dir noch nicht habe.“"," (Konfrontation)",[439,32427,32428,32429],{},"Er entgegnet: ",[990,32430,32431],{},"„Ich dachte, es eilt nicht so sehr, aber wenn du dich so aufregst, kümmere ich sofort darum und lasse\ndir die Daten zukommen.“",[439,32433,32434],{},"Mein Gegenüber reagiert defensiv, das heißt plötzlich wird meine konfrontative Botschaft zum Problem für meinen\nKollegen. Wenn ich Unterstützung von ihm möchte, muss ich mich zunächst um ihn kümmern, indem ich empathisch zuhöre. Das\nbedeutet, ich schalte um von der Ich-Botschaft zum aktiven Zuhören.",[439,32436,32437,32440],{},[990,32438,32439],{},"„Du denkst, ich bin ungeduldig und nimmst mir das übel.“"," (Zuhören)",[439,32442,32443],{},[990,32444,32445],{},"„Naja, eigentlich hatte ich vor, mich heute darum zu kümmern, aber jetzt fühle ich mich gehetzt und dadurch in Stress\nversetzt.“",[439,32447,32448,32440],{},[990,32449,32450],{},"„Du hast das Gefühl, ich dränge dich zu etwas, das du sowieso tun wolltest.“",[439,32452,32453],{},[990,32454,32455],{},"„Richtig.“",[439,32457,32458,32425],{},[990,32459,32460],{},"„Ich habe von den anderen Kollegen bereits alle benötigten Daten erhalten und wollte spätestens bis Freitag mit der\nVorbereitung fertig werden. Da habe ich mich gewundert, dass ich deine E-Mail noch nicht habe.“",[439,32462,32463],{},[990,32464,32465],{},"„Ich hatte einfach viele Termine und bin schlicht und ergreifend noch nicht dazu gekommen.“",[439,32467,32468,32440],{},[990,32469,32470],{},"„Das verstehe ich. Wenn man selbst viel zu tun hat, kann das leicht passieren.“",[439,32472,32473],{},[990,32474,32475],{},"„Ich kümmere mich noch heute darum. Spätestens heute Abend hast du meine E-Mail mit den aufbereiteten Daten.“",[439,32477,32478],{},[990,32479,32480],{},"„Danke dir!“",[439,32482,32483],{},"Durch aktives Zuhören konnte die emotionale Temperatur des Gesprächspartners etwas heruntergekühlt und das Gespräch mit\neinem positiven Ausgang verlassen werden.",[3938,32485,32487],{"id":32486},"zusammenfassung","Zusammenfassung",[439,32489,32490],{},[448,32491,32492],{},"Wenn ich meine Beziehungen verbessern möchte, muss ich lernen…",[439,32494,32495,32496,17548],{},"… zu identifizieren, wer in einer Situation das Problem besitzt (",[1002,32497,31830],{"href":32498},"#verhaltensfenster",[439,32500,32501,32502,32506,32507,17548],{},"… wie ich Gespräche einleiten und fortführen kann (",[1002,32503,32505],{"href":32504},"#tueroffner","Türöffner",") und was ich vermeiden sollte, um\nGesprächsversuche nicht im Keim zu ersticken (",[1002,32508,32510],{"href":32509},"#kommunikationssperren","Kommunikationssperren",[439,32512,32513,32514,17548],{},"… wie ich richtig zuhören kann (",[1002,32515,32517],{"href":32516},"#aktives-zuhoeren","aktives Zuhören",[439,32519,32520,32521,17548],{},"… wie ich mich mitteilen kann, sodass andere mich verstehen (",[1002,32522,32238],{"href":32523},"#ich-botschaften",[439,32525,32526],{},[1002,32527,32530],{"href":32528,"rel":32529},"https://media.synyx.de/uploads//2016/07/verhaltensfenster5.png",[1006],[2205,32531],{"alt":31830,"src":32532},"https://media.synyx.de/uploads//2016/07/verhaltensfenster5-300x188.png",[3938,32534,32536],{"id":32535},"anmerkung","Anmerkung",[439,32538,32539],{},"Das erweiterte Gordon-Modell enthält Werkzeuge zum Lösen von Bedürfnis- und Wertekonflikten. Um den Rahmen nicht zu\nsprengen wurden diese hier unbeachtet gelassen.",[3938,32541,32543],{"id":32542},"literatur","Literatur",[439,32545,32546],{},[1002,32547,32550],{"href":32548,"rel":32549},"https://www.amazon.de/gp/product/3453602323/ref=s9_simh_gw_g14_i1_r?pf_rd_m=A3JWKAKR8XB7XF&pf_rd_s=desktop-1&pf_rd_r=N1SZ9K765608G0QW9H4E&pf_rd_t=36701&pf_rd_p=7fb339d7-1d6e-42de-9c12-dadc030ae13c&pf_rd_i=desktop",[1006],"Familienkonferenz: Die Lösung von Konflikten zwischen Eltern und Kind",[439,32552,32553],{},[1002,32554,32557],{"href":32555,"rel":32556},"https://www.amazon.de/gp/product/3453602331/ref=s9_simh_gw_g14_i2_r?pf_rd_m=A3JWKAKR8XB7XF&pf_rd_s=desktop-1&pf_rd_r=N1SZ9K765608G0QW9H4E&pf_rd_t=36701&pf_rd_p=7fb339d7-1d6e-42de-9c12-dadc030ae13c&pf_rd_i=desktop",[1006],"Die Neue Familienkonferenz: Kinder erziehen ohne zu strafen",[439,32559,32560],{},[1002,32561,32564],{"href":32562,"rel":32563},"https://www.amazon.de/gp/product/3608947086/ref=s9_simh_gw_g14_i5_r?pf_rd_m=A3JWKAKR8XB7XF&pf_rd_s=desktop-1&pf_rd_r=N1SZ9K765608G0QW9H4E&pf_rd_t=36701&pf_rd_p=7fb339d7-1d6e-42de-9c12-dadc030ae13c&pf_rd_i=desktop",[1006],"Gute Beziehungen: Wie sie entstehen und stärker werden",[439,32566,32567],{},[1002,32568,32571],{"href":32569,"rel":32570},"https://www.amazon.de/Managerkonferenz-Effektives-F%C3%BChrungstraining-Thomas-Gordon/dp/3453600002/ref=sr_1_1?ie=UTF8&qid=1468922876&sr=8-1&keywords=managerkonferenz",[1006],"Managerkonferenz: Effektives Führungstraining",[439,32573,12279,32574,32579,32584,32589,32594,32599,32604,32609,32614,32619,32624],{},[1002,32575,32578],{"href":32576,"rel":32577},"http://localhost:8080/blog/tags/aktives-zuhoeren/",[1006],"aktives zuhören",[1002,32580,32583],{"href":32581,"rel":32582},"http://localhost:8080/blog/tags/familienkonferenz/",[1006],"familienkonferenz",[1002,32585,32588],{"href":32586,"rel":32587},"http://localhost:8080/blog/tags/gewaltfreie-kommunikation/",[1006],"gewaltfreie kommunikation",[1002,32590,32593],{"href":32591,"rel":32592},"http://localhost:8080/blog/tags/gordon/",[1006],"gordon",[1002,32595,32598],{"href":32596,"rel":32597},"http://localhost:8080/blog/tags/gordon-modell/",[1006],"gordon-modell",[1002,32600,32603],{"href":32601,"rel":32602},"http://localhost:8080/blog/tags/ich-botschaft/",[1006],"ich-botschaft",[1002,32605,32608],{"href":32606,"rel":32607},"http://localhost:8080/blog/tags/kommunikation/",[1006],"Kommunikation",[1002,32610,32613],{"href":32611,"rel":32612},"http://localhost:8080/blog/tags/kommunikationssperren/",[1006],"kommunikationssperren",[1002,32615,32618],{"href":32616,"rel":32617},"http://localhost:8080/blog/tags/managerkonferenz/",[1006],"managerkonferenz",[1002,32620,32623],{"href":32621,"rel":32622},"http://localhost:8080/blog/tags/tueroeffner/",[1006],"türöffner",[1002,32625,31829],{"href":32626,"rel":32627},"http://localhost:8080/blog/tags/verhaltensfenster/",[1006],{"title":469,"searchDepth":507,"depth":507,"links":32629},[32630,32631,32636,32640,32645,32648,32649,32650],{"id":31829,"depth":507,"text":31830},{"id":31890,"depth":507,"text":31891,"children":32632},[32633,32634,32635],{"id":31912,"depth":547,"text":31913},{"id":32001,"depth":547,"text":31913},{"id":32035,"depth":547,"text":32036},{"id":32059,"depth":507,"text":32060,"children":32637},[32638,32639],{"id":32082,"depth":547,"text":32083},{"id":32111,"depth":547,"text":32112},{"id":32237,"depth":507,"text":32238,"children":32641},[32642,32643,32644],{"id":32258,"depth":547,"text":32083},{"id":32316,"depth":547,"text":31913},{"id":32339,"depth":547,"text":32036},{"id":32389,"depth":507,"text":32390,"children":32646},[32647],{"id":32413,"depth":547,"text":31913},{"id":32486,"depth":507,"text":32487},{"id":32535,"depth":507,"text":32536},{"id":32542,"depth":507,"text":32543},[1031],"2016-07-20T14:57:00","https://synyx.de/blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben/",{},"/blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben",{"title":31773,"description":31784},"blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben",[32659,32583,32660,32593,32598,32603,14820,32613,32618,32661,31829],"aktives-zuhoeren","gewaltfreie-kommunikation","tueroeffner","Wir alle haben Beziehungen. Manche Beziehungen sind enger, etwa zu Familienangehörigen und Freunden, andere lockerer, etwa zu Arbeitskollegen oder Nachbarn. Mit manchen Menschen unterhalte ich mich, lerne sie aber nie…","2HIeqBoTmqtq7-iczdX8hCD6dvEM_9pTCHI5yi7J97g",{"id":32665,"title":32666,"author":32667,"body":32668,"category":32782,"date":32783,"description":32784,"extension":1034,"link":32785,"meta":32786,"navigation":916,"path":32787,"seo":32788,"slug":32672,"stem":32789,"tags":32790,"teaser":32791,"__hash__":32792},"blog/blog/synyx-bei-den-entwicklertagen-in-karlsruhe.md","synyx bei den Entwicklertagen in Karlsruhe",[97],{"type":432,"value":32669,"toc":32776},[32670,32673,32676,32680,32683,32693,32696,32699,32705,32708,32711,32714,32717,32726,32729,32733,32736,32751,32754,32758,32761,32764,32767],[435,32671,32666],{"id":32672},"synyx-bei-den-entwicklertagen-in-karlsruhe",[439,32674,32675],{},"Die Entwicklertage in unserer Heimatstadt sind schon seit Jahren ein sicherer Anker in unserem Konferenzkalender. Der\nVeranstalter Andrena hat auch dieses Jahr wieder die Entwicklergemeinde der Region herbeizitiert und drei synyx Kollegen\nsind dem Ruf gefolgt.",[3938,32677,32679],{"id":32678},"conference-day","Conference Day",[439,32681,32682],{},"Leider viel zu spät erreichte ich am Mittwoch die IHK in Karlsruhe, um in die Konferenz einzutauchen. Familiäre\nVerpflichtungen hatten mich daran gehindert, pünktlich zum Frühstück auf der Matte zu stehen. Egal – die Keynote lohnt\nsich doch meistens eh nicht, oder? Bei den netten Damen am Andrena Infostand bekam ich meine Erkennungsmarke und die\nübliche Konferenz-Kampfausrüstung bestehend aus Tasche, Stift, Block, Programmflyer. Schnell einen Kaffee und ein paar\nHäppchen gezogen und mit den Kollegen gesynct. Die erzählten mir gleich, wie toll die Keynote doch war. Prof. Dr.\nMichael Feindt hat mit anschaunlichen Beispielen eindrucksvoll beschrieben, wie er und sein Team mit Machine Learning\nden Einzelhandel bei der Warenbestellung unterstützen. Mist, jetzt ärgerte ich mich doch, es verpasst zu haben. Aber es\nwar ja noch nicht zu spät, sich auf die vielversprechenden Vorträge des Conference Day zu freuen!",[439,32684,32685],{},[1002,32686,32689],{"href":32687,"rel":32688},"https://media.synyx.de/uploads//2022/03/schlechtetests.png",[1006],[2205,32690],{"alt":32691,"src":32692},"schlechtetests","https://media.synyx.de/uploads//2022/03/schlechtetests-300x222-1.png",[439,32694,32695],{},"In den sechs parallelen Tracks fand sich einiges solides wie die “10 goldenen Regeln für schlechte Tests” von Tilmann\nGlaser und Peter Fichtner, die aus ihrem reichhaltigen Erfahrungsschatz die häufigsten Pitfalls beim Schreiben von Tests\nteilten, und wie die JUnit5 Einführung von Johannes Link und Matthias Merdes, bei der man sich schonmal auf die neue\nVersion des verbreitetsten Testframeworks einstellen konnte. Eine erfrischende Abwechslung zu den codegefüllten Slides\nbot Christian Robert mit seinen Ideen und konkreten Vorschlägen, wie man ein glücklicher Entwickler wird! Seine\n“Happiness Patterns” helfen dabei, den grauen Entwickleralltag weniger grau zu gestalten und aus gewohnten\nVerhaltensmustern auszubrechen, um eine höhere Zufriedenheit bei seiner Arbeit zu erreichen.",[439,32697,32698],{},"Den Abschluss des Tages bildete Eberhard Wolf mit seiner Keynote von gewohnt hoher Qualität. Er stellte die kontroverse\nThese auf, dass Redundanz von Code und Daten im Falle von Microservices etwas Gutes sein kann. Seine gut auf den Punkt\ngebrachten Argumente konnten das Publikum davon überzeugen, dass er damit auch recht hat. Dabei betonte er aber auch,\ndass es sich sowohl bei der Entscheidung für Microservices als auch bei der Entscheidung für Redundanz nicht um eine\nSilver Bullet sondern immer um einen Tradeoff handelt, was eine der entscheidenden Messages seines Vortrags war.",[3938,32700,32702],{"id":32701},"agile-day",[448,32703,32704],{},"Agile Day",[439,32706,32707],{},"Fast pünktlich erreichte ich diesmal den Konferenzsaal Baden, in dem die Eröffnungskeynote des Agile Day stattfand.\nGunther Verheyen erzählte davon, was Scrum uns in der Vergangenheit brachte und in der Zukunft noch bringen wird und\nging dabei sehr viel auf Scrum Basics ein, die ich schon kannte – daher bevorzugte ich eine vorgezogene Kaffeepause, um\nerstmal den Akku voll zu machen.",[439,32709,32710],{},"Die Vorträge des Agile Day hatten wie schon letztes Jahr nur teilweise mit “Agile” zu tun, vermutlich ist es nicht so\neinfach, aus diesem Bereich genug Talks für einen ganzen Tag mit sechs parallelen Tracks an Land zu ziehen. Mir als\nEntwickler macht das aber natürlich nichts aus 😉",[439,32712,32713],{},"So schmuggelten sich beispielsweise David Burkhart und Max Bechtold mit ihren umfangreichen und sehr hilfreichen\nMethoden zur automatischen Architekturanalyse in den Agile Day als auch Mark Paluch, der mit Leidenschaft und hohem\nDetailgrad davon berichtete, wie er in 16 Stunden einen Gameboy-Klon gebaut hat.",[439,32715,32716],{},"Natürlich gab es auch Vorträge aus dem agilen Umfeld. Viel mitnehmen konnte man aus dem Erfahrungsbericht von Ben\nRomberg und Georg Meyer, die verschiedene Methoden des Code Reviews in ihrem Team getestet haben und sich mit dem\nPublikum rege darüber austauschten. Auch das Manager/AgileCoach Rollenspiel von Benjamin Seidler und Marion Gakstatter\ngefiel mir gut, weil man dort den Sinn und Unsinn verschiedener Metriken zur Team Performance gut vor Augen geführt\nbekam.",[439,32718,32719],{},[1002,32720,32723],{"href":32721,"rel":32722},"https://media.synyx.de/uploads//2022/03/irrertyp.png",[1006],[2205,32724],{"alt":32725,"src":32721},"irrertyp",[439,32727,32728],{},"Die abschließende Keynote der Konferenz war etwas Besonderes. James Coplien ließ sich in einem an Irrsinn grenzenden,\nunterhaltsamen Rant darüber aus, dass die agile und objektorientierte Entwicklung ihre Wurzeln verloren haben. Bewußt\nprovokante Aussagen wie “Unit Tests are bad for quality” und “Java is the only language where it is impossible to write\nobjects” schüttelten das Publikum ganz schön durcheinander. Die Bottom Line seiner emotionalen Keynote ist aber eine\nsehr bedeutende: Beim Schreiben von Software geht es um Menschen und wir dürfen die Menschen dabei nie aus den Augen\nverlieren – ein Grundsatz, den auch wir von synyx uns auf die Fahne schreiben.",[3938,32730,32732],{"id":32731},"tutorial-day","Tutorial Day",[439,32734,32735],{},"Einer von uns drei Kollegen besuchte am Freitag sogar noch den Tutorial Day und möchte seine durchweg positiven\nErfahrungen teilen:",[439,32737,32738,32739,32744,32745,32750],{},"Sabine Neubauer, Kristin Utech und Simon Wagner luden am Freitag zum\nWorkshop “",[1002,32740,32743],{"href":32741,"rel":32742},"https://entwicklertag.de/karlsruhe/2016/solid-verstehen-und",[1006],"SOLID – Verstehen und Anwenden","” ein. Ungefähr\n15 sowohl angehende, als auch langjährige Softwareentwickler folgten der Einladung. Zu Beginn wurde den Teilnehmern die\nSOLID Prinzipien theoretisch näher gebracht, danach wurden im Pair\nverschiedene ",[1002,32746,32749],{"href":32747,"rel":32748},"https://github.com/emilybache/Racing-Car-Katas",[1006],"Aufgaben"," gelöst. In den Aufgaben sollten verschiedene\nVerletzungen der Prinzipien erkannt werden.",[439,32752,32753],{},"Zunächst wurden Implementierung und Tests den Erkenntnissen entsprechend refactored und ergänzt, dann wurden nach jeder\nAufgabe die Lösungen vorgestellt und darüber diskutiert. Durch das fundierte Wissen der Workshopleiter waren auch\nDiskussionen außerhalb der Übungen möglich. Dieses Tutorial war eine ausgezeichnete Möglichkeit für Softwareentwickler\nfernab des Tagesgeschäfts sich die SOLID Prinzipien anzueignen und diese umzusetzen.",[3938,32755,32757],{"id":32756},"ende-gut-alles-gut","Ende gut, alles gut?",[439,32759,32760],{},"Was lässt sich nach drei Entwicklertagen festhalten? Erstmal ein großes Lob an den Veranstalter Andrena! Die\nDurchführung der Konferenz war wie immer stark, das Organisationsteam hat merklich gute Arbeit geleistet. Man fühlte\nsich immer gut verpflegt, hatte immer einen Überblick über das Konferenzgeschehen und es war eine gute Aufteilung\nzwischen Vorträgen zum Zuhören und Pausen zum Austauschen. In den kleineren Vortragsräumen gab es gelegentlich Probleme\nmit dem Platz und dem zu kleinen Beamerbild aber das fiel nie groß ins Gewicht. Eine gute Idee war die Liveübertragung\nder Talks aus dem großen Saal ins Foyer, so dass man auch mal eine ausgedehnte Kaffepause mit einem Ohr beim\nKollegengespräch und einem Ohr beim Speaker zubringen konnte.",[439,32762,32763],{},"Nicht ganz so gut fällt unser Urteil diesmal beim eigentlichen “Fleisch” der Konferenz aus, den Talks. Es gab einige\nVorträge, die gut waren und die man unter “solide Grundkost” einordnen kann. Allerdings flog während der Vorträge auch\nzu oft ein “wär ich doch in den anderen Track gegangen” durch unseren Gruppenchat. Es bleibt das Gefühl, dass bei der\nAuswahl der Talks eher auf Quantität als auf Qualität geachtet wurde, so dass man die sechs Tracks für zwei Tage auch\nvoll bekommt. Was nur wenig vorkam waren echte Highlights, Neuheiten, Kontroversen, Talks aus denen man als erfahrener\nEntwickler viel mitnehmen kann. Natürlich haben wir mit drei Mann auch nur einen kleinen Teil aller Vorträge mitbekommen\nund hatten vielleicht einfach nur Pech. Dieses Urteil fiel unter uns dreien auch nicht einstimmig aus – unser jüngerer\nKollege konnte für sich viel aus den Vorträgen mitnehmen und verließ die Konferenz sehr zufrieden – insbesondere nach\nder Teilnahme am Workshop Day am Freitag.",[439,32765,32766],{},"Abschließend kann ich dennoch sagen, dass diese verlässlich grundsolide Heimatkonferenz garantiert auch nächstes Jahr\nwieder von synyx Kollegen besucht wird.",[439,32768,32769,32770,32775],{},"Folien zu den Vorträgen sind ",[1002,32771,32774],{"href":32772,"rel":32773},"https://entwicklertag.de/karlsruhe/2016/programm",[1006],"auf der Seite der Entwicklertage"," unter\nden jeweiligen Programmpunkten verlinkt.",{"title":469,"searchDepth":507,"depth":507,"links":32777},[32778,32779,32780,32781],{"id":32678,"depth":507,"text":32679},{"id":32701,"depth":507,"text":32704},{"id":32731,"depth":507,"text":32732},{"id":32756,"depth":507,"text":32757},[1031],"2016-06-24T14:53:00","Die Entwicklertage in unserer Heimatstadt sind schon seit Jahren ein sicherer Anker in unserem Konferenzkalender. Der\\nVeranstalter Andrena hat auch dieses Jahr wieder die Entwicklergemeinde der Region herbeizitiert und drei synyx Kollegen\\nsind dem Ruf gefolgt.","https://synyx.de/blog/synyx-bei-den-entwicklertagen-in-karlsruhe/",{},"/blog/synyx-bei-den-entwicklertagen-in-karlsruhe",{"title":32666,"description":32675},"blog/synyx-bei-den-entwicklertagen-in-karlsruhe",[],"Die Entwicklertage in unserer Heimatstadt sind schon seit Jahren ein sicherer Anker in unserem Konferenzkalender. Der Veranstalter Andrena hat auch dieses Jahr wieder die Entwicklergemeinde der Region herbeizitiert und drei…","T2ZyqH3l90veLzI7v-X3LsxJlfUv9UpFQDPmiecK864",{"id":32794,"title":32795,"author":32796,"body":32797,"category":33153,"date":33154,"description":33155,"extension":1034,"link":33156,"meta":33157,"navigation":916,"path":33158,"seo":33159,"slug":32801,"stem":33161,"tags":33162,"teaser":33165,"__hash__":33166},"blog/blog/synyx-goto-amsterdam.md","synyx GOTO Amsterdam",[335,124,184],{"type":432,"value":32798,"toc":33148},[32799,32802,32815,32819,32822,32825,32828,32838,32841,32844,32847,32851,32854,32857,32860,32863,32866,32869,32872,32875,32884,32892,32895,32906,32909,32913,32928,32931,32946,32949,32952,32976,32988,32991,33006,33026,33036,33039,33046,33073,33088,33093,33096,33106,33126,33132,33135,33145],[435,32800,32795],{"id":32801},"synyx-goto-amsterdam",[439,32803,32804,32805,32810,32811,32814],{},"Vom 13. bis 15.06.2016 waren wir zu siebt in Amsterdam auf der ",[1002,32806,32809],{"href":32807,"rel":32808},"http://gotocon.com/amsterdam-2016/",[1006],"goto; Amsterdam",".\nZunächst gibt es einen kleinen Reisebericht zu lesen auf den dann ein paar Impressionen aus den einzelnen Sessions und\nTalks folgen. Wer direkt zu den inhaltlichen Schwerpunkten unserer Reise springen möchte bitte ",[1002,32812,8234],{"href":32813},"#goto"," entlang.",[3938,32816,32818],{"id":32817},"die-ankunft","Die Ankunft",[439,32820,32821],{},"„Ich hol mir nen rohen Hering“, stellte Marc beim Ausstieg in Amsterdam klar. Dem wurde nicht widersprochen, allerdings\nwolle man zunächst das Gepäck zum Hostel bringen.",[439,32823,32824],{},"Dort angekommen, wurde der Plan beschlossen den Hotdog-Laden „Fat Dog“ aufzusuchen, der bereits in der Kochshow\n„Kitchen impossible“ lobend erwähnt wurde.",[439,32826,32827],{},"Nach einem mindestens zehn stündigen Fußmarsch kamen wir dann sogar dort an und hatten ordentlich Hunger mitgebracht.\nDer Großteil der Gruppe hatte sich extra für das bevorstehende Match: Deutschland – Ukraine passend mit einem mehr oder\nweniger aktuellen Deutschlandtrikot gekleidet. Dies wurde sofort bemerkt woraufhin wir auf das Tagesspecial aufmerksam\ngemacht wurden.",[439,32829,32830],{},[1002,32831,32834],{"href":32832,"rel":32833},"https://media.synyx.de/uploads//2016/06/IMG-20160612-WA0006-1.jpg",[1006],[2205,32835],{"alt":32836,"src":32837},"IMG-20160612-WA0006","https://media.synyx.de/uploads//2016/06/IMG-20160612-WA0006-1-242x300.jpg",[439,32839,32840],{},"Im Nachhinein betrachtet, hätten wir diesem Angebot mehr Beachtung schenken sollen. Denn die Hotdogs, die wir\nbestellten, reichten gerade dafür aus, die verbrauchte Energie für den 15 stündigen Fußmarsch wiederherzustellen. „Ich\nhätte noch Lust auf einen Hering“ stellte Marc erneut fest.",[439,32842,32843],{},"Allerdings rückte der Anpfiff näher und wir mussten noch eine geeignete Bar finden, um das Spiel zu verfolgen. Einige\nGehminuten später wurden wir auch fündig. Gut gelaunt verließen wir nach dem erfolgreichen 2:0 die Bar und machten uns\nauf den Wunsch von Honnel auf die Suche nach einem Krokettenautomaten (Ja richtig gelesen…). David übernahm die\nNavigation und führte uns zu besagtem Automaten. (…) Glücklich und zufrieden legten wir uns schlafen und sammelten\nEnergie für den nächsten Tag.",[439,32845,32846],{},"zZz …Hering…murmel murmel…zZzZ",[3938,32848,32850],{"id":32849},"amsterdam-kompakt","Amsterdam kompakt",[439,32852,32853],{},"Den Montag wollten wir dazu nutzen uns ein wenig mit Amsterdam vertraut zu machen. Beim gemeinsamen Frühstück, wurde\nbesprochen, wie man den Tag am besten Nutzen könnte. Hierbei entstanden zwei Pläne:",[439,32855,32856],{},"• Mit dem Rad ans Meer",[439,32858,32859],{},"• Ab ins Rijksmuseum",[439,32861,32862],{},"Die Planung für die Radtour gestaltete sich etwas schwierig, da aus den Wetterberichten verschiedener Apps keine klare\nTendenz hervorging. Wir entschieden uns daher für die etwas kürzere Variante. Zunächst suchten wir einen Fiets verhuur (\nFahrradverleih) auf und bekamen drei Fahrräder. Zwei davon hatten sogar eine Klingelautomatik (War halt kaputt und hat\nbei jeder Bodenwelle gebimmelt…) integriert!",[439,32864,32865],{},"Ansonsten waren die Fahrräder allerdings super, also machten wir uns auf den Weg in Richtung Meer. David als Navigator\nvoraus und der dauerklingelnde Marc und ich hinterher.",[439,32867,32868],{},"Zielsicher navigierte uns David im Zick Zack durch Amsterdam.",[439,32870,32871],{},"Brücke…Links…Rechts…Brücke…Rechts… Oh hier geht’s nicht weiter… Aber da drüben!",[439,32873,32874],{},"Nach einigen Minuten hatten wir aus der Stadt herausgefunden und radelten geradewegs aufs Meer zu.",[439,32876,32877],{},[1002,32878,32881],{"href":32879,"rel":32880},"https://media.synyx.de/uploads//2016/06/Radtour_Meer.jpg",[1006],[2205,32882],{"alt":32883,"src":32879},"Radtour_Meer",[439,32885,32886,32887,32891],{},"Da das Wetter noch hielt und wir noch voller Energie waren, entschlossen wir noch etwas weiter zu fahren. Zum Glück!\nSonst hätten wir nie diese Ziege\nentdeckt",[2205,32888],{"alt":32889,"src":32890},"Radtour_Ziege","https://media.synyx.de/uploads//2016/06/Radtour_Ziege.jpg","\nSchließlich in Marken angekommen, legten wir eine kurze Pause ein bevor wir uns auf den Rückweg machten. Leider begann\nes nun etwas zu regnen. Marc und David zogen das Tempo an. Ich hechelte hinterher.",[439,32893,32894],{},"Die Ziege hatte mittlerweile Gebrauch von ihrer Hütte gemacht und sich vollständig zurückgezogen. Glücklicherweise hörte\nes nach einem kurzen Schauer dann auch wieder auf zu regnen und wir kamen halbwegs trocken und zufrieden wieder im\nHostel an und machten uns auf die Suche nach dem Rest unserer Gruppe.",[439,32896,32897,32898,32905],{},"In der Zwischenzeit bewunderte der Rest der synyx-Reisegruppe die kulturellen Schätze des Rijksmuseum, allen voran\nRembrandt, Van Gogh und andere große Meister. Auch die Abteilung der „Trinkspiele“, sowie das ein oder andere Werk\nbrachten einem zum\nSchmunzeln.",[1002,32899,32902],{"href":32900,"rel":32901},"https://media.synyx.de/uploads//2016/06/IMG-20160613-WA0014.jpg",[1006],[2205,32903],{"alt":32904,"src":32900},"Museum","\nAnbetracht des Wetters war das Museum sehr gut besucht, sodass man sich ab und an durch die Menschenmassen vor dem\nBildern kämpfen musste. Auch die Umleitungssituation im Museum führte doch zu einigen Verwirrungen. Nach dem\nanstrengenden und überaus anspruchsvollen Museumsbesuch hatten wir uns eine Stärkung verdient. Allerdings war auch zu\ndiesem Zeitpunkt kein Hering in Sicht sodass wir einen ordentlichen Burger und anschließende Stroopwafel mit Genuß\nverzehrten. Zu diesem Zeitpunkt stießen dann auch die wilden Radler (gerade nochmal rechtzeitig) dazu.",[439,32907,32908],{},"Zusammen ging es dann weiter bei einer klassischen Grachtenfahrt, die sich recht schnell zur einer drei-sprachigen\nGiebelführung durch Amsterdam entpuppte.",[3938,32910,32912],{"id":32911},"die-konferenz-goto-amsterdam-2016","Die Konferenz: goto; Amsterdam 2016",[439,32914,32915,32923,32924,18773],{},[1002,32916,32919],{"href":32917,"rel":32918},"https://media.synyx.de/uploads//2016/06/IMG_20160614_101157.jpg",[1006],[2205,32920],{"alt":32921,"src":32922},"IMG_20160614_101157","https://media.synyx.de/uploads//2016/06/IMG_20160614_101157-1024x768.jpg","\nNachdem wir den ersten Tag die Stadt unsicher gemacht hatten, ging es jetzt weiter mit dem eigentlichen Ziel der Reise,\ndie ",[1002,32925,32927],{"href":32807,"rel":32926},[1006],"goto; Amsterdam 2016",[439,32929,32930],{},"Die Konferenz bot mehrere Tracks in unterschiedlichen Bereichen der Softwareentwicklung (Data, Spring, Legacy to\nMicroservices, JavaScript und Security) aber auch für nicht- technische Themen wurden in eigenen Track wie Post-Agile\noder im Philosophy Track interessante Vorträge gehalten. Alles in allem ein sehr breit ausgestelltes Programm und für\njeden etwas dabei. Zusätzlich waren die Keynotes mit großen Persönlichkeiten aus unterschiedlichsten Bereichen besetz.",[439,32932,32933,32934,32939,32940,32945],{},"Den ",[1002,32935,32938],{"href":32936,"rel":32937},"http://gotocon.com/amsterdam-2016/presentations/show_presentation.jsp?oid=7505",[1006],"Anfang"," machte Erich Gamma der über\nEntwicklung von Visual Studio Code, eins der wohl größten JavaScript basierten Projekte, berichtete. Es wurde eine\nkomplette IDE auf Basis vom Webtechnologien geschaffen, welche mit Features einer herkömmlichen IDE ausgestattet ist und\nauf allen gängigen Betriebssystemen verfügbar ist aufgrund des auf ",[1002,32941,32944],{"href":32942,"rel":32943},"http://electron.atom.io/",[1006],"Electron"," basierten\nAufbaus. Ein spannendes Projekt was durchaus mit den richtigen Ansprüchen daher kommt Stichwort: “Eating your own dog\nfood” was den direkten Einsatz der IDE bei Mircosoft mit einschließt. Allerdings wurde man während des Vortrags das\nGefühl nicht los, dass der Vortrag vor allem dazu dient Entwickler für das Microsoft Universum zu gewinnen. Zusätzlich\nhätte Erich Gamma tatsächlich mal eine Runde Live-Demo Beratung bei Josh Long nehmen können. Aber dazu später mehr.",[439,32947,32948],{},"Weiter ging es in sechs nebenläufigen Tracks die immer wieder und zur Mittagszeit etwas länger mit Pausen und sehr\nhochwertigen Essen unterbrochen wurden.",[439,32950,32951],{},"Hier ein paar Berichte aus einzelnen Sessions die uns am meisten begeistert haben:",[439,32953,32954,32955,32960,32961,32966,32967,32975],{},"Der Data Track beschäftigte sich mit Themen wie der Verarbeitung von massenhaften Daten bspw. mit maschinellen\nLernverfahren. Aber auch das Verständnis für Daten und daraus resultierenden Annahmen wurde betrachtet. Dazu\nmachte",[1002,32956,32959],{"href":32957,"rel":32958},"https://lukasvermeer.wordpress.com/",[1006],"Lukas Vermeer","\nmit ",[1002,32962,32965],{"href":32963,"rel":32964},"http://gotocon.com/amsterdam-2016/presentation/Data%20Science%20vs.%20Data%20Alchemy",[1006],"Data Sciene vs Data Alchemy","\nden Anfang und zeigte die Fallstricke der Geschichte auf die Aufgrund von Daten oder besser gesagt Beobachtungen gemacht\nwurden und sich allerdings am Ende oft als Trugschluss herausstellten. Ein Beispiel wurde anhand von Sauerkraut auf den\nSeeschiffen des 17 und 18 Jahrhunderts gemacht, welches zwar für den Anwendungsfall der Skorbut Krankheit (Armut an\nVitamin C)\nausreichte. ",[1002,32968,32971],{"href":32969,"rel":32970},"https://media.synyx.de/uploads//2016/06/Screenshot-from-2016-06-21-09-19-58.png",[1006],[2205,32972],{"alt":32973,"src":32974},"Screenshot from 2016-06-21 09-19-58","https://media.synyx.de/uploads//2016/06/Screenshot-from-2016-06-21-09-19-58-300x174.png","\nAllerdings war diese Annahme beispielsweise für die Südpolexpeditionen nicht mehr richtig. So reichte die Vitaminmenge\nder verwendeten Sauerkrautvariante einfach nicht aus um die Versorgung der Expetitionsteilnehmer mit dem lebenswichtigen\nVitamin zu decken.",[11947,32977,32978,32983],{},[439,32979,32980],{},[990,32981,32982],{},"“Science is limited by data",[439,32984,32985],{},[990,32986,32987],{},"Data is limited by engineering”",[439,32989,32990],{},"Diese Kernaussage wurde durch viele Beispiele aus der Geschichte unterstrichen und steht gerade im Hinblick auf\nService-Angeboten wie kaggle.com oder ähnlichen Möglichkeiten zur Datenanalyse im direkten Konflikt, da letztendlich\nquasi jede Aussage aus Daten generiert werden kann auf Basis gewisser Annahmen.",[439,32992,32993,32994,32999,33000,33005],{},"Auch mit vielen Daten und jede Menge Last ging es weiter im Vortrag\nvon ",[1002,32995,32998],{"href":32996,"rel":32997},"https://twitter.com/rusmeshenberg",[1006],"Ruslan Meshenberg","\nüber",[1002,33001,33004],{"href":33002,"rel":33003},"http://gotocon.com/amsterdam-2016/presentation/Microservices%20at%20Netflix%20Scale%20-%20First%20Principles,%20Tradeoffs%20&%20Lessons%20Learned",[1006],"Microservices at Netflix Scale",".\nEr berichtete aus der Umstellung der Netflix Architektur auf eine Microservice basierte Architektur und berief sich\ndabei auf einige Grundprinzipien als Quintessenz.",[994,33007,33008,33011,33020,33023],{},[997,33009,33010],{},"Buy vs Build: Zuerst Open Source Software unterstützen und nur im Ausnahmefall auf eigene Lösungen setzen",[997,33012,33013,33014,33019],{},"Services should be stateless: Zur Überprüfung steht\ndie ",[1002,33015,33018],{"href":33016,"rel":33017},"http://techblog.netflix.com/2011/07/netflix-simian-army.html",[1006],"simian army","bereit, siehe Bild 🙂",[997,33021,33022],{},"Scale out vs. scale up: Da vertikale Skalierung irgendwann an Hardwaregrenzen stößt (mindest schneller)",[997,33024,33025],{},"Automate destructive testing: Vor allem auch in der Produktivumgebung",[439,33027,33028],{},[1002,33029,33032],{"href":33030,"rel":33031},"https://media.synyx.de/uploads//2016/06/pngbase6475ef3cbae089d72b.png",[1006],[2205,33033],{"alt":33034,"src":33035},"png;base6475ef3cbae089d72b","https://media.synyx.de/uploads//2016/06/pngbase6475ef3cbae089d72b-300x212.png",[439,33037,33038],{},"Zum Letzen Punkt wurde noch eine Anmerkung gemacht, dass solche Test sinnvoller Weise nur während den Geschäftszeiten\ngemacht werden sollen. Der Grund ist einleuchtend:",[11947,33040,33041],{},[439,33042,33043],{},[990,33044,33045],{},"“Its a bad thing if your services are failing and your team is sleeping or drunk or both”",[439,33047,33048,33049,33054,33055,33060,33061,33066,33067,33072],{},"Auch für die Container-Freunde gab es etwas zu hören. ",[1002,33050,33053],{"href":33051,"rel":33052},"https://twitter.com/saturnism",[1006],"Ray Tsang"," von Google\nund ",[1002,33056,33059],{"href":33057,"rel":33058},"https://twitter.com/ArjenWassink",[1006],"Arjen Wassink"," hielten einen\ngemeinsamen ",[1002,33062,33065],{"href":33063,"rel":33064},"http://gotocon.com/amsterdam-2016/presentation/Java-Based%20Microservices,%20Containers,%20Kubernetes%20-%20How%20To",[1006],"Vortrag","\nindem eine Java Anwendung auf einem Kubernetes-Cluster mit Docker-Containern live deployt\nwurde. ",[1002,33068,33071],{"href":33069,"rel":33070},"http://kubernetes.io/",[1006],"Kubernetes"," ist ein Tool von Google, dass zur Verwaltung und Skalierung von Containern\nentwickelt wurde. Ray Tsang zeigte wie flexibel und doch einfach benutzbar Kubernetes ist. Als besonderer Effekt wurde\nder Kubernes-Kluster auf einem eigens mitgebrachten Datencenter betrieben, dass sich aus 5 Raspberry Pi zusammensetzte.\nHiermit sollte demonstriert werden, dass Kubernetes unabhängig von der Umgebung benutzt werden kann. Ob es sich dabei um\nGoogles Cloud Platform oder einen Raspberry Pi-Kluster handelt spielt keine Rolle.",[439,33074,33075,33076,33081,33082,33087],{},"Ein weiterer beeindruckender Vortrag gab es von ",[1002,33077,33080],{"href":33078,"rel":33079},"https://twitter.com/starbuxman",[1006],"Josh Long"," der mit seinem\nVortrag ",[1002,33083,33086],{"href":33084,"rel":33085},"http://gotocon.com/amsterdam-2016/presentation/Cloud%20Native%20Java",[1006],"Cloud Native Java"," neue\nGeschwindigkeitsrekorde aufstellte (sowohl auf der Tonspur als auch beim Live Coding). Die vorgestellten Projekte aus\ndem Spring Ökosystem zur Orchestrierung von Microservice-basierten Anwendungen wurden live an der Tastatur erarbeitet.\nDabei wurde ein Einblick geliefert wie eine mögliche Architektur einer Microservice-basierten Anwendung aussieht.\nAußerdem stellte er die unter dem Dach von Spring Cloud zusammengefassten Module vor. Dabei sei allerdings erwähnt, dass\nviele der Arbeiten durch Netflix veröffentlicht wurden und Einzug in das Ökosystem von Spring gehalten haben. Trotz der\nhohen Geschwindigkeit war auch noch Zeit für Humor:",[11947,33089,33090],{},[439,33091,33092],{},"“everytime you do field injection, a unit test dies.”",[439,33094,33095],{},"Neben vielen technischen Talks überzeugte die goto; Amsterdam Konferenz vor allem mit dem Philosophie Track.",[11947,33097,33098,33103],{},[439,33099,33100],{},[990,33101,33102],{},"“Jeder ist besser als ich. Ich bin nicht so gut wie andere denken und bald werden sie es herausfinden.”",[439,33104,33105],{},"(Imposter Syndrom)",[439,33107,33108,33113,33114,33119,33120,33125],{},[1002,33109,33112],{"href":33110,"rel":33111},"https://twitter.com/nativewired",[1006],"Gitte Klitgaard’s"," Vortrag über\ndas ",[1002,33115,33118],{"href":33116,"rel":33117},"https://de.wikipedia.org/wiki/Hochstapler-Syndrom",[1006],"Imposter Syndrom"," gilt es wohl hervorzuheben. Sie berichtete\nüber negative Konsequenzen wie Burn Out als auch über Chancen und positive Aspekte wie Individualität und die stetige\nSelbstverbesserung. Auch eine Folie spendiert bekam\nder ",[1002,33121,33124],{"href":33122,"rel":33123},"https://de.wikipedia.org/wiki/Dunning-Kruger-Effekt",[1006],"Dunning-Kruger-Effekt",", welcher die Selbstüberschätzung von\nAnfängern beschreibt. Traf einmal ein ein extrovertierte, seblbstbewusster Anfänger auf einen Experten mit Imposter\nSyndrom…Muss man mal drüber nachgedacht haben.",[439,33127,33128],{},[2205,33129],{"alt":33130,"src":33131},"dunning-kruger-effekt","https://web.archive.org/web/20160609113938if_/http://terrycolon.com/quotes/a-e/D-Keffect2.gif",[439,33133,33134],{},"Insgesamt eine runde Veranstaltung, welche durch viele spannende Vorträge in einer tollen Location und mit sehr gutem\nEssen glänzte.",[439,33136,33137],{},[1002,33138,33141],{"href":33139,"rel":33140},"https://media.synyx.de/uploads//2016/06/RYJ-8J34F.jpg",[1006],[2205,33142],{"alt":33143,"src":33144},"RYJ-8J34F","https://media.synyx.de/uploads//2016/06/RYJ-8J34F-1024x739.jpg",[439,33146,33147],{},"Eins ist gewiss wir kommen wieder! …und nicht bloß wegen dem verpassten Hering 😉",{"title":469,"searchDepth":507,"depth":507,"links":33149},[33150,33151,33152],{"id":32817,"depth":507,"text":32818},{"id":32849,"depth":507,"text":32850},{"id":32911,"depth":507,"text":32912},[1030],"2016-06-23T09:37:39","Vom 13. bis 15.06.2016 waren wir zu siebt in Amsterdam auf der goto; Amsterdam.\\nZunächst gibt es einen kleinen Reisebericht zu lesen auf den dann ein paar Impressionen aus den einzelnen Sessions und\\nTalks folgen. Wer direkt zu den inhaltlichen Schwerpunkten unserer Reise springen möchte bitte hier entlang.","https://synyx.de/blog/synyx-goto-amsterdam/",{},"/blog/synyx-goto-amsterdam",{"title":32795,"description":33160},"Vom 13. bis 15.06.2016 waren wir zu siebt in Amsterdam auf der goto; Amsterdam.\nZunächst gibt es einen kleinen Reisebericht zu lesen auf den dann ein paar Impressionen aus den einzelnen Sessions und\nTalks folgen. Wer direkt zu den inhaltlichen Schwerpunkten unserer Reise springen möchte bitte hier entlang.","blog/synyx-goto-amsterdam",[33163,33164,9556],"amsterdam","goto","Vom 13. bis 15.06.2016 waren wir zu siebt in Amsterdam auf der goto; Amsterdam. Zunächst gibt es einen kleinen Reisebericht zu lesen auf den dann ein paar Impressionen aus den…","_FazutC6O7cPTIwB7yc409okbswY2vWUT4JbtYMNTxQ",{"id":33168,"title":33169,"author":33170,"body":33171,"category":33276,"date":33277,"description":33178,"extension":1034,"link":33278,"meta":33279,"navigation":916,"path":33280,"seo":33281,"slug":33175,"stem":33282,"tags":33283,"teaser":33284,"__hash__":33285},"blog/blog/werte-diskurs-bei-synyx.md","Werte-Diskurs bei synyx!",[244],{"type":432,"value":33172,"toc":33274},[33173,33176,33179,33182,33187,33190,33201,33206,33209,33212,33218,33221,33224,33227,33230,33233,33236,33239,33248,33257],[435,33174,33169],{"id":33175},"werte-diskurs-bei-synyx",[439,33177,33178],{},"Klingt langweilig und abgedroschen? Nicht bei uns!",[439,33180,33181],{},"Gegen Ende des letzten Jahres entfachte sich eine spannende Diskussion über Werte. Die Idee dahinter: ein gemeinsames\nWertebild für unser Unternehmen zu finden. Denn synyx ist die letzten Jahre kontinuierlich gewachsen, weswegen sich die\nalten Unternehmenswerte verändert haben. Es war uns wichtig zusammen unsere Werte zu reflektieren.",[439,33183,33184],{},[448,33185,33186],{},"Doch wofür diesen ganzen Aufwand?",[439,33188,33189],{},"Dafür gibt es verschiedene Gründe:",[8310,33191,33192,33195,33198],{},[997,33193,33194],{},"In der täglichen Arbeit miteinander werden zahlreiche Entscheidungen getroffen. Die Grundlage hierfür sind konkrete\nWerte. Müssen wir eine unternehmerische Entscheidung für oder gegen einen Kunden treffen, prüfen wir individuell, ob\nder Kunde in unser Wertesystem passt. Da uns beispielsweise soziale Verantwortung am Herzen liegt, arbeiten wir nicht\nmit Waffenunternehmen zusammen. Mit Hilfe der Wertediskussion wird Transparenz in solchen Entscheidungsprozesse\ngeschaffen.",[997,33196,33197],{},"Aber nicht nur bei der Auswahl neuer Kunden, sondern auch neuer Mitarbeiter stellt sich die Frage, ob diese mit ihren\nWerten zu unseren Werten passen. Infolge sehr unterschiedlicher Werte kann es zu Konflikten innerhalb des\nUnternehmens kommen.",[997,33199,33200],{},"Die Annahme, dass Unternehmenswerte einmal formuliert für immer so bleiben, widerspricht unserer Erfahrung. Mit der\nZeit wandeln sich aufgrund verschiedener Einflüsse diese Werte. Die Frage, was dieser Wandel der Werte für uns\nbedeutet, ob er gut oder schlecht ist, beleuchten wir in regelmäßigen Abständen. Denn nur dadurch ist es uns möglich,\nproaktiv den Kurs zu verändern. Wenn wir diese Tatsache jedoch missachten, laufen wir Gefahr, dass sich Werte\nverfestigen, die unserem Unternehmen schaden. Denn Werte entscheiden auch, wie wir uns untereinander Verhalten und\nwie wir interagieren. Die Summe dieser Verhalten spiegelt sich in der Firmenkultur wieder. So gesehen sind Werte das\nFundament jeder Kultur. Entsteht zum Beispiel eine Kultur der Unzufriedenheit, kann dies oft ein Zeichen dafür sein,\ndass sich Werte verändert haben.",[439,33202,33203],{},[448,33204,33205],{},"Der Wertediskurs im synyx-Style:",[439,33207,33208],{},"Typischerweise finden solche Diskurse hinter verschlossenen Manager-Türen statt. Das Ergebnis wird dann der ganzen\nBelegschaft präsentiert. Wir haben uns für einen anderen Weg entschieden. Einen Weg, der durch seine intensive und\nlebhafte Diskussion sehr zeitaufwändig ist. Denn eine Diskussion über Werte bietet eine gute Möglichkeit, dass sich alle\nbei synyx daran beteiligen können. Dadurch hat jeder die Chance zu einem gemeinsamen Wertebild zu stehen.",[439,33210,33211],{},"Genau dieses Vorgehen an den Wertediskurs zeigt einen Teil unserer Werte, die wir hier bei synyx leben: Individualität,\nMitbestimmung und Offenheit. Wir sind anders und stehen dazu. Uns ist es wichtig, dass jeder gehört werden kann. Dabei\nbildet eine offene und ehrliche Kommunikation den Grundstein unserer Zusammenarbeit.",[439,33213,33214,33215],{},"Nun die interessante Frage: ",[448,33216,33217],{},"Wie haben wir bei synyx den Wertediskurs geführt?",[439,33219,33220],{},"Ganz einfach: pragmatisch. Indem sich alle an einen Tisch setzten und miteinander diskutierten, konnte sich ein offener\nDiskussionsraum entfalten. Um den Rahmen nicht zu sprengen, wurde die Diskussion von Moderatoren begleitet. Für den\nDiskussionsinhalt durfte jeder Werte aufschreiben, die für ihn bei synyx wichtig sind. Diese Werte wurden währenddessen\ngesammelt. Da einige Werte ähnlich oder gleich waren, wurden sie anschließend geclustert. Um die Werte jedoch sinnvoll\nclustern zu können, musste die Bedeutung der jeweiligen Werte geklärt werden. Denn Werte wie Respekt, Eigenverantwortung\noder Engagement bieten viel Platz für Interpretation. Die Klärung der Bedeutung ist notwendig und herausfordernd\nzugleich. Denn erst dadurch werden aus abstrakten Werten ein greifbares und konkretes Werteverständnis. An dieser Stelle\nfindet bereits ein großer Teil der Wertschöpfung des Wertediskurses statt.",[439,33222,33223],{},"Als Ergebnis dieser mehrstündigen Diskussion entstanden aus über 100 Zetteln mit Werten 30 Werte-Cluster.",[439,33225,33226],{},"Einige Tage später setzten wir den Workshop fort. Im zweiten Teil fokussierten wir uns auf die 30 Cluster, um diese\n“einzudampfen”. Die Schwierigkeit dieser Arbeit bestand darin, auf einen Teil der Werte zu verzichten. Die Fähigkeit,\nsich gemeinschaftlich auf die wesentlichen Werte zu konzentrieren und zu einigen, bildete den Mehrwert dieser\nDiskussion. Am Ende kristallisierten sich neun Werte-Cluster aus. Diese neun Werte wurden anschließend zusammen\npriorisiert. Denn je nach Entscheidung, müssen Werte gegeneinander abgewogen werden. Zum Beispiel kann eine Entscheidung\nzu Gunsten des Wertes der Kundenzufriedenheit getroffen werden, jedoch kurzfristig die Mitarbeiterbegeisterung\neinschränken. Mit Hilfe einer Priorisierung wird Transparenz und Klarheit in diesen Prozess geschaffen.",[439,33228,33229],{},"Damit wertvolle Diskussionsergebnisse aus den Workshops nicht verloren gehen, wurden im letzten Schritt die Werte\nbeschrieben. Ebenfalls wurde für jeden Wert ein aussagekräftiger Satz formuliert, der den Wert greifbar und fühlbar\nmacht. Diese Werte sollen natürlich nicht in irgendwelchen Schubladen vergammeln, sondern für alle sichtbar sein.\nDeshalb werden wir sie an einer unserer Wände im Büro visualisieren lassen. Der erste Entwurf kann bei uns besichtigt\nwerden (siehe Fotos unten).",[439,33231,33232],{},"Komm vorbei und lass dich inspirieren 🙂",[439,33234,33235],{},"Wer sich für weiterführende Literatur interessiert:",[439,33237,33238],{},"Buch: Michael Loebbert, Kultur entscheidet: kulturelle Muster in Unternehmen erkennen und verändern, Springer Gabler\n2015",[439,33240,33241,33242,33247],{},"Paper: Dr. Bernhard v.\nGuretzky, ",[1002,33243,33246],{"href":33244,"rel":33245},"http://www.community-of-knowledge.de/fileadmin/user_upload/attachments/Werte_im_Unternehmen.pdf",[1006],"Werte im Unternehmen",",\n2006",[439,33249,33250,33251,33256],{},"Promotion: Sandra\nNiedermeier, ",[1002,33252,33255],{"href":33253,"rel":33254},"https://edoc.ub.uni-muenchen.de/17504/1/Niedermeier_Sandra.pdf",[1006],"Wertebildung im Unternehmen: Theoretische Grundlagen und Implementation",",\n2014",[439,33258,33259,8351,33267],{},[1002,33260,33263],{"href":33261,"rel":33262},"https://media.synyx.de/uploads//2016/06/werte_01.jpg",[1006],[2205,33264],{"alt":33265,"src":33266},"werte_01","https://media.synyx.de/uploads//2016/06/werte_01-225x300.jpg",[1002,33268,33271],{"href":33269,"rel":33270},"https://media.synyx.de/uploads//2016/06/werte.jpg",[1006],[2205,33272],{"alt":9159,"src":33273},"https://media.synyx.de/uploads//2016/06/werte-300x225.jpg",{"title":469,"searchDepth":507,"depth":507,"links":33275},[],[1030,11122],"2016-06-15T16:27:02","https://synyx.de/blog/werte-diskurs-bei-synyx/",{},"/blog/werte-diskurs-bei-synyx",{"title":33169,"description":33178},"blog/werte-diskurs-bei-synyx",[389,3593,9158,9160],"Klingt langweilig und abgedroschen? Nicht bei uns! Gegen Ende des letzten Jahres entfachte sich eine spannende Diskussion über Werte. Die Idee dahinter: ein gemeinsames Wertebild für unser Unternehmen zu finden.…","QOxnECn3qdaPpr0_nv5Es45kC3-YxwahqT5wYMt1mWU",{"id":33287,"title":33288,"author":33289,"body":33290,"category":33404,"date":33405,"description":33406,"extension":1034,"link":33407,"meta":33408,"navigation":916,"path":33409,"seo":33410,"slug":33294,"stem":33412,"tags":33413,"teaser":33414,"__hash__":33415},"blog/blog/synyx-at-the-opensource-datacenter-conference-2016-osdc.md","synyx at the OpenSource Datacenter Conference 2016 #OSDC",[208],{"type":432,"value":33291,"toc":33402},[33292,33295,33304,33307,33310,33313,33322,33331,33334,33343,33352,33355,33376,33385,33394],[435,33293,33288],{"id":33294},"synyx-at-the-opensource-datacenter-conference-2016-osdc",[439,33296,33297,33298,33303],{},"Last week we attended the **Open Source Datacenter Conference\n** #",[1002,33299,33302],{"href":33300,"rel":33301},"https://www.netways.de/en/events_trainings/osdc/program/",[1006],"OSDC"," 2016 in Berlin.",[439,33305,33306],{},"It offered great presentations about open source tools in relation to devops, automation, monitoring, communication,\nlogging, continuous delivery and more.",[439,33308,33309],{},"I especially liked that the speakers felt like attendees themselves, with all of them being happy to answer tons of\nquestions and openly discuss their (and other’s) topics and presentations, creating a great atmosphere that felt like\nworking with colleagues that have to solve the same issues and suffer the same pain 🙂",[439,33311,33312],{},"Some detail of the most interesting conference talks I heard and what I have taken from them:",[439,33314,33315,33316,33321],{},"Dawn Foster (",[1002,33317,33320],{"href":33318,"rel":33319},"https://twitter.com/geekygirldawn",[1006],"@geekygirldawn",") opened up the conference with her keynote on open\nsource culture and ways to participate, contribute and live it (AND pay the bills:-). It gave some good insight into\nwhat and who powers the open source ecosystem and why the concept works and develops.",[439,33323,33324,33325,33330],{},"Kris Buytaert (",[1002,33326,33329],{"href":33327,"rel":33328},"https://twitter.com/krisbuytaert",[1006],"@krisbuytaert",") talked about “Another 7 tools for your #devops\nstack”. Among some docker “love” we learned that (obviously) everyone else in operations suffers from DNS, too.",[439,33332,33333],{},"We were quite happy to hear that we already include most of the tools Kris mentioned in our stack, like Grafana and\nRundeck. Obviously it was very interesting learn about other peoples views and experience with them. We also found some\nnew fun tools to try and evaluate in the very near future, like Consul and Jitsi.",[439,33335,33336,33337,33342],{},"Jonathan Boulle (",[1002,33338,33341],{"href":33339,"rel":33340},"https://twitter.com/baronboulle",[1006],"@jonboulle",") presented the latest evolution in Rocket and container\nstandards. He showed a nice introduction to CoreOS, but the more interesting thing was his summary about appc and how\nthe CoreOS team works with Docker to create an industry standard for container management. Although I had not used rkt\nor docker before, I found the talk very interesting and understandable from an operations perspective.",[439,33344,33345,33346,33351],{},"Michael Prokop (",[1002,33347,33350],{"href":33348,"rel":33349},"https://twitter.com/mikagrml",[1006],"@mikagrml",") gave a talk about “Continuous Integration in Data Centers”,\ncomparing to his talk for #OSDC 2013.",[439,33353,33354],{},"The most important part I took from this talk was that you can’t have enough tests for everything, even your own\ntesting-stack and your whole CI chain. Mika also put great emphasis on codereview, not only to prevent mistakes, but\nmost importantly to share knowledge throughout your team and company. In a side note he mentioned goss, a very\nlightweight testing tool we will definitely evaluate soon.",[439,33356,33357,33358,33363,33364,33369,33370,33375],{},"Martin Alfke (",[1002,33359,33362],{"href":33360,"rel":33361},"https://twitter.com/tuxmea",[1006],"@tuxmea",") talked about “ChatOps”. I was really looking forward to this\npresentation, because not only do we emphasize a lot on chatops in the synyx admin team, but a coworker and I are about\nto give a ",[1002,33365,33368],{"href":33366,"rel":33367},"http://www.meetup.com/devops-karlsruhe/events/229579437/",[1006],"chatops-talk ourselves"," in a couple of weeks. It\nwas very interesting to see how Martin had prepared his talk, focusing mostly on the relation between communication and\ncollaboration. Following this talk i entered some great discussion about chatops, visibility and noise in chats in\ngeneral. We actually had this problem figured out (worksforus) before, so we cleaned up\nour ",[1002,33371,33374],{"href":33372,"rel":33373},"https://github.com/synyx/hubot-audit/blob/master/src/audit.coffee",[1006],"own hubot-interaction logger"," on the way home\nand published it the day after.",[439,33377,33378,33379,33384],{},"Pere Urbon-Bayes (",[1002,33380,33383],{"href":33381,"rel":33382},"https://twitter.com/purbon",[1006],"@purbon",") showed some ELKstack features in his talk about “Ingesting\nLogs with Style”. The whole talk was basically one big live-demo about analyzing his hobby, running. Starting with his\ndetermination not to give any cloud provider access to his personal training routines, data and routes he showed how to\nimport his running logs into ElasticSearch, then analyze and graph it with Kibana. This was quite an eye-opener showing\nthe power of modern tools, but also the amount of information you might upload to the vendor of your fitness wearable.",[439,33386,33387,33388,33393],{},"Colin Charles (",[1002,33389,33392],{"href":33390,"rel":33391},"https://twitter.com/bytebot",[1006],"@bytebot",") gave us some insight on “Tuning Linux for your Database”. I had\nto spend a lot of time in the past to troubleshoot and tune slow databases, so I was quite happy about this talk, and\nColin did not disappoint to show tools and approaches. The greatest eye-opener for me (in the entire conference) was\nhis statement, that using LVM-Snapshots cause up to 40% performance loss for the first snapshot, adding another 2-3%\nfor subsequent snapshots.",[439,33395,33396,33397,33401],{},"Thank you to all speakers, thank you synyx for sending me to this conference, thank you my lovely coworkers for\naccompanying me on the 7-hour trip and thanks to ",[1002,33398,33400],{"href":33300,"rel":33399},[1006],"netways","\nfor organizing OSDC 2016, see you in 2017!",{"title":469,"searchDepth":507,"depth":507,"links":33403},[],[9045,1412],"2016-05-02T15:09:39","Last week we attended the **Open Source Datacenter Conference\\n** #OSDC 2016 in Berlin.","https://synyx.de/blog/synyx-at-the-opensource-datacenter-conference-2016-osdc/",{},"/blog/synyx-at-the-opensource-datacenter-conference-2016-osdc",{"title":33288,"description":33411},"Last week we attended the **Open Source Datacenter Conference\n** #OSDC 2016 in Berlin.","blog/synyx-at-the-opensource-datacenter-conference-2016-osdc",[],"Last week we attended the Open Source Datacenter Conference #OSDC 2016 in Berlin. It offered great presentations about open source tools in relation to devops, automation, monitoring, communication, logging, continuous…","VBPTse6kl8J8B4AV_svCzHsnsxi2kHpvkyKbHb_fdKg",{"id":33417,"title":33418,"author":33419,"body":33420,"category":33554,"date":33555,"description":33556,"extension":1034,"link":33557,"meta":33558,"navigation":916,"path":33559,"seo":33560,"slug":33561,"stem":33562,"tags":33563,"teaser":33564,"__hash__":33565},"blog/blog/nachbericht-ueber-einen-inspirierenden-tag-beim-nachhaltigkeits-barcamp-in-karlsruhe.md","Ein inspirierender Tag beim Nachhaltigkeits BarCamp",[202],{"type":432,"value":33421,"toc":33552},[33422,33425,33428,33431,33441,33449,33458,33467,33476,33486,33495,33533,33543],[435,33423,33418],{"id":33424},"ein-inspirierender-tag-beim-nachhaltigkeits-barcamp",[439,33426,33427],{},"Am vergangenen Samstag, 09. April durfte ich das erste Nachhaltigkeits BarCamp in Karlsruhe, besuchen. Mit über 350\nAnmeldungen wurden die Erwartungen deutlich übertroffen. Trotz einiger, die es doch nicht geschafft haben, war das Camp\ninsgesamt sehr sehr gut besucht.",[439,33429,33430],{},"Nach einem lockeren Frühstück und ersten Networking-Gesprächen ging es dann mit dem Programm des BarCamps los. Direkt\nzu Beginn durften wie bei jedem BarCamp interessierte Teilnehmer für “Ihre” Themen / Vorträge / Diskussionen pitchen, um\ndann in einer nachgelagerten Abstimmungsrunde die Themen zu finden, welche in einen der 25 Zeitslots aufgenommen und\ndamit diskutiert werden.",[439,33432,33433],{},[1002,33434,33437],{"href":33435,"rel":33436},"https://twitter.com/synyx_ka/status/718782805157797888",[1006],[2205,33438],{"alt":33439,"src":33440},"So many sessions","https://media.synyx.de/uploads//2016/04/so_many_sessions-252x300.jpg",[439,33442,33443,33444,33448],{},"Aufgrund einer überragend hohen Anzahl an Themen wurde auch spontan noch ein weiterer Raum von\nder ",[1002,33445,18688],{"href":33446,"rel":33447},"https://twitter.com/karlshochschule",[1006]," organisiert, so dass es im Endeffekt sogar 30 verschiedene\nTalks waren, zwischen denen man in 5 Sessions über den Tag verteilt wählen konnte. Trotz alledem sind immer noch einige\nThemen nicht aufgenommen worden, was definitiv einen Bedarf an einem weiteren Nachhaltigkeits BarCamp in der Zukunft\nzeigt.",[439,33450,33451,33452,33457],{},"Bei so vielen Themen fällt es natürlich schwer, sich für einzelne Sessions zu entscheiden. Meine Wahl für den Start fiel\naufgrund des spannenden Titels “Unternehmensdemokratie – Eine Strategie sozial nachhaltiger Unternehmensführung?” auf\ndie Diskussionsrunde von ",[1002,33453,33456],{"href":33454,"rel":33455},"https://twitter.com/zeuch",[1006],"Dr. Andreas Zeuch",". Ich fand das Thema vor allem deswegen so\nspannend, da wir bei synyx ja seit langem versuchen ein Unternehmen mit zumindest ansatzweise basisdemokratischen\nPrinzipien zu führen und mich interessierte vor allem, was für Impulse hierzu von den anderen Teilnehmern kommen. Es war\ndann auch bei den 30+ Teilnehmern schnell eine hitzige Diskussion am laufen, und man hätte locker noch einige Stunden\nweiter machen können. Ich habe auf jeden Fall viele Spannende Eindrücke und Erfahrungsberichte für mich aus dieser\nDiskussion mitgenommen und für mich war die erste Session auch gleich das Highlight des Tages.",[439,33459,33460,33461,33466],{},"Im zweiten Slot widmete ich mich unter der Führung von ",[1002,33462,33465],{"href":33463,"rel":33464},"https://twitter.com/FrankWidmayer",[1006],"Frank Widmayer"," dem ebenso\nhochspannenden Thema “Organisationelle Energie”, welche sich mit einem wissenschaftlichen Ansatz zu persönlichen\nEnergie-Leveln im Beruf und dem notwendigen “Wiederaufladen” durch Freizeitaktivitäten bzw. durch Schlaf widmete. Es\nentstand schnell eine rege Diskussion mit den Teilnehmern über die Möglichkeiten, die persönlichen Energielevel von\nKollegen und Mitarbeitern zu erkennen und mit welchen Maßnahmen eine zu starke “Entladung” vorgebeugt werden kann, bevor\ndiese letztendlich im Burnout endet. Spannendes Thema, sehr interessant vorgetragen und auch hier wieder viel gelernt,\nund viele Impulse für die tägliche Arbeit mitgenommen.",[439,33468,33469,33470,33475],{},"Den dritten Slot habe ich dann mangels für mich wirklich spannenden Themen im Playspace der Karlshochschule beim\nwirklich leckeren veganen Mittagessen aus 100% biologisch abbaubaren Schälchen und mit Holzbesteck dem Networking\ngewidmet und einige interessante Gespräche geführt unter anderem mit Ama von ",[1002,33471,33474],{"href":33472,"rel":33473},"https://twitter.com/wearnepra",[1006],"Wear Nepra","\nüber die in Karlsruhe geborene Idee von nachhaltiger Sportbekleidung, die in Finnland dann wirklich Realität wurde.",[439,33477,33478],{},[1002,33479,33482],{"href":33480,"rel":33481},"https://media.synyx.de/uploads//2016/04/ncka16_collage-1.jpg",[1006],[2205,33483],{"alt":33484,"src":33485},"ncka16_collage","https://media.synyx.de/uploads//2016/04/ncka16_collage-1-209x300.jpg",[439,33487,33488,33489,33494],{},"Der vierte und für mich leider letzte Slot gestaltete nachhaltigen Raum\nmit ",[1002,33490,33493],{"href":33491,"rel":33492},"https://twitter.com/Susanne_Theisen",[1006],"Susanne Theisen",". Hierbei ging es um die Herausforderungen, Raum nachhaltig\nund vor allem für die darin befindlichen Personen angenehm zu benutzen. Spannendes Thema mit hochinteressanten Ausflügen\nin Farblehre und Feng Shui sowie ein psychologischer Exkurs in die Grundbedürfnisse von Menschen. Auch hier konnte ich\nsehr interessante Diskussionen und Anregungen mit nach Hause nehmen.",[439,33496,33497,33498,33503,33504,33509,33510,520,33515,33520,33521,33526,33527,33532],{},"Den fünften Slot musste ich leider aufgrund eines privaten Termins für mich ausfallen lassen, schaffte es aber zum Glück\nrechtzeit zur superspannenden ",[1002,33499,33502],{"href":33500,"rel":33501},"https://twitter.com/synyx_ka/status/718831542068461569",[1006],"Podiumsdiskussion"," zum\nThemenbereich Nachhaltigkeit mit einem überzeugenden Moderator ",[1002,33505,33508],{"href":33506,"rel":33507},"https://twitter.com/breitenbach",[1006],"Patrick Breitenbach",".\nMit diskutiert haben der Lebensmittelretter und\nKonsumverweigerer ",[1002,33511,33514],{"href":33512,"rel":33513},"https://twitter.com/raphaelfellmer",[1006],"Raphael Fellmer",[1002,33516,33519],{"href":33517,"rel":33518},"https://twitter.com/SchubertPanecka",[1006],"Dr. Katarzyna Schubert-Panecka","\nderen Vortrag zum Thema “Ökologie des Menschen” ich leider nicht besuchen konnte, mit der ich aber nach der\nPodiumsdiskussion noch ein längeres und sehr informatives Gespräch führen durfte, der Kopf des Premium Cola\nKollektivs ",[1002,33522,33525],{"href":33523,"rel":33524},"https://twitter.com/luebbermann",[1006],"Uwe Lübbermann",", der Professor für “International Sustainability\nManagement” an der Karlshochschule ",[1002,33528,33531],{"href":33529,"rel":33530},"https://twitter.com/andrereichel",[1006],"Dr. André Reichel",", sowie die bei Bosch in\nKarlsruhe für “Change Management & People Development” zuständige Isabell Mohr.",[439,33534,33535],{},[1002,33536,33539],{"href":33537,"rel":33538},"https://media.synyx.de/uploads//2016/04/barcamp_2016.png",[1006],[2205,33540],{"alt":33541,"src":33542},"Logo des Nachhaltigkeits BarCamp 2016","https://media.synyx.de/uploads//2016/04/barcamp_2016-300x155.png",[439,33544,33545,33546,33551],{},"Am Schluss gab es dann noch ein lockeres Zusammensein mit Networking, bei welchem sich noch lange und spannenden\nGespräche ergaben und sich das Gefühl dann verfestigte, dass hier in Karlsruhe wirklich sehr viel Interesse an\nNachhaltigkeit besteht, aber auch schon wahnsinnig viele Projekte laufen und auch schon zum Teil sehr erfolgreich sind.\nEin herzliches Dankeschön am Schluss daher an Elena Lenz von\nder ",[1002,33547,33550],{"href":33548,"rel":33549},"https://twitter.com/Fairantwortung",[1006],"Initiative Fairantwortung"," für die Organisation dieses rundum gelungenen\nBarCamps und ich freue mich schon auf’s nächste Jahr. (Und synyx ist selbstverständlich dann auch wieder als Sponsor\ndabei!)",{"title":469,"searchDepth":507,"depth":507,"links":33553},[],[1031],"2016-04-12T16:38:47","Am vergangenen Samstag, 09. April durfte ich das erste Nachhaltigkeits BarCamp in Karlsruhe, besuchen. Mit über 350\\nAnmeldungen wurden die Erwartungen deutlich übertroffen. Trotz einiger, die es doch nicht geschafft haben, war das Camp\\ninsgesamt sehr sehr gut besucht.","https://synyx.de/blog/nachbericht-ueber-einen-inspirierenden-tag-beim-nachhaltigkeits-barcamp-in-karlsruhe/",{},"/blog/nachbericht-ueber-einen-inspirierenden-tag-beim-nachhaltigkeits-barcamp-in-karlsruhe",{"title":33418,"description":33427},"nachbericht-ueber-einen-inspirierenden-tag-beim-nachhaltigkeits-barcamp-in-karlsruhe","blog/nachbericht-ueber-einen-inspirierenden-tag-beim-nachhaltigkeits-barcamp-in-karlsruhe",[],"Am vergangenen Samstag, 09. April durfte ich das erste Nachhaltigkeits BarCamp in Karlsruhe, besuchen. Mit über 350 Anmeldungen wurden die Erwartungen deutlich übertroffen. Trotz einiger, die es doch nicht geschafft…","5f_QzYngN9claIsTM8Gd3k56KCnYYl558oL7KlOW1G0",{"id":33567,"title":33568,"author":33569,"body":33570,"category":33636,"date":33637,"description":33638,"extension":1034,"link":33639,"meta":33640,"navigation":916,"path":33641,"seo":33642,"slug":33644,"stem":33645,"tags":33646,"teaser":33649,"__hash__":33650},"blog/blog/our-days-parallel-2016.md","Our days @ para//el 2016",[42],{"type":432,"value":33571,"toc":33634},[33572,33575,33590,33599,33608,33611,33616,33625,33628,33631],[435,33573,33568],{"id":33574},"our-days-parael-2016",[439,33576,33577,33578,33583,33584,33589],{},"Last week Stefan and me took part as guests at the ",[1002,33579,33582],{"href":33580,"rel":33581},"http://parallelcon.de/",[1006],"para//el conference"," in Heidelberg. The\nactual ",[1002,33585,33588],{"href":33586,"rel":33587},"http://parallelcon.de/programm.php",[1006],"program"," was separated into 2 keynotes, one per day, and 36 talks, 18 per\nday, three at a time (in parallel). Right at the beginning it was said, that we were two out of 150 participants, which,\nI would say, leads to a nice atmosphere. It wasn’t difficult to attend a discussion or meet other people during the\npauses and I haven’t further experienced, quite contrary to other conferences, any large queueing at launch. Why? Well,\nsomeone encountered that a table, if placed correctly, offers space for more than one line at the buffet, which is quite\na good news! 🙂",[439,33591,33592,33593,33598],{},"We’ve both really enjoyed the days viewed from that angle, so lets talk about the conference and the talks itself. It\nbecomes visible, that concurrency or parallism – a detail that people tend to interprete quite differently or have\ndifferent opionions about the exact meaning – is not considered something that is only subject to languages like C/C++\nor to specific topics of the IT like embedded programming or ",[1002,33594,33597],{"href":33595,"rel":33596},"https://en.wikipedia.org/wiki/Supercomputer",[1006],"HPC",", but\nrather something that can be discussed in a broader sense.",[439,33600,33601,33602,33607],{},"At synyx, we program most of the time in Java and talks had been scheduled very nicely without any or less overlap,\nwhich is true for other languages as well, but my point here is, that, concurrency on the JVM is getting more and more\nattention nowadays. When did you read the last time about the memory model of the JVM? But sometimes, it makes even more\nsense to listen to sessions, that do not cross your every-day-borders, just to see how problems get solved or pattern\napplied by different people or divisions. What’s a cache-line, volatile, atomic? What are the implications to\nmulti-core or multi-socket environments, and what if we put a JVM in between? A slight change to a system, for\ninstance, a change to the compiler, JVM or even a different CPU model might have an impact, due some underlying rules\nand optimization strategies, including hard and software. Visibility constraints might be a good keyword here and it was\nquite interesting to hear about so many different aspects again. The speakers did a good job to paint the picture with\nsmaller examples: a + b + c might not be c + b + a under certain circumstances. While this problem is not related to\nparallism at first, but rather to ",[1002,33603,33606],{"href":33604,"rel":33605},"https://en.wikipedia.org/wiki/Numerical_analysis",[1006],"numerical precision errors",", it\nmight be more visible to those environments and reveals the great spectrum of possible talks everyone could attend.",[439,33609,33610],{},"So, when I go through all of the sessions in my mind again, there was one challenging question to me, quite a bit\nphilosophic maybe, at least to me.",[439,33612,33613],{},[990,33614,33615],{},"In a concurrent world, how much precision would you be willing to relinquish, for a correct view of the shared world?",[439,33617,33618,33619,33624],{},"I would like to keep this open for now, you may want to think about it on your own, but you may also want to anticipate\nthe outer rim of the IT. I can really recommend the reading of ",[1002,33620,33623],{"href":33621,"rel":33622},"http://jcip.net/",[1006],"java concurrency in practice"," from\nGoetz Brian et. al to everyone who couldn’t join the para//el in 2016 and wants to know more about the concurrent world.\nThreads are not evil, if used with the right abstraction and pooling helps to minimize the initialization costs.",[439,33626,33627],{},"In general, there is only one thing that I’ve missed along the talks about performance, correctness and theory, which is\ntest. I’d really like to gain more insights about how people verify concurrent code. How fine grained should we\nformulate tests? What can we say about tools, software, patterns or even a simple setup? Might be even more complex for\nthe Embedded world.",[439,33629,33630],{},"Well, might be relevant for 2017, might not be relevant, we will see.",[439,33632,33633],{},"Thanks to everyone who made the conference to what we’ve seen in 2016!",{"title":469,"searchDepth":507,"depth":507,"links":33635},[],[1030],"2016-04-11T09:25:02","Last week Stefan and me took part as guests at the para//el conference in Heidelberg. The\\nactual program was separated into 2 keynotes, one per day, and 36 talks, 18 per\\nday, three at a time (in parallel). Right at the beginning it was said, that we were two out of 150 participants, which,\\nI would say, leads to a nice atmosphere. It wasn’t difficult to attend a discussion or meet other people during the\\npauses and I haven’t further experienced, quite contrary to other conferences, any large queueing at launch. Why? Well,\\nsomeone encountered that a table, if placed correctly, offers space for more than one line at the buffet, which is quite\\na good news! 🙂","https://synyx.de/blog/our-days-parallel-2016/",{},"/blog/our-days-parallel-2016",{"title":33568,"description":33643},"Last week Stefan and me took part as guests at the para//el conference in Heidelberg. The\nactual program was separated into 2 keynotes, one per day, and 36 talks, 18 per\nday, three at a time (in parallel). Right at the beginning it was said, that we were two out of 150 participants, which,\nI would say, leads to a nice atmosphere. It wasn’t difficult to attend a discussion or meet other people during the\npauses and I haven’t further experienced, quite contrary to other conferences, any large queueing at launch. Why? Well,\nsomeone encountered that a table, if placed correctly, offers space for more than one line at the buffet, which is quite\na good news! 🙂","our-days-parallel-2016","blog/our-days-parallel-2016",[33647,8276,711,33648],"concurrency","parallism","Last week Stefan and me took part as guests at the para//el conference in Heidelberg. The actual program was separated into 2 keynotes, one per day, and 36 talks, 18…","gVStre4dXOOMAefhjnopDpeh9ljMwq_ok3cq7NrRQKk",{"id":33652,"title":33653,"author":33654,"body":33655,"category":34059,"date":34060,"description":34061,"extension":1034,"link":34062,"meta":34063,"navigation":916,"path":34064,"seo":34065,"slug":34067,"stem":34068,"tags":34069,"teaser":34073,"__hash__":34074},"blog/blog/springboot-reactjs-progressive-enhancement-based-on-list-sorting.md","springboot & reactjs #2 | progressive enhancement based on list sorting",[335],{"type":432,"value":33656,"toc":34051},[33657,33660,33669,33672,33674,33678,33723,33725,33728,33731,33736,33743,33746,33749,33756,33778,33789,33792,33799,33806,33809,33816,33835,33841,33844,33847,33850,33864,33867,33890,33904,33907,33917,33923,33930,33933,33936,33939,33953,33972,33975,33984,34000,34004,34027,34033,34037,34044,34049],[435,33658,33653],{"id":33659},"springboot-reactjs-2-progressive-enhancement-based-on-list-sorting",[439,33661,33662,33663,33668],{},"This is the second article of a springboot & reactjs article series about server side rendering and progressive\nenhancement. In the ",[1002,33664,33667],{"href":33665,"rel":33666},"https://synyx.de/2016/03/universal-webapp-development-with-spring-boot-react/",[1006],"first article"," we\nhave learned how to render a ReactJS app on the server with nashorn. However, actually it is not really an “app” yet.\nCurrently we just see a static list of awesome products…",[439,33670,33671],{},"Today we will implement the sorting feature that should work with a plain html form submit as well as with a Ajax\nRequest and client side rendering. So the app is progressively enhanced with JavaScript \\o/",[17894,33673],{},[3938,33675,33677],{"id":33676},"springboot-reactjs-article-series","springboot & reactjs article series",[8310,33679,33680,33688,33717,33720],{},[997,33681,33682,33687],{},[1002,33683,33686],{"href":33684,"rel":33685},"https://synyx.de/2016/03/springboot-reactjs-server-side-rendering",[1006],"server side rendering"," ✅",[997,33689,33690,33691],{},"progressive enhancement based on list sorting 🆕\n",[994,33692,33693,33699,33705,33711],{},[997,33694,33695],{},[1002,33696,33698],{"href":33697},"#html-form-and-server-side-rendering","HTML form and server side rendering",[997,33700,33701],{},[1002,33702,33704],{"href":33703},"#enhance-the-client","Enhance the client",[997,33706,33707],{},[1002,33708,33710],{"href":33709},"#make-the-back-button-work-again","Make the back button work again",[997,33712,33713],{},[1002,33714,33716],{"href":33715},"#what-do-we-have-learned-so-far","What do we have learned so far",[997,33718,33719],{},"improving developer experience",[997,33721,33722],{},"lessons learned",[17894,33724],{},[439,33726,33727],{},"While the JavaScript solution with client side rendering results in faster rendering and therefore a better user\nexperience, it also has it’s costs. We have to manage the browser url by ourselves. Furthermore we have to implement the\nbrowsers back button feature /o\\",[439,33729,33730],{},"But let’s take one step after another…",[439,33732,33733],{},[448,33734,33735],{},"tl;dr",[439,33737,33738],{},[1002,33739,33742],{"href":33740,"rel":33741},"https://github.com/synyx/springboot-reactjs-demo",[1006],"project source code is available on github",[3938,33744,33698],{"id":33745},"html-form-and-server-side-rendering",[439,33747,33748],{},"Before we can even start thinking about the back button we have to implement the sorting feature. So we create a plain\nhtml form first that fires a good old get request.",[439,33750,33751,33752,33755],{},"The ProductFilterItem is a simple input field of type radio(button). For the moment the sorting should only consider one\nattribute. Therefore a radiobutton group is the way to go. So every input is defined with ",[471,33753,33754],{},"name=\"sort\""," and a label to\nincrease the clickable area.",[439,33757,33758,33759,33762,33763,33766,33767,33770,33771,33773,33774,33777],{},"In the previous blog the ",[448,33760,33761],{},"ProductList"," was the only component to render and therefore the entry in ",[448,33764,33765],{},"main.js",". But\nthe new ",[448,33768,33769],{},"ProductFilter"," is not part of the list. The ",[448,33772,33761],{}," reacts to the filter parameter set by the user.\nSo we have to create an ",[448,33775,33776],{},"App"," container that combines our awesome ProductList and ProductFilter components.",[439,33779,33780,33781,33784,33785,33788],{},"Since we have a container now to combine the ProductList and the ProductFilter components we also have to adjust the\n",[471,33782,33783],{},"global.renderServer"," function. First we add a second parameter ",[471,33786,33787],{},"sortBy"," to be able to render the selected radio button.\nThen we must render the new App container instead of the plain ProductList.",[439,33790,33791],{},"That’s it for the frontend part!",[439,33793,33794,33795,33798],{},"Next we need to extend the backend controller to process the ",[471,33796,33797],{},"sort"," request parameter defined in the ProductFilter form\nand use it to sort the product list.",[439,33800,33801,33802,33805],{},"Additionally the ",[471,33803,33804],{},"React#renderProducts"," method must be extended, too, of course.",[439,33807,33808],{},"And that’s it with the backend part as well!",[439,33810,33811,33812,33815],{},"Now build the frontend, start the spring boot app, open your browser on ",[471,33813,33814],{},"http://localhost:8080"," and start sorting the\nawesome product list 🙂",[464,33817,33819],{"className":16895,"code":33818,"language":16897,"meta":469,"style":469},"\n$ npm run build\n$ ./gradlew bootRun\n\n",[471,33820,33821,33825,33830],{"__ignoreMap":469},[474,33822,33823],{"class":476,"line":477},[474,33824,917],{"emptyLinePlaceholder":916},[474,33826,33827],{"class":476,"line":507},[474,33828,33829],{},"$ npm run build\n",[474,33831,33832],{"class":476,"line":547},[474,33833,33834],{},"$ ./gradlew bootRun\n",[439,33836,33837],{},[2205,33838],{"alt":33839,"src":33840},"awesome_productlist_sorting","https://media.synyx.de/uploads//2016/04/awesome_productlist_sorting.gif",[3938,33842,33704],{"id":33843},"enhance-the-client",[439,33845,33846],{},"So far our awesome product list is fully functional. Let’s recap what we can do now.",[439,33848,33849],{},"We are able to:",[994,33851,33852,33855,33858,33861],{},[997,33853,33854],{},"see the awesome product info",[997,33856,33857],{},"sort the awesome products by name or price",[997,33859,33860],{},"use the browser’s back and forward button (static site!)",[997,33862,33863],{},"bookmark every single view",[439,33865,33866],{},"As the next step we want to increase the user experience with AJAX requests and client side rendering. This results in a\nmuch quicker feedback for the user as requesting the whole html document.",[439,33868,33869,33870,33874,33875,33879,33880,4715,33883,520,33886,33889],{},"To fetch data dynamically from the server we have to prevent the native form submit and take over the control by\nourselves. React provides [lifecycle methods](",[1002,33871,33872],{"href":33872,"rel":33873},"http://git@gitlab-test.synyx.coffee",[1006],":\nseber/SynyxBibliothek.git ",[1002,33876,33877],{"href":33877,"rel":33878},"https://facebook.github.io/react/docs/component-specs.html#lifecycle-methods",[1006],") like ",[471,33881,33882],{},"onClick",[471,33884,33885],{},"onChange",[471,33887,33888],{},"onSubmit",", etc. So we simply have to register a handler for the form submit. The handler does nothing but\nto prevent the native behaviour and to inform the consumer of the ProductFilter about the submit.",[439,33891,33892,33893,33895,33896,33898,33899,18773],{},"You may ask why we are subscribing our submit handler via ",[471,33894,33888],{}," on the form and not the ",[471,33897,33882],{}," hook on the\nsubmit button. Well, actually we could listen to the button click. But then we had to keep track of all form data by\nourselves… And the html form element already provides all this data as\na ",[1002,33900,33903],{"href":33901,"rel":33902},"https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements",[1006],"HTMLFormControlsCollection",[439,33905,33906],{},"Since we use Reacts API for DOM event handling, we have to render our awesome product list on the client, too. We only\nhave server side rendering at this moment, remember? 😉",[439,33908,33909,33910,33912,33913,33916],{},"So additionally to the ",[471,33911,33783],{}," function used by Nashorn we need a second function ",[471,33914,33915],{},"window.renderClient","\nthat we have to call on the client side (browser) as we will see later in this tutorial.",[439,33918,33919,33920,33922],{},"Next we have to add the initial rendering to the index.html template. Of course, we must call ",[471,33921,33915],{}," with\nthe same data as on the server. However, React prints a nice error message on the browser console if the data differs (\nmeans the client side rendering would result in another DOM structure as the already existing one).",[439,33924,33925,33926,33929],{},"Back to the Java backend we have to inject the initial product list and the sortBy value into the server side model of\nthe ",[471,33927,33928],{},"ProductController.java"," class. Additionally we add a second endpoint to provide the sorted product list as json.",[439,33931,33932],{},"That’s it!",[439,33934,33935],{},"A form submit now fetches the minimal data from the server and the client takes care about the rendering. Awesome,\nright? If only this queasy feeling wouldn’t be there… Right… The browser url is not changing anymore /o\\ And if it\ncouldn’t be worse… Without url changing we also lost the power of the glorious back button.",[3938,33937,33710],{"id":33938},"make-the-back-button-work-again",[439,33940,33941,33942,33947,33948,1402],{},"Okay, at first we should face the browser url. With HTML5 we’ve gained\nthe ",[1002,33943,33946],{"href":33944,"rel":33945},"https://developer.mozilla.org/en-US/docs/Web/API/History_API",[1006],"window.history"," api which is supported by all modern\nbrowsers. Changing the url is as simple as pushing the new state into the history\nwith ",[1002,33949,33952],{"href":33950,"rel":33951},"https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries",[1006],"window.history.pushState",[439,33954,33955,33956,33959,33960,33963,33964,33967,33968,33971],{},"Next we want to listen to the browsers back and forward buttons. This can be implemented with subscribing to the\n",[471,33957,33958],{},"popstate"," event. The subscription is done within ",[471,33961,33962],{},"componentDidMount"," since we only want to subscribe in the browser\nenvironment. ",[471,33965,33966],{},"Constructor"," and it’s counterpart ",[471,33969,33970],{},"componentWillMount"," are both called on server side creating the static\nhtml markup.",[439,33973,33974],{},"Finally we made it 🙂",[439,33976,33977,33978,33983],{},"Go on, build the frontend, start the spring boot app, open ",[990,33979,33980],{},[1002,33981,33814],{"href":33814,"rel":33982},[1006]," and admire our awesome progressively\nenhanced product list. Functional without JavaScript and even better with enabled JavaScript.",[464,33985,33986],{"className":16895,"code":33818,"language":16897,"meta":469,"style":469},[471,33987,33988,33992,33996],{"__ignoreMap":469},[474,33989,33990],{"class":476,"line":477},[474,33991,917],{"emptyLinePlaceholder":916},[474,33993,33994],{"class":476,"line":507},[474,33995,33829],{},[474,33997,33998],{"class":476,"line":547},[474,33999,33834],{},[3938,34001,34003],{"id":34002},"what-do-we-have-learned-so-far","What do we have learned so far?",[994,34005,34006,34016,34024],{},[997,34007,34008,34009,34012,34013],{},"use plain HTML ",[471,34010,34011],{},"\u003Cform>"," element and enhance it with JavaScript and ",[471,34014,34015],{},"event.preventDefault",[997,34017,34018,34019,18175,34021,34023],{},"use ",[471,34020,33946],{},[471,34022,33958],{}," event to handle the browser back/forward button on the client",[997,34025,34026],{},"manually rebuilding and reloading the ReactJS app still sucks (autoreload would be cool, right)",[439,34028,34029],{},[2205,34030],{"alt":34031,"src":34032},"progressive_js","https://media.synyx.de/uploads//2016/04/progressive_js.gif",[3938,34034,34036],{"id":34035},"the-next-steps-will-be","The next steps will be",[994,34038,34039,34042],{},[997,34040,34041],{},"using webpack to enhance the developer experience",[997,34043,33722],{},[439,34045,34046],{},[448,34047,34048],{},"Stay tuned and keep learning!",[1024,34050,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":34052},[34053,34054,34055,34056,34057,34058],{"id":33676,"depth":507,"text":33677},{"id":33745,"depth":507,"text":33698},{"id":33843,"depth":507,"text":33704},{"id":33938,"depth":507,"text":33710},{"id":34002,"depth":507,"text":34003},{"id":34035,"depth":507,"text":34036},[1030],"2016-04-08T16:00:09","This is the second article of a springboot & reactjs article series about server side rendering and progressive\\nenhancement. In the first article we\\nhave learned how to render a ReactJS app on the server with nashorn. However, actually it is not really an “app” yet.\\nCurrently we just see a static list of awesome products…","https://synyx.de/blog/springboot-reactjs-progressive-enhancement-based-on-list-sorting/",{},"/blog/springboot-reactjs-progressive-enhancement-based-on-list-sorting",{"title":33653,"description":34066},"This is the second article of a springboot & reactjs article series about server side rendering and progressive\nenhancement. In the first article we\nhave learned how to render a ReactJS app on the server with nashorn. However, actually it is not really an “app” yet.\nCurrently we just see a static list of awesome products…","springboot-reactjs-progressive-enhancement-based-on-list-sorting","blog/springboot-reactjs-progressive-enhancement-based-on-list-sorting",[711,15201,34070,34071,1426,34072],"react","reactjs","springboot","This is the second article of a springboot & reactjs article series about server side rendering and progressive enhancement. In the first article we have learned how to render a…","Qu3p7HujGhE20FqRlTNddTQnUYreWnouc7NrqxyfhpM",{"id":34076,"title":34077,"author":34078,"body":34079,"category":34251,"date":34252,"description":34088,"extension":1034,"link":34253,"meta":34254,"navigation":916,"path":34255,"seo":34256,"slug":34257,"stem":34258,"tags":34259,"teaser":34265,"__hash__":34266},"blog/blog/anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-fuer-ein-resource-planning-board.md","Anwendungsprojekt der Hochschule Karlsruhe bei synyx: Prototyp für ein Resource Planning Board",[394],{"type":432,"value":34080,"toc":34249},[34081,34084,34089,34092,34097,34100,34103,34106,34111,34114,34124,34129,34135,34142,34148,34154,34160,34166,34172,34181,34186,34189,34192,34195,34200,34203,34206,34211,34214,34219,34222,34227,34230,34235,34240,34243,34246],[435,34082,34077],{"id":34083},"anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-für-ein-resource-planning-board",[439,34085,34086],{},[448,34087,34088],{},"Einleitung",[439,34090,34091],{},"In den letzten 4 Monaten hat ein kleines studentisches Team der Hochschule Karlsruhe für synyx im Rahmen eines\nAnwendungsprojekts die Entwicklung eines Prototyps für ein Resource Planning Board durchgeführt. Unsere Aufgabe war es,\neine auf Usability optimierte Ressourcenplanung zu entwickeln, welche die Projekt- und Mitarbeiterplanung innerhalb\neines Unternehmens leicht machen und optimieren soll. Im Folgenden ist ein kleiner Bericht über den Projektablauf und\ndie Zusammenarbeit mit synyx.",[439,34093,34094],{},[448,34095,34096],{},"Über das Anwendungsprojekt",[439,34098,34099],{},"Das Anwendungsprojekt der Hochschule ist ein Modul für Wirtschaftsinformatik-studenten/innen (B.Sc.), in dem jeder\nStudent mindestens 225 Stunden leisten soll. Das Projekt soll existierendes methodisches Wissen durch praktische Arbeit\nmit realen Kunden vertiefen.",[439,34101,34102],{},"Im Vordergrund soll ein Entwicklungsprozess für eine Entwicklergruppe (4-7 Personen) in einem realen Projekt von der\nAngebotserstellung bis zur Abnahme durch den Kunden durchlaufen werden. Für die Bearbeitung des Projekts wurde ein\niteratives Vorgehen gewählt. Die Studierenden nehmen dabei, über das Semester verteilt, unterschiedliche Rollen im\nProjektteam ein. Die Veranstaltung besteht aus Pflichtteilen und Projektarbeit, die von den Projektteams\neigenverantwortlich gestaltet wird. Die beteiligten Professoren/innen stehen den Projektteams als Coaches zur Seite.",[439,34104,34105],{},"Das Anwendungsprojekt fängt mit der Auswahl und Zuordnung von Studenten zu Projekten an. Jeder potentielle Kunde hält\neine kurze Präsentation an der Hochschule und erläutert die Ziele, die Herausforderungen des eigenen Projekts für die\nStudenten. Auch synyx war mit Christian Rückert vertreten und hat ihre Idee für ein Resource Planning Board vorgestellt.\nDanach konnten wir uns drei Projekte aussuchen, in denen wir gern mitarbeiten würden. Am Ende entschieden sich die\nProfessoren, welche Studenten welches Projekt bekommen. Ich freute mich sehr, als ich dem synyx-Projekt zugeordnet\nwurde, das war nämlich meine erste Wahl. Meine ersten Eindrücke von synyx nach der Präsentation waren ein kompetentes,\nerfahrenes Team, klare Ziele in dem Projekt und eine innovative Arbeitsatmosphäre.",[439,34107,34108],{},[448,34109,34110],{},"Das Projekt: Resource Planning Board",[439,34112,34113],{},"Das Resource Planning Board soll eine optimierte Ressourcenplanung ermöglichen. Die Bedienung der Webanwendung soll\ndabei sehr einfach, selbsterklärend und intuitiv von statten gehen. Projekte und Ressourcen sollen einfach angelegt und\nper Drag&Drop zugeordnet werden können. Nach der Zuordnung von Ressourcen und Projekten werden freie Kapazitäten\nerkannt und können verplant werden. Mit der Zuordnung werden bestimmte Ressourcen-, Projekt- und\nUnternehmenskennzahlen on-the-fly aktualisiert und man hat jederzeit alle wichtigen Daten direkt im Blick.",[439,34115,34116],{},[1002,34117,34120],{"href":34118,"rel":34119},"https://media.synyx.de/uploads//2016/03/screenshot.png",[1006],[2205,34121],{"alt":34122,"src":34123},"UI Resource Planning Board","https://media.synyx.de/uploads//2016/03/screenshot-1024x504.png",[439,34125,34126],{},[448,34127,34128],{},"Technisch haben wir folgende Frameworks eingesetzt:",[439,34130,34131,34132,34134],{},"Als Technologie im Backend wurde ",[990,34133,18586],{}," verwendet. Das Framework bietet durch einfache Konfiguration und gute\nIntegration vielerlei Komponenten die Möglichkeit, initiale Aufwände zu minimieren und somit den anfänglichen\nEntwicklungsprozess zu beschleunigen.",[439,34136,34137,34138,34141],{},"Als Datenbank wurde ",[990,34139,34140],{},"H2"," ausgewählt. Gegenüber großen DBMS wie Oracle oder SQL Server ist die H2-Datenbank\nleichtgewichtig und braucht wenige Ressourcen. Zudem kann sie in eine Spring Boot-Anwendung integriert werden, wodurch\ndie Installation, Konfiguration und Pflege einer externen Datenbank entfällt. H2 ist für die Entwicklung sehr gut\ngeeignet, da durchgeführte Datenbankoperationen wenig komplex sind und ausreichend gut unterstützt werden.",[439,34143,34144,34147],{},[990,34145,34146],{},"AngularJS"," ist ein von Google entwickeltes JavaScript-Framework, das die Entwicklung von Single-Page-Webanwendungen\nerleichtern soll. AngularJS setzt das in der Webentwicklung häufig vorkommende MVC-Pattern um, dessen Ziel es ist,\nDaten von ihrer Darstellung zu trennen. Änderungen der visuellen Darstellung haben einen unmittelbaren Einfluss auf die\nzugrunde liegenden Daten und umgekehrt. Darüber hinaus bietet Angular ein einfaches Templating an, wodurch aufwändige\nDOM-Manipulationen entfallen.",[439,34149,34150,34153],{},[990,34151,34152],{},"Dragula"," ist ein weiteres JavaScript-Framework, das die Entwicklung von Anwendungen mit Drag&Drop-Funktionalität\nstark erleichtert. Es bietet durch sein einfaches und verständliches, dennoch sauber umgesetztes Konzept die\nMöglichkeit, schnell und einfach grundlegende Drag&Drop-Funktionen zu integrieren und anschließend den Anforderungen\nanzupassen.",[439,34155,34156,34159],{},[990,34157,34158],{},"Bower"," wird benutzt, um Front-End-Abhängigkeiten für unsere Anwendung wie z.B. AngularJS flexibel laden zu können.",[439,34161,34162,34165],{},[990,34163,34164],{},"Grunt"," übernimmt viele Front-End-seitige Aufgaben, wie z.B. Tests ausführen, JavaScript-Dateien zusammenführen und\nkomprimieren oder LESS-Dateien in CSS konvertieren.",[439,34167,34168,34171],{},[990,34169,34170],{},"Karma"," ist ein Plug-In für Grunt, welches für das Ausführen von Tests verantwortlich ist.",[439,34173,34174,2040,34177,34180],{},[990,34175,34176],{},"JUnit",[990,34178,34179],{},"Jasmine"," sind etablierte Frameworks für Unit-Tests in Java bzw. JavaScript.",[439,34182,34183],{},[448,34184,34185],{},"Zusammenarbeit mit synyx",[439,34187,34188],{},"Während der letzten 4 Monate arbeiteten wir einmal in der Woche bei synyx im Büro. Neben der Büroinfrastruktur,\nGetränken und Goodies konnten wir Unterstützung auch in fachlichen Fragen durch synyx Mitarbeiter nutzen. Synyx\norganisierte für uns verschiedene Workshops im Bereich Technik, Usability und Design. Außerdem standen einige\nMitarbeiter von synyx auch bei individuellen Fragen zur Seite. Vor Allem möchte ich mich dafür bei David, Olle und\nKatharina bedanken.",[439,34190,34191],{},"Unserer Ansprechpartner, Christian Rückert spielte den Product Owner im Projekt. Er wirkte nicht nur bei der\nSprintplanung und den Sprintreviews mit sondern er gab uns auch wertvolle Ratschläge und Hinweise auf das Vorgehen in\neinem realen Projekt und in der Zusammenarbeit mit Kunden.",[439,34193,34194],{},"Außerdem muss ich noch das freundliche Verhalten von allen synyx-Mitarbeiter erwähnen. Wir fühlten uns während der\nArbeit mit synyx mit den Mitarbeitern gleichgestellt und als Teil des richtigen synyx-Teams.",[439,34196,34197],{},[448,34198,34199],{},"Unsere Eindrücke vom AWP",[439,34201,34202],{},"Im Folgenden einige Gedanken aus unserem Erfahrungsbericht zum AWP:",[439,34204,34205],{},"Andre Eberhard:",[439,34207,34208],{},[990,34209,34210],{},"„Das Anwendungsprojekt hat die Möglichkeit geboten, einen Eindruck über den Verlauf eines realen Projektes zu erhalten.\nVor allem galt es, organisatorische und kommunikative Hürden zu bewältigen, was mit zunehmendem Projektfortschritt\nbesser gelang. Auch konnten Gruppenmitglieder ihre Kompetenzen durch die von ihnen gelösten Aufgaben und Probleme\nerweitern.“",[439,34212,34213],{},"Volkan Güllü:",[439,34215,34216],{},[990,34217,34218],{},"„Das Anwendungsprojekt hat viele spannende Aspekte geboten. Die realitätsnahen Rahmenbedingungen haben mir einen guten\nEinblick in einen Projektverlauf mit realen Kunden ermöglicht. Hierbei habe ich gelernt welche Schwierigkeiten auftreten\nkönnen bei der Kommunikation und Umsetzung von Anforderungen bzw. Kundenwünschen.“",[439,34220,34221],{},"Marvin Lackus:",[439,34223,34224],{},[990,34225,34226],{},"„Das Anwendungsprojekt hat mich unter vielen Aspekten vorangebracht. Vor allem technisch durch das eigenständige\nAufsetzen einer gesamten Infrastruktur und die Implementierung verschiedener Features, wobei unser persönlicher Coach\nDavid bei Fragen immer aufschlussreiche Antworten parat hatte…“",[439,34228,34229],{},"Tamara Tunczinger:",[439,34231,34232],{},[990,34233,34234],{},"„Viele “Kleinigkeiten” haben in meinem Auge eine größere Bedeutung erhalten, wie z.B. die Dokumentation der\nKundengespräche (Protokoll schreiben) oder die Dokumentation der eigenen Fortschritte in JIRA. Außerdem habe ich\ngelernt, dass es eine längere Zeit dauert, bis ein Projektteam richtig gut zusammenarbeiten kann und welche wichtige\nRolle Kommunikation in der Projektentwicklung spielt … Ich denke, jeder in dem Team konnte sich in verschiedenen\nBereichen weiterentwickeln, sowohl fachlich als auch sozial gesehen … jeder von uns kann etwas aus diesen Erfahrungen\nfür die Zukunft mitnehmen.“",[439,34236,34237],{},[448,34238,34239],{},"Schlusswort",[439,34241,34242],{},"Das Projekt bzw. die Zusammenarbeit mit synyx hat uns in vielen Aspekten vorangebracht. Sowohl technisch, als auch\norganisatorisch konnten wir unsere Kenntnisse durch die Erfahrungen im realitätsnahen Umfeld weiterentwickeln.",[439,34244,34245],{},"Die Ergebnisse wurden am Ende Februar bei synyx vorgestellt und anschließend mit einem angenehmen Grillabend gefeiert.",[439,34247,34248],{},"Wir möchten uns nochmal bei dem gesamten synyx-Team, vor allem bei Christian Rückert für die Zusammenarbeit und seine\nUnterstützung bedanken.",{"title":469,"searchDepth":507,"depth":507,"links":34250},[],[1031],"2016-03-23T15:45:32","https://synyx.de/blog/anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-fuer-ein-resource-planning-board/",{},"/blog/anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-fuer-ein-resource-planning-board",{"title":34077,"description":34088},"anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-fuer-ein-resource-planning-board","blog/anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-fuer-ein-resource-planning-board",[34260,34261,34262,34263,34264],"awp","hochschule-karlsruhe","projektmanagement","resource-planning-board","ressourcenplanung","Einleitung In den letzten 4 Monaten hat ein kleines studentisches Team der Hochschule Karlsruhe für synyx im Rahmen eines Anwendungsprojekts die Entwicklung eines Prototyps für ein Resource Planning Board durchgeführt.…","t-WFxbkCX6FBYH_FB-askFDpgDnLfaLsbO-XFxKvTMo",{"id":34268,"title":34269,"author":34270,"body":34271,"category":34284,"date":34285,"description":469,"extension":1034,"link":34286,"meta":34287,"navigation":916,"path":34288,"seo":34289,"slug":34275,"stem":34290,"tags":34291,"teaser":34293,"__hash__":34294},"blog/blog/devoxx4kids-meets-javaland.md","Devoxx4Kids meets JavaLand",[172],{"type":432,"value":34272,"toc":34282},[34273,34276],[435,34274,34269],{"id":34275},"devoxx4kids-meets-javaland",[439,34277,34278],{},[2205,34279],{"alt":34280,"src":34281},"Javaland4kids","https://media.synyx.de/uploads//2016/03/IMG_1212-185x300.jpg",{"title":469,"searchDepth":507,"depth":507,"links":34283},[],[1031],"2016-03-17T12:42:22","https://synyx.de/blog/devoxx4kids-meets-javaland/",{},"/blog/devoxx4kids-meets-javaland",{"title":34269,"description":469},"blog/devoxx4kids-meets-javaland",[4216,11539,26921,34292],"synyx-event","Zum zweiten mal fand im Rahmen der JavaLand Konferenz im Phantasialand Brühl die JavaLand4Kids statt. Wie auch letztes Jahr hat die Devoxx4Kids das Event tatkräftig mit Hardware und dem JumpingSumo…","9rLGVfPNE3tY0WqDCKeQRkOdWwGwomgofLGylz-h5b8",{"id":34296,"title":34297,"author":34298,"body":34299,"category":34881,"date":34882,"description":34883,"extension":1034,"link":34884,"meta":34885,"navigation":916,"path":34886,"seo":34887,"slug":34888,"stem":34889,"tags":34890,"teaser":34891,"__hash__":34892},"blog/blog/springboot-reactjs-server-side-rendering.md","springboot & reactjs #1 | server side rendering",[335],{"type":432,"value":34300,"toc":34874},[34301,34304,34307,34309,34311,34371,34373,34376,34379,34384,34387,34390,34393,34396,34399,34403,34408,34411,34414,34417,34425,34428,34431,34439,34445,34471,34474,34483,34489,34492,34495,34500,34507,34512,34527,34534,34545,34559,34563,34577,34581,34588,34603,34606,34609,34614,34629,34634,34641,34644,34668,34673,34683,34687,34708,34711,34713,34716,34729,34734,34737,34740,34743,34778,34789,34795,34800,34806,34809,34822,34828,34830,34852,34858,34860,34868,34872],[435,34302,34297],{"id":34303},"springboot-reactjs-1-server-side-rendering",[439,34305,34306],{},"This is the first article of a series about server side rendering and progressive enhancement. We will implement a\nproduct list that can be sorted by two parameters. Furthermore the app will be progressively enhanced, means the html\ndocument is rendered on the server and javascript will just enhance the app on the client if possible.",[17894,34308],{},[3938,34310,33677],{"id":33676},[8310,34312,34313,34359,34367,34369],{},[997,34314,34315,34316],{},"server side rendering ✅\n",[994,34317,34318,34324,34330,34336,34342,34348,34354],{},[997,34319,34320],{},[1002,34321,34323],{"href":34322},"#why-java","Why Java for the backend?",[997,34325,34326],{},[1002,34327,34329],{"href":34328},"#why-reactjs","Why ReactJS for the client?",[997,34331,34332],{},[1002,34333,34335],{"href":34334},"#springboot","Spring Boot Initializr",[997,34337,34338],{},[1002,34339,34341],{"href":34340},"#backend","Backend",[997,34343,34344],{},[1002,34345,34347],{"href":34346},"#frontend","Frontend",[997,34349,34350],{},[1002,34351,34353],{"href":34352},"#running-the-app","Running the app",[997,34355,34356],{},[1002,34357,33716],{"href":34358},"#what-we-have-learned-so-far",[997,34360,34361,34366],{},[1002,34362,34365],{"href":34363,"rel":34364},"https://synyx.de/2016/04/springboot-reactjs-progressive-enhancement-based-on-list-sorting/",[1006],"progressive enhancement based on list sorting","\n🆕",[997,34368,33719],{},[997,34370,33722],{},[17894,34372],{},[439,34374,34375],{},"Today we are going to create an awesome product list while using progressive enhancement to provide the best user\nexperience possible on each device. Progressive enhancement is a method in web development that sets focus on content\nfirst. The content will then be enhanced with dynamic features (JavaScript) and Layout (css). To provide content even\nwith disabled JavaScript the page has to be rendered on the server. For reasons (explained below) we use Java & spring\non the backend and ReactJS on the client side.",[439,34377,34378],{},"But why do we even want to make the effort of developing a universal web application? And even a progressive one? Of\ncourse this is overhead that must be handled and we have to implement and maintain more APIs as we will see later.\nHowever, the user experience is worth this effort in my opinion.",[439,34380,34381],{},[448,34382,34383],{},"Benefits of universal applications",[439,34385,34386],{},"thanks to server side rendered markup",[439,34388,34389],{},"– the app is fully functional from the start",[439,34391,34392],{},"– the app is usable instantly",[439,34394,34395],{},"JavaScript just enhances the features",[439,34397,34398],{},"– ajax calls without full page reloading are super fast",[439,34400,34401],{},[448,34402,33735],{},[439,34404,34405],{},[1002,34406,33742],{"href":33740,"rel":34407},[1006],[3938,34409,34323],{"id":34410},"why-java-for-the-backend",[439,34412,34413],{},"To start with our little project we first have to think about the technologies we want to use. At synyx we are mainly\nusing Java for the backend. And over the last year we have built up a large knowledge around the spring ecosystem as\nwell.",[439,34415,34416],{},"So to answer the question",[994,34418,34419,34422],{},[997,34420,34421],{},"team knowledge (Spring, …)",[997,34423,34424],{},"battle tested solutions (Spring Security, Spring MVC, …)",[3938,34426,34329],{"id":34427},"why-reactjs-for-the-client",[439,34429,34430],{},"Well, personally I am a fan of react and it’s ecosystem. That’s it! 😎",[439,34432,34433,34434,1402],{},"Okay… actually there are good reasons to use react. React provides a simple API to develop UI components and it works\nwell with reactive architectures like Flux and its\nsuccessor ",[1002,34435,34438],{"href":34436,"rel":34437},"https://web.archive.org/web/20160411023634/https://egghead.io/series/getting-started-with-redux",[1006],"redux",[439,34440,34441,34442],{},"Furthermore it supports server side rendering quite well. ",[990,34443,34444],{},"(afaik Angular 2 and Ember could also be used, or cyclejs,\nor …)",[11947,34446,34447,34452],{},[439,34448,34449],{},[448,34450,34451],{},"Disclaimer",[439,34453,34454,34455,34460,34461,34465,34466,34470],{},"I will not explain spring, react or $technology. Nor I will explain Java or JavaScript, es2015 syntax in particular.\nThere already are excellent articles out there on the web\nlike ",[1002,34456,34459],{"href":34457,"rel":34458},"http://javascriptplayground.com/blog/2016/02/the-react-webpack-tooling-problem",[1006],"how to start with react"," (\nwithout\nusing webpack). Additionally I recommend to have a look at the official documentation\nfor ",[1002,34462,34070],{"href":34463,"rel":34464},"https://facebook.github.io/react/docs/thinking-in-react.html",[1006]," as well as ",[1002,34467,1426],{"href":34468,"rel":34469},"https://spring.io/docs",[1006],"\nof\ncourse.",[435,34472,34335],{"id":34473},"spring-boot-initializr",[439,34475,34476,34477,34482],{},"At first we’re going to generate a bootstrap project with the awesome ",[1002,34478,34481],{"href":34479,"rel":34480},"https://start.spring.io",[1006],"spring starter"," web\ninterface. We use gradle as build system, thymeleaf as template engine to render our views on the server and good old\nspring-web. Handlebars would also be an option as template engine but it is not supported by spring initializr.",[439,34484,34485],{},[2205,34486],{"alt":34487,"src":34488},"springboot-initializr","https://media.synyx.de/uploads//2016/02/springboot-initializer.png",[435,34490,34341],{"id":34491},"backend",[439,34493,34494],{},"Starting with the backend we have to create the following files.",[439,34496,34497],{},[448,34498,34499],{},"index.html",[439,34501,34502,34503,34506],{},"The html template is as simple as it could be. We just need a div that acts as container for our ReactJS app.\n",[471,34504,34505],{},"th:utext=\"${content}\""," is thymeleaf specific and injects the content attribute of the view model as unescaped string.",[439,34508,34509],{},[448,34510,34511],{},"React.java",[439,34513,34514,34515,34518,34519,34522,34523,34526],{},"In React.java we are going to instantiate the Nashorn engine to be able to interpret JavaScript code which we will add\nlater. Nashorn is just a runtime environment for JavaScript on the JVM. It neither provides a ",[471,34516,34517],{},"window"," object nor a\n",[471,34520,34521],{},"console"," for logging. But since the latter one is required by ReactJS we have to load a ",[471,34524,34525],{},"nashorn-polyfill.js"," file\nbefore anything else.",[439,34528,34529,34530,34533],{},"We are loading our JavaScript sources into Nashorn with ",[471,34531,34532],{},"nashornScriptEngine.eval (\"load ('...')\")",". This is the same as\nincluding a script tag in a html document.",[439,34535,34536,34537,34540,34541,34544],{},"However, we could also call ",[471,34538,34539],{},"nashorn.eval (new InputStreamReader (...))"," to load the JavaScript files instead of using\nthe Nashorn specific ",[990,34542,34543],{},"load"," function. But we would lose the ability to debug the JavaScript code while running in\nNashorn (at least with IntelliJ). Which could be… useful 😉",[439,34546,34547,34548,34550,34551,34554,34555,34558],{},"Furthermore we have to implement a method ",[990,34549,33804],{}," which will invoke a global ",[471,34552,34553],{},"renderServer"," function\ndefined in ",[990,34556,34557],{},"app.bundle.js"," to create the rendered html string.",[439,34560,34561],{},[448,34562,34525],{},[439,34564,34565,34566,34569,34570,34572,34573,34576],{},"The polyfill for nashorn has to define a ",[448,34567,34568],{},"global"," variable (for reasons I will explain later) and the already\nmentioned ",[448,34571,34521],{},". ",[990,34574,34575],{},"print"," is a Nashorn function that logs on stdout.",[439,34578,34579],{},[448,34580,33928],{},[439,34582,34583,34584,34587],{},"The ProductController is responsible for getting the products and for setting the rendered html string as the ",[471,34585,34586],{},"content","\nattribute of the view model.",[439,34589,34590,34591,34596,34597,34602],{},"Additionally we need\na ",[1002,34592,34595],{"href":34593,"rel":34594},"https://github.com/synyx/springboot-reactjs-demo/blob/031a52fee5cc49c91988227b6b29b9857e5fed86/src/main/java/de/synyx/tutorials/spring/reactjs/demo/product/Product.java",[1006],"Product.java","\nPOJO and\na ",[1002,34598,34601],{"href":34599,"rel":34600},"https://github.com/synyx/springboot-reactjs-demo/blob/031a52fee5cc49c91988227b6b29b9857e5fed86/src/main/java/de/synyx/tutorials/spring/reactjs/demo/product/ProductRepository.java",[1006],"ProductRepository.java",".\nI think this is very straight forward and code snippets are obsolete here.",[435,34604,34347],{"id":34605},"frontend",[439,34607,34608],{},"With the backend part ready we can start with the frontend.",[439,34610,34611,34612,1402],{},"At first we have to do a small setup to enable es2015 compilation and module bundling with webpack. Webpack is actually\nnot required but eases our developer lives immensely. Babel-cli could also be used with small adjustments in\n",[990,34613,34511],{},[464,34615,34617],{"className":16895,"code":34616,"language":16897,"meta":469,"style":469},"$ npm init\n$ npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom\n\n",[471,34618,34619,34624],{"__ignoreMap":469},[474,34620,34621],{"class":476,"line":477},[474,34622,34623],{},"$ npm init\n",[474,34625,34626],{"class":476,"line":507},[474,34627,34628],{},"$ npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom\n",[439,34630,34631],{},[448,34632,34633],{},"webpack.config.js",[439,34635,34636,34637,34640],{},"Next we configure webpack to generate a bundle of our JavaScript files including the ReactJS library and our app\nbusiness logic. Please note ",[448,34638,34639],{},"output.filename"," which is the file loaded by React.java.",[439,34642,34643],{},"Webpack can then simply be used to create the bundle by a npm task.",[464,34645,34647],{"className":16895,"code":34646,"language":16897,"meta":469,"style":469},"// package.json\n\"scripts\": {\n \"build\": \"webpack\"\n}\n\n",[471,34648,34649,34654,34659,34664],{"__ignoreMap":469},[474,34650,34651],{"class":476,"line":477},[474,34652,34653],{},"// package.json\n",[474,34655,34656],{"class":476,"line":507},[474,34657,34658],{},"\"scripts\": {\n",[474,34660,34661],{"class":476,"line":547},[474,34662,34663],{}," \"build\": \"webpack\"\n",[474,34665,34666],{"class":476,"line":584},[474,34667,703],{},[439,34669,34670],{},[448,34671,34672],{},"ProducList.js",[439,34674,34675,34676,34678,34679,34682],{},"To start with the UI components we are going to create the ProductList as the main component. It will be, drum roll,\nresponsible for displaying a list of products which have a name and a price (remember ",[990,34677,34595],{},"?). Therefore we\nimplement a function that takes our products and returns the representational markup. To avoid\n",[471,34680,34681],{},"\"Cannot read property 'map' of undefined\""," type errors we simply assign an empty array to the products by default.",[439,34684,34685],{},[448,34686,33765],{},[439,34688,34689,34690,34692,34693,34695,34696,34698,34699,34701,34702,34704,34705,34707],{},"Next we need the entry point of our ReactJS app to define the ",[448,34691,34553],{}," function invoked by Nashorn. Remember the\n",[448,34694,34568],{}," variable set in ",[990,34697,34525],{},"? We use this variable now to “export” our ",[990,34700,34553],{}," function. If\nyou are familiar with the NodeJS environment, you already know that the ",[990,34703,34568],{}," object is the equivalent to the\n",[990,34706,34517],{}," object available in the browser. And Nashorn is our equivalent of NodeJS 😉",[435,34709,34353],{"id":34710},"running-the-app",[439,34712,33932],{},[439,34714,34715],{},"Now we can run our first universal server side rendered springboot application to admire our graceful product list. Go\non, run",[464,34717,34719],{"className":16895,"code":34718,"language":16897,"meta":469,"style":469},"$ npm run build\n$ ./gradlew bootRun\n",[471,34720,34721,34725],{"__ignoreMap":469},[474,34722,34723],{"class":476,"line":477},[474,34724,33829],{},[474,34726,34727],{"class":476,"line":507},[474,34728,33834],{},[439,34730,34731,34732,1402],{},"open your Browser and load ",[471,34733,33814],{},[439,34735,34736],{},"Just…",[439,34738,34739],{},"to see…",[439,34741,34742],{},"a wonderful stacktrace…",[464,34744,34746],{"className":16895,"code":34745,"language":16897,"meta":469,"style":469},"jdk.nashorn.internal.runtime.ECMAException: TypeError:\n[de.synyx...Product@553287f8, Product@65ae29e6] has no such function \"map\"\n at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:58) ~[nashorn.jar:na]\n at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:214) ~[nashorn.jar:na]\n at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:186) ~[nashorn.jar:na]\n at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:173) ~[nashorn.jar:na]\n\n",[471,34747,34748,34753,34758,34763,34768,34773],{"__ignoreMap":469},[474,34749,34750],{"class":476,"line":477},[474,34751,34752],{},"jdk.nashorn.internal.runtime.ECMAException: TypeError:\n",[474,34754,34755],{"class":476,"line":507},[474,34756,34757],{},"[de.synyx...Product@553287f8, Product@65ae29e6] has no such function \"map\"\n",[474,34759,34760],{"class":476,"line":547},[474,34761,34762],{}," at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:58) ~[nashorn.jar:na]\n",[474,34764,34765],{"class":476,"line":584},[474,34766,34767],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:214) ~[nashorn.jar:na]\n",[474,34769,34770],{"class":476,"line":607},[474,34771,34772],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:186) ~[nashorn.jar:na]\n",[474,34774,34775],{"class":476,"line":642},[474,34776,34777],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:173) ~[nashorn.jar:na]\n",[439,34779,34780,34781,34784,34785,34788],{},"The reason is that Nashorn interprets Java objects as, surprise, Java objects. As you remember, our ReactJS component\n",[471,34782,34783],{},"\u003CProductList />"," expects a list of products (actually a JavaScript array). But currently the type of products is a\n",[471,34786,34787],{},"java.util.List"," which doesn’t have the map method. Note the datatype of products in the image below.",[439,34790,34791],{},[2205,34792],{"alt":34793,"src":34794},"nashorn-debugging","https://media.synyx.de/uploads//2016/03/nashorn-debugging.png",[11947,34796,34797],{},[439,34798,34799],{},"“Given a Java array or Collection, this function returns a JavaScript array with a shallow copy of its contents”",[439,34801,34802,34803,34805],{},"So our renderServer function defined in ",[471,34804,33765],{}," must be extended to:",[439,34807,34808],{},"Now we’re ready to go 🙂",[439,34810,34811,34812,34815,34816,34821],{},"Rebuild the frontend with ",[471,34813,34814],{},"npm run build",", restart the Spring Boot application, reload ",[990,34817,34818],{},[1002,34819,33814],{"href":33814,"rel":34820},[1006]," and\nadmire our awesome product list.",[439,34823,34824],{},[2205,34825],{"alt":34826,"src":34827},"awesome-product-list-001","https://media.synyx.de/uploads//2016/03/awesome-product-list-001.png",[3938,34829,34003],{"id":34002},[994,34831,34832,34835,34841,34849],{},[997,34833,34834],{},"using Nashorn is no rocket science",[997,34836,34837,34838,34840],{},"load js files via ",[471,34839,34532],{}," to enable debugging (at least in IntelliJ)",[997,34842,34843,34845,34846],{},[990,34844,34787],{}," must be converted to JavaScript array with ",[471,34847,34848],{},"Java.from",[997,34850,34851],{},"manually rebuilding and reloading the ReactJS app sucks (autoreload would be cool, right)",[439,34853,34854],{},[2205,34855],{"alt":34856,"src":34857},"js-webpack-nashorn","https://media.synyx.de/uploads//2016/03/js-webpack-nashorn.png",[3938,34859,34036],{"id":34035},[994,34861,34862,34865],{},[997,34863,34864],{},"using webpack to enhance developer experience",[997,34866,34867],{},"implementing the sorting feature",[439,34869,34870],{},[448,34871,34048],{},[1024,34873,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":34875},[34876,34877,34878,34879,34880],{"id":33676,"depth":507,"text":33677},{"id":34410,"depth":507,"text":34323},{"id":34427,"depth":507,"text":34329},{"id":34002,"depth":507,"text":34003},{"id":34035,"depth":507,"text":34036},[1030],"2016-03-11T11:29:12","This is the first article of a series about server side rendering and progressive enhancement. We will implement a\\nproduct list that can be sorted by two parameters. Furthermore the app will be progressively enhanced, means the html\\ndocument is rendered on the server and javascript will just enhance the app on the client if possible.","https://synyx.de/blog/springboot-reactjs-server-side-rendering/",{},"/blog/springboot-reactjs-server-side-rendering",{"title":34297,"description":34306},"springboot-reactjs-server-side-rendering","blog/springboot-reactjs-server-side-rendering",[711,15201,34070,34071,1426,34072],"This is the first article of a series about server side rendering and progressive enhancement. We will implement a product list that can be sorted by two parameters. Furthermore the…","NgbG5R1prPK9EUbjPI1FYf4uauo9uWfYO5zNB_kSUvk",{"id":34894,"title":34895,"author":34896,"body":34897,"category":35132,"date":35133,"description":469,"extension":1034,"link":35134,"meta":35135,"navigation":916,"path":35136,"seo":35137,"slug":34901,"stem":35138,"tags":35139,"teaser":35142,"__hash__":35143},"blog/blog/nagios-benachrichtigungsmanagement.md","Nagios Benachrichtigungsmanagement",[184],{"type":432,"value":34898,"toc":35120},[34899,34902,34906,34909,34912,34916,34919,34922,34926,34929,34936,34939,34942,34950,34953,34955,34958,34964,34966,34969,34972,34979,34982,34985,34988,34991,34994,34997,35000,35003,35006,35013,35015,35018,35022,35025,35029,35032,35035,35038,35041,35044,35047,35056,35059,35068,35071,35080,35084,35087,35090,35094,35097,35100,35103,35107,35110,35114,35117],[435,34900,34895],{"id":34901},"nagios-benachrichtigungsmanagement",[3938,34903,34905],{"id":34904},"warum-wird-ein-benachrichtigungsmanagement-benötigt","Warum wird ein Benachrichtigungsmanagement benötigt?",[439,34907,34908],{},"Monitoring ist wichtig, aber nicht alle Monitoring-Tests sind (nur) für System Administratoren interessant. Oft\ninteressieren sich auch Entwickler zum Beispiel über den Zustand ihrer Anwendung und wollen darüber benachrichtigt\nwerden, falls diese ausfällt. Die Gruppenverwaltung über Nagios gestaltet sich allerdings kompliziert und wenig\nAnwenderfreundlich.",[439,34910,34911],{},"Außerdem nimmt die Zahl an Nagios Checks immer weiter zu, so dass ein einfacher E-Mail Versand bei einem\nfehlgeschlagenen Test zu einer Überflutung des E-Mail Postfaches führen kann. Eine Limitierung und Priorisierung von\nNachrichten ist hier hilfreich.",[3938,34913,34915],{"id":34914},"was-ist-mit-benachrichtigungsmanagement-gemeint","Was ist mit Benachrichtigungsmanagement gemeint?",[439,34917,34918],{},"Mit dem Benachrichtigungsmanagement ist eine Webanwendung gemeint, die es ermöglicht, aus einem Pool an Personen,\nGruppen zu erstellen.",[439,34920,34921],{},"Diese Gruppen können wiederum für bestimmte Nagios Checks registriert werden, über deren Ergebnis die Mitglieder der\nGruppe anschließend Benachrichtigt werden.",[3938,34923,34925],{"id":34924},"was-muss-an-nagios-vorgenommen-werden-um-das-benachrichtigungsmanagement-zu-verwenden","Was muss an Nagios vorgenommen werden, um das Benachrichtigungsmanagement zu verwenden?",[439,34927,34928],{},"Nagios wird zur Verwendung des Benachrichtigungsmanagements um zwei “commands” erweitert.",[439,34930,34931],{},[1002,34932,34935],{"href":34933,"rel":34934},"https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/objectdefinitions.html#command",[1006],"Siehe Nagios Dokumentation: Command Definition",[439,34937,34938],{},"`define command {",[439,34940,34941],{},"command_name service-notify-by-notification-management",[439,34943,34944,34945,34949],{},"command_line curl -X POST -H \"Content-Type:\napplication/json\" \"",[1002,34946,34947],{"href":34947,"rel":34948},"http://benachrichtigungs-management.synyx.de/notifications",[1006],"\" -d '{\"nagiosCheck\":{\"\nhostname\":\"$HOSTALIAS$\",\"servicename\":\"$SERVICEDESC$\"}, \"type\":\"$NOTIFICATIONTYPE$\", \"state\":\"$SERVICESTATE$\"}'",[439,34951,34952],{},"}`",[439,34954,34938],{},[439,34956,34957],{},"command_name host-notify-by-notification-management",[439,34959,34944,34960,34963],{},[1002,34961,34947],{"href":34947,"rel":34962},[1006],"\" -d '{\"nagiosCheck\":{\"\nhostname\":\"$HOSTNAME$\"}, \"type\": \"$HOSTSTATE$\"}'",[439,34965,34952],{},[439,34967,34968],{},"Diese sorgen dafür, dass das Ergebnis eines Nagios Checks per HTTP-POST an das Benachrichtigungsmanagement übertragen\nwird.",[439,34970,34971],{},"Nun muss noch ein “contact” definiert werden, der diese commands verwendet.",[439,34973,34974],{},[1002,34975,34978],{"href":34976,"rel":34977},"https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/objectdefinitions.html#contact",[1006],"Siehe Nagios Dokumentation: Contact Definition",[439,34980,34981],{},"`define contact {",[439,34983,34984],{},"contact_name notificationManagement",[439,34986,34987],{},"alias notificationManagement",[439,34989,34990],{},"service_notification_period 24x7",[439,34992,34993],{},"host_notification_period 24x7",[439,34995,34996],{},"service_notification_options w,u,c,r",[439,34998,34999],{},"host_notification_options d,r",[439,35001,35002],{},"service_notification_commands service-notify-by-notification-management",[439,35004,35005],{},"host_notification_commands host-notify-by-notification-management",[439,35007,35008,35009],{},"email ",[1002,35010,35012],{"href":35011},"mailto:admin@synyx.de","admin@synyx.de",[439,35014,34952],{},[439,35016,35017],{},"Wenn dieser contact für alle Nagios Checks registriert ist, sendet Nagios nun jedes Ergebnis per HTTP-POST an das\nBenachrichtigungsmanagement, in dem die Benachrichtigung weiter verarbeitet werden kann.",[3938,35019,35021],{"id":35020},"wie-funktioniert-dieses-benachrichtigungsmanagement","Wie funktioniert dieses Benachrichtigungsmanagement?",[439,35023,35024],{},"Das Prinzip des Benachrichtigungsmanagements ist, dass eine Benachrichtigung von Nagios empfangen und an interessierte\nPersonen unter Berücksichtigung von Limitierung und Priorisierung weitergeleitet wird. Es verfügt sowohl über eine\ngraphische Benutzeroberfläche als auch über eine RESTful API.",[1065,35026,35028],{"id":35027},"gruppenverwaltung","Gruppenverwaltung",[439,35030,35031],{},"Die Gruppenverwaltung ist derzeit so umgesetzt, dass alle Mitarbeiter aus einem LDAP abgefragt werden. Diese sind also\nder vorhin erwähnte Personen-Pool anhand derer Gruppen erstellt werden können. Zudem werden automatisch Gruppen anhand\nder LDAP-Gruppen erstellt.",[439,35033,35034],{},"Sollten diese nicht ausreichen, lassen sich weitere Gruppen über die API oder über die graphische Oberfläche bilden.",[439,35036,35037],{},"Diesen Gruppen können dann explizit Nagios Checks zugewiesen werden, so dass die Mitglieder der Gruppe über den Status\ndes Nagios Checks per E-Mail informiert werden.",[439,35039,35040],{},"Damit diese E-Mails nicht das E-Mail Postfach überfluten, existiert eine Möglichkeit, die Anzahl an E-Mails in einem\nselbst definierten Zeitintervall einzuschränken.",[439,35042,35043],{},"Dies ist ebenfalls, sowohl über die API als auch über die graphische Oberfläche möglich.",[439,35045,35046],{},"Um dies zu veranschaulichen, folgt nun ein Beispiel.",[439,35048,35049],{},[1002,35050,35053],{"href":35051,"rel":35052},"https://media.synyx.de/uploads//2016/03/NNM_Checks.png",[1006],[2205,35054],{"alt":35055,"src":35051},"NNM_Checks",[439,35057,35058],{},"Im Bild wird aus der Liste der verfügbaren Nagios Checks nach “Urlaub” gefiltert. Anschließend soll einem dieser Nagios\nChecks die Gruppe “Urlaubsverwaltung” zugeordnet werden, die über Änderungen am Zustand des Checks informiert werden\nsoll. Dies ist im nächsten Bild zu sehen.",[439,35060,35061],{},[1002,35062,35065],{"href":35063,"rel":35064},"https://media.synyx.de/uploads//2016/03/NNM_Map_UV.png",[1006],[2205,35066],{"alt":35067,"src":35063},"NNM_Map_UV",[439,35069,35070],{},"Sobald die Gruppe ausgewählt wurde, wird sie als zugeordnete Gruppe angezeigt. Alle Mitglieder der Gruppe\n“Urlaubsverwaltung” werden nun über Zustandsänderungen dieses Nagios Checks benachrichtigt.",[439,35072,35073],{},[1002,35074,35077],{"href":35075,"rel":35076},"https://media.synyx.de/uploads//2016/03/NNM_UV_Mapped.png",[1006],[2205,35078],{"alt":35079,"src":35075},"NNM_UV_Mapped",[1065,35081,35083],{"id":35082},"limitierung-von-nachrichten","Limitierung von Nachrichten",[439,35085,35086],{},"Die Limitierung von Nachrichten funktioniert, anhand einer nutzerspezifischen Konfiguration. Es besteht hier die\nMöglichkeit die Anzahl an E-Mails in einem selbst definierten Zeitintervall einzuschränken. Standardmäßig werden bis zu\n10 Benachrichtigungen in einem Zeitraum von 30 Minuten verschickt. Alle Benachrichtigungen, die über dieses Limit hinaus\neintreffen, werden in eine Queue geleitet, die alle 10 Sekunden überprüft wird.",[439,35088,35089],{},"Zu Berücksichtigen ist hierbei, dass Benachrichtigungen über ein positives Ergebnis eines Nagios Checks (‘RECOVERY’ oder\n‘UP’) nicht von dieser Limitierung betroffen sind. Wenn also ein Test in der Zwischenzeit wieder erfolgreich durchlaufen\nwerden konnte, wird darüber auf jeden Fall informiert. Ebenso zählen diese Erfolgsbenachrichtigungen nicht in das Limit\nder versandten Nachrichten hinein. Sollten noch Fehlerbenachrichtigungen (PROBLEM, DOWN) in der Queue sein, werden diese\nbei einer später eintreffenden Erfolgsbenachrichtigung aus der Queue entfernt.",[1065,35091,35093],{"id":35092},"priorisierung-von-nachrichten","Priorisierung von Nachrichten",[439,35095,35096],{},"Damit wichtige Benachrichtigungen nicht von unwichtigeren bereits versandten Benachrichtigungen blockiert werden,\nexistiert zudem eine Priorisierung von Benachrichtigungen basierend auf dem Host des Nagios Checks. Befindet sich beim\nAbarbeiten der Queue eine Nachricht mit höherer Priorität als eine der bereits versandten Benachrichtigungen, wird diese\ndennoch verschickt. Dabei wird die unwichtigste Benachrichtigung aus der List der bereits versandten Benachrichtigungen\nmit der Neuen ersetzt.",[439,35098,35099],{},"Zur Priorisierung werden alle zu überwachenden Hosts abgefragt und mit Tags versehen. Diese Tags sind selbst zu\nerstellen und mit einer Gewichtung zu versehen. Die Zuweisung der Tags funktioniert automatisch über selbst definierbare\nNamensregeln. So kann zum Beispiel jedem Host mit einem Hostnamen, der “-test” enthält das Tag “Testsystem” zugewiesen\nwerden. Jeder Host, der kein “-test” in einem Hostnamen hat wird dagegen mit dem Tag “Produktivsystem”, welches eine\nhöhere Gewichtung haben könnte, versehen.",[439,35101,35102],{},"Die Verwaltung von Tags und Regeln ist ebenfalls sowohl über die API als auch über die graphische Oberfläche möglich.",[1065,35104,35106],{"id":35105},"temporäres-deaktivieren-von-nagios-checks-via-ticketsystem","Temporäres deaktivieren von Nagios Checks via Ticketsystem",[439,35108,35109],{},"Oft kommt es auch dazu, dass bestimmte Nagios Checks zeitweise uninteressant werden. Zum Beispiel dann, wenn ein Projekt\npausiert und die Maschine daher keine Priorität mehr hat. Man möchte in dieser Zeit keine weiteren Benachrichtigungen\nüber fehlgeschlagene Nagios Checks erhalten. Daher wurde eine Möglichkeit geschaffen, einem Nagios Check ein Ticket aus\ndem Ticketsystem zuzuordnen. Dies sorgt dafür, dass der Nagios Check deaktiviert wird, bis das Ticket geschlossen wird.\nSo hat man einen Überblick darüber, welche Nagios Checks aus welchen Gründen und für welche Dauer ausgeschaltet sind.",[3938,35111,35113],{"id":35112},"was-ist-noch-denkbar","Was ist noch denkbar?",[439,35115,35116],{},"Überlegungen sind unter anderem zusätzliche Benachrichtigungswege (SMS,IRC, ..), so dass zum Beispiel bei\nBenachrichtigungen besonders hoher Priorität zu bestimmten Uhrzeiten SMS verschickt werden können, um über kritische\nZustände zu informieren.",[439,35118,35119],{},"Ebenso ist eine nutzerspezifische Priorisierung von expliziten Nagios Checks denkbar.",{"title":469,"searchDepth":507,"depth":507,"links":35121},[35122,35123,35124,35125,35131],{"id":34904,"depth":507,"text":34905},{"id":34914,"depth":507,"text":34915},{"id":34924,"depth":507,"text":34925},{"id":35020,"depth":507,"text":35021,"children":35126},[35127,35128,35129,35130],{"id":35027,"depth":547,"text":35028},{"id":35082,"depth":547,"text":35083},{"id":35092,"depth":547,"text":35093},{"id":35105,"depth":547,"text":35106},{"id":35112,"depth":507,"text":35113},[1031],"2016-03-04T09:53:43","https://synyx.de/blog/nagios-benachrichtigungsmanagement/",{},"/blog/nagios-benachrichtigungsmanagement",{"title":34895,"description":469},"blog/nagios-benachrichtigungsmanagement",[35140,35141,21204],"monitoring","nagios","Warum wird ein Benachrichtigungsmanagement benötigt? Monitoring ist wichtig, aber nicht alle Monitoring-Tests sind (nur) für System Administratoren interessant. Oft interessieren sich auch Entwickler zum Beispiel über den Zustand ihrer Anwendung…","KLiNZZlOL8fLbKb6H2p-BUHryi-VNl9X6pED_PD_KTY",{"id":35145,"title":35146,"author":35147,"body":35148,"category":35262,"date":35263,"description":35264,"extension":1034,"link":35265,"meta":35266,"navigation":916,"path":35267,"seo":35268,"slug":35152,"stem":35270,"tags":35271,"teaser":35273,"__hash__":35274},"blog/blog/heute-sprechen-wir-python-und-scratch.md","Heute sprechen wir Python und Scratch!",[21],{"type":432,"value":35149,"toc":35260},[35150,35153,35166,35169,35172,35181,35184,35210,35213,35239,35242,35245,35254,35257],[435,35151,35146],{"id":35152},"heute-sprechen-wir-python-und-scratch",[439,35154,35155,35156,35159,35160,35165],{},"Am Samstag fand der erste IT-Workshop mit Unterstützung der ",[1002,35157,12123],{"href":26744,"rel":35158},[1006],"\nim ",[1002,35161,35164],{"href":35162,"rel":35163},"https://www.internationaler-bund.de/angebote/standort/202305",[1006],"IB Jugendmigrationsdienst Karlsruhe"," in der Südstadt\nstatt. Migranten haben im IB u. a. die Möglichkeit, Sprachkurse in Deutsch als Fremdsprache für verschiedene Stufen zu\nabsolvieren.",[439,35167,35168],{},"Einige dieser Schüler, sowie andere IT-interessierte Migranten aus dem Landkreis Karlsruhe, fanden sich um 10:30 Uhr\nzum Start der Workshops ein.",[439,35170,35171],{},"Ein Ziel war unter anderem, potentielle Kandidaten für eine IT-Ausbildung zu identifizieren, da auch Teilnehmer mit\nkonkretem IT-Ausbildungwunsch vor Ort waren. Wieder andere wollten einfach gerne mehr über die IT erfahren bzw. sich\nein erstes Bild machen und mal reinschnuppern.",[439,35173,35174,35175,35180],{},"Zusammen mit der ",[1002,35176,35179],{"href":35177,"rel":35178},"https://www.codecentric.de/",[1006],"codecentric AG"," hatten wir eine ausreichend große Gruppe von Mentoren, um\ndie jungen Erwachsenen zwischen 20 – 25 Jahren bei den Workshops zu betreuen.",[439,35182,35183],{},"Nach einer kurzen Einführung machten wir uns auch sofort ans Werk und teilten uns in sechs Gruppen zu je zwei Personen\nauf. Los ging es mit einem Scratch Workshop und nach kurzweiligen 75 Minuten stellten die Gruppen ihre Ergebnisse vor.",[439,35185,35186,35194,35202],{},[1002,35187,35190],{"href":35188,"rel":35189},"https://media.synyx.de/uploads//2016/01/ib_baden_08.jpg",[1006],[2205,35191],{"alt":35192,"src":35193},"ib_baden_08","https://media.synyx.de/uploads//2016/01/ib_baden_08-150x150.jpg",[1002,35195,35198],{"href":35196,"rel":35197},"https://media.synyx.de/uploads//2016/01/ib_baden_03.jpg",[1006],[2205,35199],{"alt":35200,"src":35201},"ib_baden_03","https://media.synyx.de/uploads//2016/01/ib_baden_03-150x150.jpg",[1002,35203,35206],{"href":35204,"rel":35205},"https://media.synyx.de/uploads//2016/01/ib_baden_05.jpg",[1006],[2205,35207],{"alt":35208,"src":35209},"ib_baden_05","https://media.synyx.de/uploads//2016/01/ib_baden_05-150x150.jpg",[439,35211,35212],{},"Im Anschluss folgte ein Minecraft Workshop, der die in Scratch neu erworbenen Logikkenntnisse bzw. Programmierkonzepte\ndirekt auf eine harte Probe stellte 🙂",[439,35214,35215,35223,35231],{},[1002,35216,35219],{"href":35217,"rel":35218},"https://media.synyx.de/uploads//2016/01/ib_baden_02.jpg",[1006],[2205,35220],{"alt":35221,"src":35222},"ib_baden_02","https://media.synyx.de/uploads//2016/01/ib_baden_02-150x150.jpg",[1002,35224,35227],{"href":35225,"rel":35226},"https://media.synyx.de/uploads//2016/01/ib_baden_07.jpg",[1006],[2205,35228],{"alt":35229,"src":35230},"ib_baden_07","https://media.synyx.de/uploads//2016/01/ib_baden_07-150x150.jpg",[1002,35232,35235],{"href":35233,"rel":35234},"https://media.synyx.de/uploads//2016/01/ib_baden_01.jpg",[1006],[2205,35236],{"alt":35237,"src":35238},"ib_baden_01","https://media.synyx.de/uploads//2016/01/ib_baden_01-150x150.jpg",[439,35240,35241],{},"Zur Stärkung gab es dann Pizza für alle!",[439,35243,35244],{},"Wir alle hatten super viel Spaß und kleinere Sprachbarrieren konnten durch ein Lächeln sowie den Einsatz von Händen und\nFüßen einfach ausgeräumt werden 🙂",[439,35246,35247],{},[1002,35248,35251],{"href":35249,"rel":35250},"https://media.synyx.de/uploads//2016/01/ib_baden_06.jpg",[1006],[2205,35252],{"alt":35253,"src":35249},"ib_baden_06",[439,35255,35256],{},"In der Abschlussrunde teilte uns unsere Ansprechpartnerin vom IB mit, dass alle ganz begeistert waren und der Wunsch\nnach einer Wiederholung groß sei. Sie hätte bereits eine ganze Liste mit Interessenten für einen zweiten Termin.",[439,35258,35259],{},"Unser Feedback zu einzelnen Teilnehmern hat sie aufgenommen, um deren individuelle Entwicklung besser fördern und\nunterstützen zu können.",{"title":469,"searchDepth":507,"depth":507,"links":35261},[],[1031],"2016-01-26T15:24:13","Am Samstag fand der erste IT-Workshop mit Unterstützung der Devoxx4Kids\\nim IB Jugendmigrationsdienst Karlsruhe in der Südstadt\\nstatt. Migranten haben im IB u. a. die Möglichkeit, Sprachkurse in Deutsch als Fremdsprache für verschiedene Stufen zu\\nabsolvieren.","https://synyx.de/blog/heute-sprechen-wir-python-und-scratch/",{},"/blog/heute-sprechen-wir-python-und-scratch",{"title":35146,"description":35269},"Am Samstag fand der erste IT-Workshop mit Unterstützung der Devoxx4Kids\nim IB Jugendmigrationsdienst Karlsruhe in der Südstadt\nstatt. Migranten haben im IB u. a. die Möglichkeit, Sprachkurse in Deutsch als Fremdsprache für verschiedene Stufen zu\nabsolvieren.","blog/heute-sprechen-wir-python-und-scratch",[4216,35272],"fluechtlingshilfe","Am Samstag fand der erste IT-Workshop mit Unterstützung der Devoxx4Kids im IB Jugendmigrationsdienst Karlsruhe in der Südstadt statt. Migranten haben im IB u. a. die Möglichkeit, Sprachkurse in Deutsch als…","Zt-fnsp03snxJaS-0nXAG99UYxD1WhWyGH_-HYGPN2k",{"id":35276,"title":35277,"author":35278,"body":35279,"category":35379,"date":35380,"description":35381,"extension":1034,"link":35382,"meta":35383,"navigation":916,"path":35384,"seo":35385,"slug":35283,"stem":35386,"tags":35387,"teaser":35394,"__hash__":35395},"blog/blog/it-wertbeitrag-ganz-pragmatisch.md","IT-Wertbeitrag ganz pragmatisch?!",[312],{"type":432,"value":35280,"toc":35377},[35281,35284,35287,35290,35295,35298,35301,35304,35309,35312,35315,35320,35323,35326,35329,35340,35343,35348,35353,35356,35359,35370],[435,35282,35277],{"id":35283},"it-wertbeitrag-ganz-pragmatisch",[439,35285,35286],{},"Eigentlich hatte ich vor mit den nachfolgenden Zeilen einen pragmatischen Ansatz zu liefern, wie man den IT-Wertbeitrag\nermittelt. Das Wörtchen „eigentlich“ verrät bereits, dass man diesen pragmatischen Ansatz zur IT-Wertbeitragsermittlung\nim Folgenden nicht finden wird. Im Gegenteil, ich bin mittlerweile davon überzeugt, dass die Ermittlung des Wertbeitrags\nZeitverschwendung ist!",[439,35288,35289],{},"Wie komme ich zu dem Schluss? In meinen Augen steht der Versuch einer objektiven Wertbeitragsermittlung in keinem\nadäquaten Verhältnis von Aufwand zu Nutzen. Dabei stellt sich natürlich die Frage, was ich für angemessen bzw. nicht\nadäquat halte. Um dies bewerten zu können, ist es notwendig sich mit dem Ziel bzw. den Hintergründen der\nIT-Wertbeitragsermittlung auseinanderzusetzen.",[439,35291,35292],{},[448,35293,35294],{},"Aus Gründen",[439,35296,35297],{},"Warum will ich überhaupt den IT-Wertbeitrag ermitteln? Dafür gibt es im Großen und Ganzen zwei Gründe. Der am meisten\ngenannte Grund – zumindest nach meiner Erfahrung – ist der, der Rechtfertigung. Die Geschäftsführung bzw. der\nFachbereich wirft der IT(Abteilung) Ineffizienz und zu hohe Kosten vor. Die IT soll belegen, wie viel sie eigentlich\nWert ist, bzw. was sie dem Unternehmen bringt. IT-Verantwortliche, welche mit solchen Vorwürfen und der\nWertbeitragsermittlung konfrontiert sind, haben einen sehr schweren Stand. Zum einen gibt es ein klares\nVerständnisdefizit auf Fachbereichsseite über die Zusammenhänge von Unternehmensprozessen und der IT – was durchaus der\nIntransparenz und Kommunikationsunfreudigkeit der IT selbst geschuldet sein kann – und zum anderen können sie alleine\nüberhaupt gar nicht beurteilen, wie viel die IT tatsächlich zum Unternehmenswert beiträgt. Dies geht, wenn überhaupt nur\nmit dem Fachbereich zusammen! Denn Werte entstehen erst dann, wenn die IT genutzt wird und der Nutzer ist nun mal der\nFachbereich.",[439,35299,35300],{},"Der zweite Grund, warum man den Wertbeitrag der IT ermitteln will, ist sinnvoller aber in meinen Augen auch zu teuer.\nMan möchte eine bessere Entscheidungsgrundlage für IT-Investitionen haben. Idealerweise möchte man wissen, wie hoch der\nIT-Wertbeitrag in den jeweiligen Unternehmensbereichen ist, um dann entscheiden zu können, wo mehr und wo weniger in\ndie IT investiert wird. Auch hier kann die Wertbeitragsermittlung nicht die IT alleine machen und der Fachbereich muss\nmitarbeiten.",[439,35302,35303],{},"Und, warum ist jetzt die IT-Wertbeitragsermittlung meiner Meinung nach zu teuer? Wie ich weiter oben beschrieben habe,\nentstehen Werte nicht in der IT sondern im Fachbereich. Was für Werte entstehen dort? Ist das nur das\nUnternehmensergebnis (Gewinn/Verlust)? Wenn ja, wie teilt sich dieses Ergebnis auf die jeweiligen Fachbereiche auf?\nAlso, welcher Fachbereich (Sales & Marketing, Produktion, Einkauf, HR, …) leistet wie viel für das Ergebnis? Die Frage\nstellt sich, da die IT diesen Fachbereichen zuliefert! Oder sind neben dem Gewinn noch weitere Kennzahlen, wie bspw.\nQualität, Mitarbeiterbindung, Wachstum, Risikomanagement, … Bestandteile der Wertbetrachtung? Wenn ja, in welchem\nVerhältnis stehen diese zueinander? Wie viel Prozent vom ganzen Wert entspricht die jeweilige Kennzahl? Und, wie teilen\nsich diese Kennzahlen auf die Fachbereiche auf? Mit diesen Fragen will ich klar stellen, dass es eine sehr komplexe\nAngelegenheit ist überhaupt die Werte des Unternehmens zu beziffern von denen die jeweiligen Anteile der Beitragenden (\nu. a. die IT) ermittelt werden sollen.",[439,35305,35306],{},[448,35307,35308],{},"Objektivität ist der Knackpunkt",[439,35310,35311],{},"Voraussetzung für eine objektive Wertbeitragsermittlung ist eine transparente Bewertung des Unternehmenswertes und\nseiner Bestandteile. Ich tippe mal, bei 99 % der Unternehmen ist diese nicht vorhanden oder nicht objektiv. Objektiv\nbedeutet, die Bewertung ist nachvollziehbar, schlüssig und wird anerkannt. Sie ist nicht „angreifbar“. Wenn man bereits\nauf Fachbereichsseite keine Objektivität gewährleisten kann, wird man sich ständig rechtfertigen müssen – was man ja\neigentlich durch die IT-Wertbeitragsermittlung lösen wollte …",[439,35313,35314],{},"Falls das Unternehmen zu dem vermeintlichen 1 % gehören sollte, welches die Grundvoraussetzung erfüllt und eine\nobjektive Werteaufstellung vorliegen hat, bin ich immer noch der Überzeugung, dass der Aufwand für die\nWertbeitragsermittlung zu hoch ist. Eine IT-Wertbeitragsermittlung ist die Differenz von Kosten zu Nutzen. Dazwischen\nist der Prozess, das Tun des Unternehmens. An dem Prozess ist nicht nur die IT beteiligt. Es gibt mehrere\nProzessbeteiligte die etwas dazu tun, dass am Ende des Prozesses ein Ergebnis entsteht. Um auch hier Objektivität sicher\nzu stellen, genügt es nicht, alleine die Leistung der IT zu bewerten. Es müssen alle Prozessbeteiligten bewertet werden.\nNur dann ist die Bewertung vollständig und schlüssig. Worauf ich hinaus will. Man müsste das gesamte Unternehmen von der\nStrategie, den Ergebnissen, über die Maßnahmen, die zu den Ergebnissen führen sollen, seine Prozesse und alle\nProzessbeteiligten beleuchten und bewerten, um zu einem schlüssigen Ergebnis zu kommen, wer wie viel zum\nUnternehmenserfolg beiträgt. Ich bezweifle wirklich, dass dieser potentielle Aufwand in einem guten Verhältnis zum\nNutzen steht und plädiere für „bleiben lassen“!",[439,35316,35317],{},[448,35318,35319],{},"Alternativen?",[439,35321,35322],{},"Nichtsdestotrotz besteht weiterhin der Bedarf nach mehr Klarheit über den Nutzen der IT und Hilfe bei der\nEntscheidungsfindung für IT-Investitionen. Die Wertbeitragsermittlung ist aber in meinen Augen, der ineffizienteste Weg\ndiese Fragen zu beantworten. Meine Empfehlung ist, dem Fachbereich und Entscheidern ein besseres Verständnis über die IT\nund die komplexen Zusammenhänge in ihrem Unternehmen zu vermitteln. Dadurch können diese ein besseres Gefühl für gute\nund relevante Fragen entwickeln, die zur Entscheidungsfindung wichtig sind. Transparenz, Kommunikation,\nBeziehungsmanagement und Wissen über das Geschäft des Unternehmens helfen auf Seite der IT, dem Fachbereich\nIT-Know-how und Verständnis zu vermitteln. Kennzahlen, die belegen wie die IT genutzt wird (Menge, Zeit,\nProzessoutput, …) helfen sehr dabei, dieses Verständnis zu verbessern und zu untermauern. Auf Fachbereichsseite ist\nebenso Transparenz erforderlich, aber auch der Wille sich IT-Know-how anzueignen und offen für Beratungsvorschläge zu\nsein. Beiden Bereichen muss klar sein, dass sie nur zusammen dem Unternehmen helfen können.",[439,35324,35325],{},"Was wäre ein pragmatischer Ansatz, um den Stellenwert der IT zu stärken, Entscheider über die Wichtigkeit der IT zu\nsensibilisieren und Entscheidungsgrundlagen für IT-Investitionen zu schaffen?",[439,35327,35328],{},"Stellenwert und Wichtigkeit der IT:",[8310,35330,35331,35334,35337],{},[997,35332,35333],{},"Aufzeigen des Durchdringungsgrades der IT: Wie viele Mitarbeiter UND Systeme/Anlagen nutzen IT? Wie viele Kunden\nnutzen die IT? Wo unterstützt die IT Unternehmensprozesse?",[997,35335,35336],{},"Aufzeigen der IT-Kosten im Verhältnis zu den Gesamtkosten und zu anderen Teilkosten. *Anmerkung: Je direkter das\nGeschäft die IT nutzt, umso höher sind die IT-Kosten und entsprechend sieht das Ergebnis auf den ersten Blick\nnatürlich nicht so vorteilhaft aus.",[997,35338,35339],{},"IT-Know-how des Managements durch Kennzahlen, regelmäßige Meetings, Schulungen, externe Beratung, … verbessern und\nPotentiale aufzeigen.",[439,35341,35342],{},"Restrukturierung von IT und Umschichten von IT-Investitionen:",[8310,35344,35345],{},[997,35346,35347],{},"Aufzeigen der IT-Kostenanteile für Projekte (Themen, die den Fachbereich in irgendeiner Art und Weise unterstützen)\nim Verhältnis zu IT-Kostenanteil für Betrieb & Support.",[439,35349,35350],{},[448,35351,35352],{},"IT is still the next big thing!",[439,35354,35355],{},"Häufig wird von der IT verlangt, dass sie sich in die Effizienzkette einhängt und pro Jahr x % weniger an Kosten\nverursacht. Wenn man sich den im Verhältnis kleinen Kostenanteil der IT zu den Gesamtkosten (siehe *Anmerkung weiter\noben) ansieht und die viel größeren Prozesskosten betrachtet, sollte schnell klar sein, dass es bei den Prozesskosten\nviel mehr Einsparpotential gibt.",[439,35357,35358],{},"Gründe für den Ausbau von IT-Investitionen:",[8310,35360,35361,35364,35367],{},[997,35362,35363],{},"Mit IT Prozessabläufe automatisieren = Prozesskostensenkung, Steigerung von\nOutput/Geschwindigkeit/Nachvollziehbarkeit und Qualität, Kunden / Lieferanten Integrieren",[997,35365,35366],{},"Mit IT mehr Daten sammeln und auswerten = Generierung, Verarbeitung und Auswertung von Daten / großen Datenmengen,\nbessere Entscheidungsfindung.",[997,35368,35369],{},"Mit IT die Kommunikation ausbauen = Maschine zu Maschine, B2B oder B2C.",[439,35371,35372,35373,35376],{},"Und, es gibt immer noch keine bessere Alternative zur IT, um Effizienz zu erzielen! Wieso also Investitionen in IT\nreduzieren? Im Gegenteil, aus den genannten Gründen muss ",[448,35374,35375],{},"VIEL MEHR"," in die IT investiert werden. Für diese Erkenntnis\nbrauche ich keinen IT-Wertbeitrag, allerdings Wissen, was man mit IT machen und wie man sie für sein Geschäft nutzen\nkann!",{"title":469,"searchDepth":507,"depth":507,"links":35378},[],[1031],"2016-01-21T15:11:10","Eigentlich hatte ich vor mit den nachfolgenden Zeilen einen pragmatischen Ansatz zu liefern, wie man den IT-Wertbeitrag\\nermittelt. Das Wörtchen „eigentlich“ verrät bereits, dass man diesen pragmatischen Ansatz zur IT-Wertbeitragsermittlung\\nim Folgenden nicht finden wird. Im Gegenteil, ich bin mittlerweile davon überzeugt, dass die Ermittlung des Wertbeitrags\\nZeitverschwendung ist!","https://synyx.de/blog/it-wertbeitrag-ganz-pragmatisch/",{},"/blog/it-wertbeitrag-ganz-pragmatisch",{"title":35277,"description":35286},"blog/it-wertbeitrag-ganz-pragmatisch",[35388,8469,35389,35390,35391,35392,35393],"best-practice","it-kosten","it-management","it-marketing","it-wertbeitrag","pragmatisch","Eigentlich hatte ich vor mit den nachfolgenden Zeilen einen pragmatischen Ansatz zu liefern, wie man den IT-Wertbeitrag ermittelt. Das Wörtchen „eigentlich“ verrät bereits, dass man diesen pragmatischen Ansatz zur IT-Wertbeitragsermittlung…","V5lFst8g9i1u812ZRuZ_0RN8cvogDxJ5taHG4v_nshg",{"id":35397,"title":35398,"author":35399,"body":35400,"category":35478,"date":35479,"description":35480,"extension":1034,"link":35481,"meta":35482,"navigation":916,"path":35483,"seo":35484,"slug":35404,"stem":35485,"tags":35486,"teaser":35490,"__hash__":35491},"blog/blog/lichtschwerterundso-mit-synyx-ins-kino.md","#lichtschwerterundso – Mit synyx ins Kino",[312],{"type":432,"value":35401,"toc":35476},[35402,35405,35408,35418,35421,35431,35434,35444,35447,35450,35460,35463,35473],[435,35403,35398],{"id":35404},"lichtschwerterundso-mit-synyx-ins-kino",[439,35406,35407],{},"Am Montagabend, den 21.12.2015 um 18 Uhr ging es endlich los, unser Kino Event mit #lichtschwerterundso in der Kurbel\nin Karlsruhe.",[439,35409,35410],{},[1002,35411,35414],{"href":35412,"rel":35413},"https://media.synyx.de/uploads//2016/01/IMG_0409-1.jpg",[1006],[2205,35415],{"alt":35416,"src":35417},"Stormtrooper persönlich","https://media.synyx.de/uploads//2016/01/IMG_0409-1-683x1024.jpg",[439,35419,35420],{},"Mit ungefähr 130 Personen war der Bereich vor dem Kinosaal gut gefüllt und bei Wraps, kleinen Häppchen und dem ein oder\nanderen Hopfengetränk konnte man es sich ganz gut gehen lassen.",[439,35422,35423],{},[1002,35424,35427],{"href":35425,"rel":35426},"https://media.synyx.de/uploads//2016/01/IMG_0430-1.jpg",[1006],[2205,35428],{"alt":35429,"src":35430},"Viel los","https://media.synyx.de/uploads//2016/01/IMG_0430-1-1024x683.jpg",[439,35432,35433],{},"Kurz vor dem Film wurden die Lose für unser kleines Lego Gewinnspiel sowie die 3D-Brillen und das Popcorn verteilt und\ndann ging es endlich los mit dem 7. Teil der Star Wars Saga „Das Erwachen der Macht“.",[439,35435,35436],{},[1002,35437,35440],{"href":35438,"rel":35439},"https://media.synyx.de/uploads//2016/01/DSC_0188.jpg",[1006],[2205,35441],{"alt":35442,"src":35443},"Im Kinosaal","https://media.synyx.de/uploads//2016/01/DSC_0188-1024x576.jpg",[439,35445,35446],{},"Achtung nun folgt ein kleiner Spoiler:",[439,35448,35449],{},"Die visuellen Effekte und Bilder fand ich einfach großartig! J.J. Abrams hat ein wirklich grandioses, bildgewaltiges\nWerk vollbracht. Die Geschichte des Films war in meinen Augen etwas zu sehr an den alten Episoden 4 bis 6 angelehnt.\nTrotzdem hat sie mich in Ihren Bann gezogen und ich werde mir den Film definitiv ein weiteres mal ansehen. Man darf\ngespannt sein, wie sich die Geschichte im 8. Teil entwickelt.",[439,35451,35452],{},[1002,35453,35456],{"href":35454,"rel":35455},"https://media.synyx.de/uploads//2016/01/IMG_0475.jpg",[1006],[2205,35457],{"alt":35458,"src":35459},"Verlosung","https://media.synyx.de/uploads//2016/01/IMG_0475-1024x683.jpg",[439,35461,35462],{},"Nach dem Film fand die Verlosung statt und es durften sich drei unserer Gäste über die bereits auf Twitter angekündigten\nLego Star Wars Sets freuen. Viel Spaß beim 3D-Puzzeln 😉",[439,35464,35465],{},[1002,35466,35469],{"href":35467,"rel":35468},"https://media.synyx.de/uploads//2016/01/SYNEMA.jpg",[1006],[2205,35470],{"alt":35471,"src":35472},"Lego Preise","https://media.synyx.de/uploads//2016/01/SYNEMA-1024x576.jpg",[439,35474,35475],{},"Zum Ausklang des Abends hat sich ein guter Teil der Leute bei synyx im Büro getroffen. Dank weiterer Kaltgetränke,\nKicker, Billard und Konsolenspielen hat der Abend einen wirklich gelungen Abschluss gefunden. Und wer weiß, vielleicht\nmachen wir nächstes Jahr ein weiteres #SYNEMA Event? An dieser Stelle auch noch mal vielen Dank an alle, die\nmitgeholfen haben und besonders an die Verantwortlichen der Kurbel in Karlsruhe für die tolle Zusammenarbeit!",{"title":469,"searchDepth":507,"depth":507,"links":35477},[],[1031],"2016-01-07T17:56:44","Am Montagabend, den 21.12.2015 um 18 Uhr ging es endlich los, unser Kino Event mit #lichtschwerterundso in der Kurbel\\nin Karlsruhe.","https://synyx.de/blog/lichtschwerterundso-mit-synyx-ins-kino/",{},"/blog/lichtschwerterundso-mit-synyx-ins-kino",{"title":35398,"description":35407},"blog/lichtschwerterundso-mit-synyx-ins-kino",[35487,35488,35489,34292],"kino","lichtschwerterundso","synema","Am Montagabend, den 21.12.2015 um 18 Uhr ging es endlich los, unser Kino Event mit #lichtschwerterundso in der Kurbel in Karlsruhe. Mit ungefähr 130 Personen war der Bereich vor dem…","RFBZqqu3rA3ZTrRm5ZT4chcunKPwDXSpzd3X5e4Cd7g",{"id":35493,"title":35494,"author":35495,"body":35496,"category":35639,"date":35640,"description":35641,"extension":1034,"link":17950,"meta":35642,"navigation":916,"path":35643,"seo":35644,"slug":35500,"stem":35646,"tags":35647,"teaser":35651,"__hash__":35652},"blog/blog/visual-thinking-synyx-sketcht.md","Visual Thinking – synyx sketcht",[418,166],{"type":432,"value":35497,"toc":35637},[35498,35501,35516,35519,35529,35532,35535,35538,35548,35551,35554,35557,35560,35570,35573,35576,35581,35591,35601,35608,35618,35628],[435,35499,35494],{"id":35500},"visual-thinking-synyx-sketcht",[439,35502,35503,35504,35509,35510,35515],{},"Anstatt immer wieder zur Fortbildung auf Konferenzen zu fahren hatten wir dieses Jahr noch eine andere Idee: Wir hatten\neinen Inhouse-Workshop zum Thema ",[1002,35505,35508],{"href":35506,"rel":35507},"http://www.frauhoelle.com/sketchnotes/",[1006],"“Visual Thinking”"," mit Tanja\nalias ",[1002,35511,35514],{"href":35512,"rel":35513},"https://twitter.com/frauhoelle",[1006],"@frauhoelle",". Ziel des Workshops war es, den Teilnehmern die Bildsprache näher zu\nbringen und dadurch die Kommunikation zu verbessern.",[439,35517,35518],{},"Da am Workshop mit 11 Teilnehmern großes Interesse war haben wir die Themen auf mehrere Tage verteilt: Am ersten Tag\nhatten wir eine gemeinsame Einführung mit grundlegenden Themen, Techniken und Symbolen. Tag zwei blieb dann unseren\n“Moderatoren” zur Spezialisierung und an Tag drei kamen wir “Techniker” nochmals zum Zug.",[439,35520,35521],{},[1002,35522,35525],{"href":35523,"rel":35524},"https://media.synyx.de/uploads//2015/12/sketch_kombiniert-e1450077980406.jpg",[1006],[2205,35526],{"alt":35527,"src":35528},"sketch_kombiniert","https://media.synyx.de/uploads//2015/12/sketch_kombiniert-1024x288.jpg",[439,35530,35531],{},"Als Informatiker sind wir es gewohnt sehr stark zu abstrahieren und Dinge aufs “Wesentliche” zu reduzieren. Wir\nbevorzugen Schrift, eckige Kästen und gerade Linien um unsere Sachverhalte anderen zu erklären. Wir haben quasi verlernt\nvisuell zu sprechen. Es ist jedoch oft so, dass die Sachverhalte so schwer greifbar werden und sich nicht wirklich in\nunser Gedächtnis einprägen. Dem wollten wir mit einer Auffrischung unserer kreativen Fähigkeiten entgegenwirken. Wir\nwollten künftig unsere Kommunikation – egal ob mit Kunden oder Kollegen – visueller und einprägsamer gestalten.",[439,35533,35534],{},"Aus unserer Sicht war das eigentliche Ziel des Workshops die Motivation der Teilnehmer sich von Schrift, Altbekanntem\nund vor allem Digitalem zu lösen und den Mut zu finden, wieder Kind zu sein und unterstützend Bilder zu malen.",[439,35536,35537],{},"An allen Workshop-Tagen stand das spielerische Lernen und Mitmachen im Vordergrund. So war immer Action und es wurde\nnie langweilig. Obwohl natürlich etwas Theorie mit dabei war, wurde schon in der ersten Stunde tatkräftig visualisiert.\nDas war vor allem deshalb möglich, weil Tanja Unmengen von Stiften und Material mit im Gepäck hatte und diese stets\ngriffbereit vor unseren Nasen lagen. Darüber hinaus stand sie uns permanent mit Rat, Tat und Motivation zur Seite.",[439,35539,35540],{},[1002,35541,35544],{"href":35542,"rel":35543},"https://media.synyx.de/uploads//2015/12/alle_am_tisch-e1449841973591.jpg",[1006],[2205,35545],{"alt":35546,"src":35547},"Jeder macht mit","https://media.synyx.de/uploads//2015/12/alle_am_tisch-768x1024.jpg",[439,35549,35550],{},"Am ersten Tag erlernten wir spielerisch die Grundlagen wie Punkte, Striche, Dreiecke, Vierecke und Kreise. Nachmittags\nwurden aus diesen dann komplexere Symbole bis hin zu unserem eigenen Symbol-Alphabet. Wir haben gelernt unsere Ideen\nmit möglichst wenigen Strichen zu transportieren und mit kleinen Details wie Schatten einen große Wirkung zu erzielen.\nDabei haben verschiedene Übungen unsere Kreativität geweckt und uns Selbstvertrauen gegeben.",[439,35552,35553],{},"Am zweiten Tag wurden die Grundlagen und unsere Symbole weiter verfeinert und schließlich um Figuren mit Gesten und\nEmotionen ergänzt. Dazu kamen Schrift, Rahmen, Platzaufteilung und viele kleine Kniffe und Tricks. Die Übungen\nfokussierten sich mehr auf das Visualisieren komplexerer Sachverhalte, Vorträge oder Geschichten.",[439,35555,35556],{},"Durch den Workshop haben wir verschiedene Möglichkeiten kennengelernt, unsere Kommunikation durch visuelle Elemente zu\nverbessern. Vor allem haben wir aber den Mut und das Selbstvertrauen gewonnen, diese Kenntnisse einzusetzen – egal ob am\nFlipchart, auf Papier oder auf PostITs.",[439,35558,35559],{},"Das alles ist schon im synyx-Büro erkennbar: Flipcharts und andere Elemente sind auf einmal bunter und mit der Kraft\nder Bilder aufgepeppt und viele Teilnehmer sind eifrig dabei ihre Fähigkeiten weiter zu verbessern.",[439,35561,35562],{},[1002,35563,35566],{"href":35564,"rel":35565},"https://media.synyx.de/uploads//2015/12/buero-e1449847979285.jpg",[1006],[2205,35567],{"alt":35568,"src":35569},"EIndrücke aus dem Büro","https://media.synyx.de/uploads//2015/12/buero-768x1024.jpg",[439,35571,35572],{},"Insgesamt können wir Frau Hölle und ihre Workshops nur wärmstens weiterempfehlen: Wir hatten viel Spaß und haben viel\nWissen und Mut mitgenommen.",[439,35574,35575],{},"Ab sofort haben wir immer einen Schattenstift in der Tasche!",[439,35577,35578],{},[448,35579,35580],{},"Zum Schluss noch paar Eindrücke vom Workshop…",[439,35582,35583],{},[1002,35584,35587],{"href":35585,"rel":35586},"https://media.synyx.de/uploads//2015/12/brainstorm-e1449841965214.jpg",[1006],[2205,35588],{"alt":35589,"src":35590},"Alle mit dabei","https://media.synyx.de/uploads//2015/12/brainstorm-1024x768.jpg",[439,35592,35593],{},[1002,35594,35597],{"href":35595,"rel":35596},"https://media.synyx.de/uploads//2015/12/chaotisch.jpg",[1006],[2205,35598],{"alt":35599,"src":35600},"Kreatives Chaos","https://media.synyx.de/uploads//2015/12/chaotisch-768x1024.jpg",[439,35602,35603],{},[1002,35604,35606],{"href":35542,"rel":35605},[1006],[2205,35607],{"alt":35546,"src":35547},[439,35609,35610],{},[1002,35611,35614],{"href":35612,"rel":35613},"https://media.synyx.de/uploads//2015/12/spassdabei-e1449842435714.jpg",[1006],[2205,35615],{"alt":35616,"src":35617},"spassdabei","https://media.synyx.de/uploads//2015/12/spassdabei-1024x768.jpg",[439,35619,35620],{},[1002,35621,35624],{"href":35622,"rel":35623},"https://media.synyx.de/uploads//2015/12/whiteboard-e1449841924365.jpg",[1006],[2205,35625],{"alt":35626,"src":35627},"Ergebnis einer Übungsaufgabe","https://media.synyx.de/uploads//2015/12/whiteboard-1024x768.jpg",[439,35629,35630],{},[1002,35631,35634],{"href":35632,"rel":35633},"https://media.synyx.de/uploads//2015/12/wasnehmenwirmit-e1449841945934.jpg",[1006],[2205,35635],{"alt":1385,"src":35636},"https://media.synyx.de/uploads//2015/12/wasnehmenwirmit-768x1024.jpg",{"title":469,"searchDepth":507,"depth":507,"links":35638},[],[1030],"2015-12-14T10:49:00","Anstatt immer wieder zur Fortbildung auf Konferenzen zu fahren hatten wir dieses Jahr noch eine andere Idee: Wir hatten\\neinen Inhouse-Workshop zum Thema “Visual Thinking” mit Tanja\\nalias @frauhoelle. Ziel des Workshops war es, den Teilnehmern die Bildsprache näher zu\\nbringen und dadurch die Kommunikation zu verbessern.",{},"/blog/visual-thinking-synyx-sketcht",{"title":35494,"description":35645},"Anstatt immer wieder zur Fortbildung auf Konferenzen zu fahren hatten wir dieses Jahr noch eine andere Idee: Wir hatten\neinen Inhouse-Workshop zum Thema “Visual Thinking” mit Tanja\nalias @frauhoelle. Ziel des Workshops war es, den Teilnehmern die Bildsprache näher zu\nbringen und dadurch die Kommunikation zu verbessern.","blog/visual-thinking-synyx-sketcht",[9555,14820,35648,35649,35650,9459],"sketch","visual-thinking","viz","Anstatt immer wieder zur Fortbildung auf Konferenzen zu fahren hatten wir dieses Jahr noch eine andere Idee: Wir hatten einen Inhouse-Workshop zum Thema “Visual Thinking” mit Tanja alias @frauhoelle. Ziel…","8ajiRM_Ie-9oQaTMg3l9lNm1CxHKwd_DXd7eC7xmzKQ",{"id":35654,"title":35655,"author":35656,"body":35657,"category":35884,"date":35885,"description":35886,"extension":1034,"link":35887,"meta":35888,"navigation":916,"path":35889,"seo":35890,"slug":35661,"stem":35892,"tags":35893,"teaser":35898,"__hash__":35899},"blog/blog/a-conference-series-that-matters-from-nosql-to-distributed.md","A conference series that matters – From NoSQL to distributed",[238],{"type":432,"value":35658,"toc":35878},[35659,35662,35685,35689,35692,35695,35719,35732,35743,35749,35759,35763,35777,35781,35788,35799,35832,35856,35873,35875],[435,35660,35655],{"id":35661},"a-conference-series-that-matters-from-nosql-to-distributed",[439,35663,35664,35665,35670,35671,35674,35675,35678,35679,35684],{},"Since November 2013 I have been attending six conferences of the ",[1002,35666,35669],{"href":35667,"rel":35668},"https://distributed-matters.org/",[1006],"conference series","\nformerly known as ",[448,35672,35673],{},"NoSQL matters",", and that now runs by the name ",[448,35676,35677],{},"distributed matters",". And you can say I am a fan!\nThe conferences take place in various interesting places of the world, so far they were hosted in Cologne, Berlin,\nParis, Dublin and Barcelona. I have not been everywhere, but my favorite city is Barcelona. Beautifully located at\nthe ",[1002,35680,35683],{"href":35681,"rel":35682},"http://www.uab-casaconvalescencia.org/ca/index.php",[1006],"Casa Convalescència",", it is fun to attend all the talks on\ndifferent topics and discuss with the speakers and attendees. So, after being to distributed matters in Barcelona again\non November 21, I want to take the chance to wrap up and share some of my impressions.",[3938,35686,35688],{"id":35687},"why-i-like-the-conference-series","Why I like the conference series",[439,35690,35691],{},"What I really like about the conferences is that it is still quite small. There is no rushing from talk to talk, nor are\nthere people who just come to give their talk and leave immediately afterwards. This makes it possible to get in touch\nwith the speakers, even the ones with high reputation in the field of modern database technology and distributed\nsystems. So far, speakers from Facebook, MapR, Google, Codecentric and many more companies shared their knowledge and\nexperiences, and were available for discussions during the day or at the get-together.",[439,35693,35694],{},"I attended talks that go beyond the commercial tech-talks that is sometimes heard at bigger conferences. I have my\npersonal highlights for every year:",[439,35696,35697,35698,35701,35702,35707,35708,35713,35714,11288],{},"In 2013 in Barcelona there was a talk by ",[448,35699,35700],{},"Doug Turnbull"," where he took up a historical review on the NoSQL movement\nwhich inspired us to have a critical look on the NoSQL movement in Germany in\na ",[1002,35703,35706],{"href":35704,"rel":35705},"https://synyx.de/2014/06/a-very-brief-history-of-the-nosql-development/",[1006],"blog post",", and later in an article series\nfor the ",[1002,35709,35712],{"href":35710,"rel":35711},"https://jaxenter.de/eine-kleine-reise-durch-nosql-28451",[1006],"German Java Magazin"," (also available at\nthe ",[1002,35715,35718],{"href":35716,"rel":35717},"https://synyx.de/unternehmen/publikationen/",[1006],"synyx homepage",[439,35720,35721,35722,35725,35726,35731],{},"The following year, 2014, ",[448,35723,35724],{},"Ellen Friedman"," beautifully told a story on the emergence of big data and the usage of *\n*time series databases** of which we ",[1002,35727,35730],{"href":35728,"rel":35729},"https://synyx.de/2014/11/time-series-data-is-the-the-new-big-data/",[1006],"blogged"," a\nshort recap that became relatively popular.",[439,35733,35734,35735,35738,35739,35742],{},"And this year ",[448,35736,35737],{},"Pablo Chacin"," gave a talk on ",[990,35740,35741],{},"The tale of two microservices",", which focuses on the different\ninterpretations on the weakly defined term microservices (which, in my opinion, is comparibaly weak as the term NoSQL).",[439,35744,35745,35746,35748],{},"And I do not forget to mention the talks of ",[448,35747,28804],{},", who is kind of a permanent guest at the conferences,\nand whose talks I always enjoy as they reveal intersting insight into his experiences with modern information technology\ntrends.",[439,35750,35751,35752,35754,35755,35758],{},"The new trend is going from distribution to microservices, and ",[448,35753,27672],{}," is, like ",[448,35756,35757],{},"NoSQL",", a buzzword with a\nmaybe limited survival chance. But I like that the topic is controversially and honestly discussed in the community at\nthe conferences. Often, a clear and honest message is promoted: If you don’t need microservices, don’t do them. And, as\npointed out at several occasions, honesty is a valuable good in modern distributed datastore times.",[3938,35760,35762],{"id":35761},"the-conference-organization","The conference organization",[439,35764,35765,35766,18175,35769,35772,35773,35776],{},"To my opinion, the conferences are always very well organized. From my side a big thanks to ",[448,35767,35768],{},"Katja Keil",[448,35770,35771],{},"Jana\nVolkova"," from the organization team! Well chosen venues and a good organized entrance make participation easy and free\nthe participants to focus on talks and discussions. And also (or almost most) important: There is always very good\ncoffee. 😉 The traditional after-conference get-togethers provide local cuisine (like famous ",[990,35774,35775],{},"tapas and beer"," in\nBarcelona) and promotes further discussions in a calm atmosphere. Keeping the same locations in cultural interesting\ncities makes it easy and fun to return on a yearly basis, and also to plan elongated weekends with family.",[3938,35778,35780],{"id":35779},"dmconf15-in-barcelona","#dmconf15 in Barcelona",[439,35782,35783,35784,35787],{},"A few impressions on ",[448,35785,35786],{},"this years conference in Barcelona",", that took place on 21.11.2015. For the sake of brevity,\nI focus on the (in my personal opinion!) most interesting talks of the day.",[439,35789,35790,35791,35794,35795,35798],{},"The conference key note was held by ",[448,35792,35793],{},"K. Hightower"," from Google. His talk focused on resource usage optimization in\nvirtualized environments. If you virtualize your machine, there is a good chance that you waste resources, what limits\nthe capacity of machines that can be hosted. That is not only a waste of money, but also puts harsh limits on the number\nof machines. Algorithms that optimally schedule machines in the environment are needed. Hightower\nshowed ",[1002,35796,8177],{"href":33069,"rel":35797},[1006],", an open source project that serves the purpose.",[439,35800,35801,35806,35807,35812,35813,35818,35819,35824,35825,18175,35828,35831],{},[1002,35802,35805],{"href":35803,"rel":35804},"http://de.slideshare.net/dmconf/f1-the-distributed-sql-database-supporting-googles-ad-business-bart-samwell",[1006],"B. Samwell",",\nalso from Google, gave an introduction to ",[1002,35808,35811],{"href":35809,"rel":35810},"http://research.google.com/pubs/pub38125.html",[1006],"Googles F1",", a distributed\ndatabase build atop ",[1002,35814,35817],{"href":35815,"rel":35816},"http://research.google.com/archive/spanner.html",[1006],"Spanner",". F1 is a hybrid database that takles\nreplication inside and across datacenters, and combines the scalability\nof ",[1002,35820,35823],{"href":35821,"rel":35822},"http://research.google.com/archive/bigtable.html",[1006],"BigTable"," with the usability of a SQL database. F1 provides\nscaling without NoSQL and focuses on ",[448,35826,35827],{},"OLTP",[448,35829,35830],{},"OLAP"," as well. As F1 resides atop Spanner, snapshot isolation is\nguaranteed. Samwell warned that migration to F1 might be hard (it took him and his team two years to migrate from a\nMySQL cluster), and to be aware of hidden actions when you use ORMs.",[439,35833,35834,35839,35840,35843,35844,35849,35850,35855],{},[1002,35835,35838],{"href":35836,"rel":35837},"http://de.slideshare.net/dmconf/a-tale-of-two-microservices-pablo-chacin",[1006],"P. Chacin"," told the interesting ",[990,35841,35842],{},"Tale of two\nMicroservices",". As the term microservice is rather weakly defined, he looked at microservice from two different\nphilosophies: Those considering it as an architectural style, and from the point of operations. They differ in certain\npoints but also have their similarities, also to the SOA architectural style. Chacin emphasized that no complete\ndecoupling of services is achievable, as their inevitable coupling dependencies in time (synchronization), location (\nbinding), knowledge (schema), centrality (orchestration), context and state (persistence). He underlined again the law\nof conversion of complexity in software design (see\ne.g. ",[1002,35845,35848],{"href":35846,"rel":35847},"https://michaelfeathers.silvrback.com/microservices-until-macro-complexity",[1006],"M. Feathers article","), and the role of\nsize and usage of microservices (cp.\nthe ",[1002,35851,35854],{"href":35852,"rel":35853},"https://www.tigerteam.dk/2014/micro-services-its-not-only-the-size-that-matters-its-also-how-you-use-them-part-1/",[1006],"blog series by J. Cramon",").\nTalk and mentioned article are definitely recommendable.",[439,35857,35858,35859,35864,35865,35868,35869,35872],{},"Again, ",[1002,35860,35863],{"href":35861,"rel":35862},"http://de.slideshare.net/dmconf/microservices-stressfree-and-without-increased-heartattack-risk-uwe-friedrichsen",[1006],"U. Friedrichsen","\nwas present as speaker in Barcelona. He warned of the way down microservice road towards ",[448,35866,35867],{},"microservice hell",". He\nshared some of his experiences and pointed out: You can go down this road, but you do not have not! Those that are not\nable to build up a well-structured monolith might not be able to master a complicated distributed (micro)service\narchitecture. But microservices might be a choice if the time from idea to market is of high business importance, as\nwell as the autonomy of teams. He also emphasized Evans’ ",[448,35870,35871],{},"Domain Driven Design"," approach, and the importance to focus\non business functionality, not on data. The DRY principle might not be a good idea when applied across process\nboundaries, evolution of interfaces is usually unavoidable (as business is due to change!). At the end of the talk he\nstated about consistency: The real world, he said, is either BASE or inconsistent. Worth considering in discussions\nabout ACID in the near future..",[3938,35874,9392],{"id":9391},[439,35876,35877],{},"I still recommend the conference series to everyone interested in NoSQL and distributed data storage and systems, and I\nam in good hope that the quality of talks and speakers can be kept up. As long the the number of participants stays\nrelatively small, discussions will keep to be insightful and fruitful. For me it is clear to go by BlindBird to\nBarcelona again next year. I hope to see some new faces then, as speakers and in the audience! 😉",{"title":469,"searchDepth":507,"depth":507,"links":35879},[35880,35881,35882,35883],{"id":35687,"depth":507,"text":35688},{"id":35761,"depth":507,"text":35762},{"id":35779,"depth":507,"text":35780},{"id":9391,"depth":507,"text":9392},[1031],"2015-11-30T11:37:01","Since November 2013 I have been attending six conferences of the conference series\\nformerly known as NoSQL matters, and that now runs by the name distributed matters. And you can say I am a fan!\\nThe conferences take place in various interesting places of the world, so far they were hosted in Cologne, Berlin,\\nParis, Dublin and Barcelona. I have not been everywhere, but my favorite city is Barcelona. Beautifully located at\\nthe Casa Convalescència, it is fun to attend all the talks on\\ndifferent topics and discuss with the speakers and attendees. So, after being to distributed matters in Barcelona again\\non November 21, I want to take the chance to wrap up and share some of my impressions.","https://synyx.de/blog/a-conference-series-that-matters-from-nosql-to-distributed/",{},"/blog/a-conference-series-that-matters-from-nosql-to-distributed",{"title":35655,"description":35891},"Since November 2013 I have been attending six conferences of the conference series\nformerly known as NoSQL matters, and that now runs by the name distributed matters. And you can say I am a fan!\nThe conferences take place in various interesting places of the world, so far they were hosted in Cologne, Berlin,\nParis, Dublin and Barcelona. I have not been everywhere, but my favorite city is Barcelona. Beautifully located at\nthe Casa Convalescència, it is fun to attend all the talks on\ndifferent topics and discuss with the speakers and attendees. So, after being to distributed matters in Barcelona again\non November 21, I want to take the chance to wrap up and share some of my impressions.","blog/a-conference-series-that-matters-from-nosql-to-distributed",[35894,35895,35896,35897],"conference-blog","distributed-systems","microservices","nosql","Since November 2013 I have been attending six conferences of the conference series formerly known as NoSQL matters, and that now runs by the name distributed matters. And you can…","gpWHkrU4i0v7LAtsksybHKaG_wXDbV-x2Nw9w-nTlfw",{"id":35901,"title":35902,"author":35903,"body":35904,"category":35978,"date":35979,"description":35911,"extension":1034,"link":35980,"meta":35981,"navigation":916,"path":35982,"seo":35983,"slug":35908,"stem":35984,"tags":35985,"teaser":35987,"__hash__":35988},"blog/blog/marshmallow-challenge-bei-synyx.md","Marshmallow Challenge bei synyx",[244],{"type":432,"value":35905,"toc":35976},[35906,35909,35912,35915,35924,35927,35935,35938,35941,35955,35958],[435,35907,35902],{"id":35908},"marshmallow-challenge-bei-synyx",[439,35910,35911],{},"Man nehme…",[439,35913,35914],{},"20 Spaghetti – 1 Marshmallow – 1 Meter Schnur – 1 Meter Krepp-Klebeband",[439,35916,35917,35918,35923],{},"…und fertig ist ein verrücktes Pasta-Rezept – wäre eine Möglichkeit. Die andere nennt\nsich ",[1002,35919,35922],{"href":35920,"rel":35921},"http://marshmallowchallenge.com/Welcome.html",[1006],"Marshmallow Challenge",": ein spannendes Spiel zum Thema „agile\nPrinzipien“. Ziel dieses Spieles ist es, nur mit Hilfe der oben genannten Zutaten die höchste, freistehende und stabile\nStruktur zu bauen, die als Herausforderung auf der Spitze der Struktur einen Marshmallow tragen muss.",[439,35925,35926],{},"Drei mutige Teams à vier synyxler stellten sich dieser Aufgabe. Sie bauten in 9 Minuten was das Zeug hält (im wahrsten\nSinne des Wortes). Denn sie durften die Schnur, das Klebeband und die Spaghetti verwenden, wie sie wollten. Einzig der\nMarshmallow durfte weder geteilt, noch gegessen werden.",[439,35928,35929,35930,1402],{},"Nach 9 Minuten Bauzeit lieferten die Teams klasse Ergebnisse (siehe Bilder). Denn jedes Team hat auf seine eigene Weise\neine Struktur geschaffen, die jeweils einen Marshmallow tragen konnte. Im Vergleich zu anderen Marshmallow Challenges\nist dies beachtlich. Meine Erfahrung hat gezeigt, dass nach Ablauf der Zeit viele Spaghetti-Konstruktionen entweder\nnicht fertig werden oder unter der Last des Marshmallows zusammenstürzen. Zum gleichen Ergebnis kommt auch Tom Wujec in\nseinem bekannten Ted-Talk. Dort schildert er seine Erfahrungen und untermauert sie\nmit ",[1002,35931,35934],{"href":35932,"rel":35933},"https://www.youtube.com/watch?v=H0_yKBitO8M",[1006],"zahlreichen Beispielen",[439,35936,35937],{},"Es folgte eine zweite Runde der Marshmallow Challenge. Wieder 9 Minuten Bauzeit und somit eine Chance das Wissen und die\nErfahrung aus der ersten Runde zu nutzen. Motiviert und begeistert machten sich die drei Teams mit neuem Material ans\nWerk. Das Resultat konnte sich zeigen lassen: alle bauten noch höher (bis zu 70 cm).",[439,35939,35940],{},"Aus der anschließenden Diskussion mit den drei Teams ergaben sich folgende Erkenntnisse:",[994,35942,35943,35946,35949,35952],{},[997,35944,35945],{},"Planen ist gut, Machen ist besser. Mit Hilfe von kurzen Zyklen frühes Feedback bekommen und nicht den einen perfekten\nWeg als Lösung anzustreben war eine Erkenntnis (beispielsweise kleine Prototypen bauen).",[997,35947,35948],{},"Der vermeintlich „leichte und fluffige“ Marshmallow verführt zu der Annahme, dass er ganz einfach von der Struktur\ngetragen wird. Jedoch ist der Marshmallow im Vergleich zu den Spaghetti schwerer. Bezogen auf reale Projekte ist es\nsinnvoll, sich Gedanken zu solch versteckten Annahmen (Marshmallows) zu machen. Sind die im Vorfeld getroffenen\nAnnahmen gerechtfertigt?",[997,35950,35951],{},"Ein Team, das gut zusammenarbeitet und mit allen seinen Sinnen dabei ist, kann sehr gute Ergebnisse erzielen.",[997,35953,35954],{},"Spaß an der Arbeit ist zwar nicht notwendig, kann aber ein Garant für Erfolg sein.",[439,35956,35957],{},"Vielen Dank an die drei Teams für die tolle Mitarbeit und guten Ergebnisse!",[439,35959,35960,8351,35968],{},[1002,35961,35964],{"href":35962,"rel":35963},"https://media.synyx.de/uploads//2015/11/IMG_3336.jpg",[1006],[2205,35965],{"alt":35966,"src":35967},"IMG_3336","https://media.synyx.de/uploads//2015/11/IMG_3336-225x300.jpg",[1002,35969,35972],{"href":35970,"rel":35971},"https://media.synyx.de/uploads//2015/11/IMG_3341.jpg",[1006],[2205,35973],{"alt":35974,"src":35975},"IMG_3341","https://media.synyx.de/uploads//2015/11/IMG_3341-225x300.jpg",{"title":469,"searchDepth":507,"depth":507,"links":35977},[],[1031],"2015-11-12T12:59:33","https://synyx.de/blog/marshmallow-challenge-bei-synyx/",{},"/blog/marshmallow-challenge-bei-synyx",{"title":35902,"description":35911},"blog/marshmallow-challenge-bei-synyx",[9546,35986,9557,389],"marshmallow-challenge","Man nehme… 20 Spaghetti – 1 Marshmallow – 1 Meter Schnur – 1 Meter Krepp-Klebeband …und fertig ist ein verrücktes Pasta-Rezept – wäre eine Möglichkeit. Die andere nennt sich Marshmallow…","xeg-YkowNrLEK-bo7NsMcvDDwdOVkp8Odn4IMdKvUNk",{"id":35990,"title":35991,"author":35992,"body":35993,"category":36136,"date":36137,"description":469,"extension":1034,"link":36138,"meta":36139,"navigation":916,"path":36140,"seo":36141,"slug":36142,"stem":36143,"tags":36144,"teaser":36145,"__hash__":36146},"blog/blog/dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spass.md","Dritte Devoxx4Kids: Neue Location, neue Workshops, altbekannter Spaß",[238,172],{"type":432,"value":35994,"toc":36134},[35995,35998,36008,36011,36014,36018,36021,36026,36037,36042,36053,36058,36069,36073,36084,36087,36095,36131],[435,35996,35991],{"id":35997},"dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spaß",[439,35999,36000],{},[1002,36001,36004],{"href":36002,"rel":36003},"https://media.synyx.de/uploads//2015/10/P1020269.jpg",[1006],[2205,36005],{"alt":36006,"src":36007},"P1020269","https://media.synyx.de/uploads//2015/10/P1020269-300x169.jpg",[439,36009,36010],{},"Am 17.10. war es wieder so weit: in Karlsruhe fand die dritte Devoxx4Kids statt. Wieder einmal fanden sich 32 Kinder\nzwischen neun und 15 Jahren zusammen, um einen Tag lang mit Spaß und Freude Technik zu erforschen. Und das dieses mal in\neiner für alle Beteiligten neuen Location, der Hochschule für Technik und Wirtschaft. Diese bedeutete Umstellungen in\nvielen Bereichen: Die Anfahrt musste für Eltern und Kids neu beschrieben werden, die Beschilderung der genutzten Räume\nmusste angepasst werden, und nicht zuletzt lagen auch die Workshop-Räume weiter voneinander entfernt.",[439,36012,36013],{},"Aber nicht nur die Location war anders, auch die Workshops waren allesamt neu gestaltet. Da es sich um eine aufbauende\nVeranstaltung gehandelt hat, standen sowohl die Kids als auch die Mentoren vor neuen Herausforderungen. Das tat der\nFreude an der Veranstaltung aber keinen Abbruch, und die Workshops waren wiederum lehrreich für Kinder und Mentoren.",[439,36015,36016],{},[448,36017,12184],{},[439,36019,36020],{},"Im Angebot hatten wir dieses Mal vier nagelneue Workshops, von denen die Kids alle durchlaufen durften. Thematisch\nversuchten die Workshops die bekannten Schwerpunkte Programmierung, Robotik und das Internet der Dinge spielerisch zu\nkombinieren.",[439,36022,36023],{},[448,36024,36025],{},"Tinkerforge",[439,36027,36028,36036],{},[1002,36029,36032],{"href":36030,"rel":36031},"https://media.synyx.de/uploads//2015/10/P1020208.jpg",[1006],[2205,36033],{"alt":36034,"src":36035},"P1020208","https://media.synyx.de/uploads//2015/10/P1020208-300x169.jpg","\nDer Tinkerforge-Workshop befasst sich mit dem s.g. Internet der Dinge, d. h. der Beobachtung der wahrnehmbaren Umwelt\nmit Sensoren unterschiedlichster Art. Tinkerforge stellt dabei elektronische Komponenten bereit, die sich legoartig\nkombinieren lassen. Der Workshop legte seinen Schwerpunkt diesmal klar auf die Vermittlung eines physikalischen\nGrundverständnisses: Mit Hilfe von in Reihe geschalteten Kartoffeln und Metallen wurden Batterien gebaut, mit denen\nelektronische Komponenten wie Lüfter betrieben wurden. Über die Kopplung der Komponenten mithilfe der Tinkerforge Bricks\nkonnten die erzeugten Ströme und Spannungen dann in einer kleinen JavaFX-Anwendung auch visuell betrachtet und\ninterpretiert werden. Die Verbindung von bekannten physikalischen Experimenten wie der Kartoffelbatterie mit neuen\nTechnologien wie sie Tinkerforge verbreitet hat den Kindern viel Freude bereitet.",[439,36038,36039],{},[448,36040,36041],{},"CardBoard",[439,36043,36044,36052],{},[1002,36045,36048],{"href":36046,"rel":36047},"https://media.synyx.de/uploads//2015/10/P1020344.jpg",[1006],[2205,36049],{"alt":36050,"src":36051},"P1020344","https://media.synyx.de/uploads//2015/10/P1020344-300x169.jpg","\nEbenfalls physikalisch motiviert war auch unser Google CardBoard Workshop, der sich mit der Physik des Auges, der\nTäuschung des Gehirns und virtueller Realität beschäftigt hat. Jedes Team wurde ausgestattet mit einem Handy und einem\nGoogle CardBoard, um sich eine eigene kleine virtuelle Welt zu erzeugen. Begonnen hat der Workshop mit einer kleinen\nEinführung darüber, wie das menschliche Auge sich täuschen lässt und die Illusion einer dreidimensionalen Welt\nhergestellt werden kann. Danach haben die Kids mit den Mentoren eine eigene kleine Umgebung erstellen können, durch die\nsie sich fortbewegen konnten. Der Workshop fand guten Anklang und das Durchschreiten der virtuell konstruierten\nLabyrinthe machte viel Freude.",[439,36054,36055],{},[448,36056,36057],{},"Minecraft",[439,36059,36060,36068],{},[1002,36061,36064],{"href":36062,"rel":36063},"https://media.synyx.de/uploads//2015/10/P1020304.jpg",[1006],[2205,36065],{"alt":36066,"src":36067},"P1020304","https://media.synyx.de/uploads//2015/10/P1020304-300x169.jpg","\nMincraft ist ein Spiel, das eigentlich keinen der Kids mehr hat erklärt werden müssen, hier haben im Gegenteil die\nMentoren so einiges erfahren darüber, was man mit Minecraft alles gestalten kann. Das Ziel des Workshops aber war, das\nModden des Spiels, d.h. das Aufmotzen und Erstellen eines eigenen kleinen Spiels im Spiel. Dazu ist eine für Minecraft\nfür den RaspberryPi vorhandene Python-Schnittstelle verwendet worden. Dies stellt aus Sicht der Mentoren eine\nschwierige Aufgabe an die Kinder da, die diese aber alle mit Bravour gemeistert haben. Unter Anleitung der Mentoren\nhaben fast alle Gruppen ihre eigene individuelle Welt programmatisch erstellt, in der sie nach eigenen programmierten\nRegeln ein Spiel gestaltet haben, in dem sie Punkte sammeln und gegen die Zeit spielen konnten. Nicht zuletzt war es\nhier auch eine Herausforderung, die Spiele anderer Gruppen zu testen und zu meistern.",[439,36070,36071],{},[448,36072,26042],{},[439,36074,36075,36083],{},[1002,36076,36079],{"href":36077,"rel":36078},"https://media.synyx.de/uploads//2015/10/P1020069.jpg",[1006],[2205,36080],{"alt":36081,"src":36082},"P1020069","https://media.synyx.de/uploads//2015/10/P1020069-300x169.jpg","\nDie Firma Parrot hat uns für unser Event freundlicherweise fünf Exemplare ihrer Jumping Sumos gesponsert. Dies haben wir\nmit einem neuen Workshop honoriert. Die erste Hürde hierzu hatten die Mentoren zu nehmen, in dem sie ihr Ziel eine\neigene Java API für den Sumo zu kreieren umsetzen mussten. Kudos dafür! Diese wurde von den Kindern genutzt, um in\neigenen einfachen textuellen Programmen den Sumo dazu zu bewegen, nach eigenen Wünschen zu agieren. Mit einfachen\nBefehlen für vorwärts- und rückwärtsfahren, Drehen und natürlich Springen (die Spezialität des Sumo) musste ein\nParcours gemeistert werden. Die Kinder lösten ihre Aufgabe prima, und natürlich macht es auch einfach Spaß die\nFähigkeiten des Sumo auszureizen und einfach mal zu prüfen wie die einfachen Eigenschaften zu komplexen Verhalten\nkombiniert werden können.",[439,36085,36086],{},"Auch die dritte D4K in Karlsruhe war für alle Beteiligten ein Riesenspaß und großer Erfolg. An alle Kinder ein\nherzliches Dankeschön! Die Eltern der Kinder konnten sich am Ende des Tages noch zeigen lassen was ihre Sprösslinge so\ngeleistet haben, und staunten teilweise nicht schlecht darüber, wie natürlich manche der Kinder mittlerweile mit den\nneuen Technologien umzugehen wissen.",[439,36088,36089,36090,36094],{},"Für uns Mentoren ist die Anzahl der wiederkehrenden Kinder ein Beweis dafür, dass sich Arbeit und Aufwand im Vorfeld\nlohnen. Wir hoffen das es so weiter geht, und das der Erfolg dieser kleinen Tagung für Kinder Schule macht und sich in\nweitere deutsche Städte verbreitet. Wir jedenfalls machen weiter! Die Workshops stehen\nauf ",[1002,36091,4042],{"href":36092,"rel":36093},"https://github.com/devoxx4kidsde",[1006]," frei zur Verfügung. Wer daran Interesse hat, kann gerne mitmachen.",[439,36096,36097,36098,520,36103,520,36108,520,36113,520,36117,520,36122,26132,36125,36130],{},"Wir möchten uns natürlich auch bei den\nSponsoren ",[1002,36099,36102],{"href":36100,"rel":36101},"http://www.parrot.com/de/",[1006],"Parrot",[1002,36104,36107],{"href":36105,"rel":36106},"http://www.siteos.de/",[1006],"SiteOS",[1002,36109,36112],{"href":36110,"rel":36111},"https://www.innoq.com/de/",[1006],"innoQ",[1002,36114,36116],{"href":35177,"rel":36115},[1006],"codecentric",[1002,36118,36121],{"href":36119,"rel":36120},"https://www.inovex.de/de/",[1006],"inovex",[1002,36123,389],{"href":26130,"rel":36124},[1006],[1002,36126,36129],{"href":36127,"rel":36128},"http://www.hs-karlsruhe.de/home.html",[1006],"Hochschule"," bedanken.",[439,36132,36133],{},"Bis zum nächsten Mal… 😉",{"title":469,"searchDepth":507,"depth":507,"links":36135},[],[1031],"2015-10-28T13:40:29","https://synyx.de/blog/dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spass/",{},"/blog/dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spass",{"title":35991,"description":469},"dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spass","blog/dritte-devoxx4kids-neue-location-neue-workshops-altbekannter-spass",[],"Am 17.10. war es wieder so weit: in Karlsruhe fand die dritte Devoxx4Kids statt. Wieder einmal fanden sich 32 Kinder zwischen neun und 15 Jahren zusammen, um einen Tag lang…","8X30S6W4I-oFwiOcXiwJycSPfYqUfAWClqQq4qsayek",{"id":36148,"title":36149,"author":36150,"body":36151,"category":36224,"date":36225,"description":36226,"extension":1034,"link":36227,"meta":36228,"navigation":916,"path":36229,"seo":36230,"slug":36231,"stem":36232,"tags":36233,"teaser":36234,"__hash__":36235},"blog/blog/vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-buero.md","Vom Altbau zu den War-Rooms – ein Jahr neues synyx Büro",[172],{"type":432,"value":36152,"toc":36222},[36153,36156,36159,36162,36172,36175,36185,36195,36198,36201,36204,36213],[435,36154,36149],{"id":36155},"vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-büro",[439,36157,36158],{},"Es ist nun ein Jahr her, dass wir in das Synus Gebäude gezogen sind. Ein Jahr? Ist das wirklich schon so lange her? Kaum\nzu glauben! Und, in diesem Jahr ist auch sehr viel passiert. Doch zunächst erst mal zurück zum Anfang:",[439,36160,36161],{},"Unser tolles Altbau-Büro wurde uns doch zu kuschelig. Wir brauchten definitiv mehr Platz. Die Bürosuche gestaltete sich\nschwieriger als gedacht. Nach langem hin und her stand dann endlich fest, dass wir unser neues Domizil im Synus Gebäude\nbeziehen werden. Am ersten Oktober ging es dann auch los mit dem Umzug. Das ganze Wochenende über wurde im neuen Büro\nfleißig aufgebaut und Möbel gerückt, so dass am Montag dann alle Mitarbeiter an ihren neuen Arbeitsplätzen starten\nkonnten.",[439,36163,36164,36171],{},[1002,36165,36168],{"href":36166,"rel":36167},"https://media.synyx.de/uploads//2015/10/buero_01.jpg",[1006],[2205,36169],{"alt":36170,"src":36166},"buero_01","\nIch persönlich muss schon sagen, dass ich mir mit dem Start im Großraumbüro etwas schwer getan habe. Zuvor hatten wir\nkleine Büros á 2-6 Personen. Die Umstellung nun mit allen Kollegen in einem großen Raum zu sein, war nicht einfach. Die\nTatsache, dass unser neues Büro noch nicht ganz fertig war, erschwerte den Anfang, denn wir konnten einen Teil davon\nerst gar nicht nutzen oder betreten. Aber nicht nur das, der rechte Teil des Gebäudes war noch eine richtige Baustelle.\nSo wurde unser Arbeiten oftmals durch Bohr- und Hammergeräusche begleitet.",[439,36173,36174],{},"Nach und nach wurde immer mehr Räume freigegeben, die Bauarbeiten wurden weniger und somit auch leiser. Und auch wir\nalle von synyx haben uns an das Großraumbüro gewöhnt. Letztlich kann ich es mir gar nicht mehr vorstellen, anders zu\narbeiten.",[439,36176,36177,36184],{},[1002,36178,36181],{"href":36179,"rel":36180},"https://media.synyx.de/uploads//2015/10/kreativ_02.jpg",[1006],[2205,36182],{"alt":36183,"src":36179},"kreativ_02","\nAber auch wir waren nicht untätig unser Büro gemütlich zu gestalten. Wir synyxer stimmten ab, wie unsere Meeting- oder\nauch gerne War-Room genannt, eingerichtet werden sollen. Die Entscheidung stand fest und so sollte es also ein\nWohnzimmer, einen Kreativraum, einen grünen Raum, eine Werkstatt, einen Fitnessraum sowie eine Bar geben. Alle Räume\nsollten aber wie gesagt auch als Meeting-Räume nutzbar sein. Viele engagierte Mitarbeiter machten sich dann an die\nAusgestaltung der Räume und fast alle sind soweit fertig. Es macht unheimlich viel Spaß die unterschiedlichen\nAtmosphären und Funktionen zu nutzen.",[439,36186,36187,36194],{},[1002,36188,36191],{"href":36189,"rel":36190},"https://media.synyx.de/uploads//2015/10/spiel.jpg",[1006],[2205,36192],{"alt":36193,"src":36189},"spiel","Was uns\nauch besonders freut: durch die größere Fläche haben wir nun auch einen großen Meeting-Raum, welcher im Alltag z.B. für\ndas Mittagessen genutzt wird. Dadurch haben wir zusätzlich die Möglichkeit der Java User Group Karlsruhe oder wie morgen\nder Scrum User Group einen Raum anzubieten.",[439,36196,36197],{},"Ja, es ist schon ein Jahr her. Die Zeit ist unheimlich schnell vergangen. Und wir sind auch noch mehr synyxer geworden.\nSeit letztem Jahr kamen 17 neue Arbeitskollegen dazu. Und wir sind immer noch auf der Suche, z.B. nach einem Android\nEntwickler oder Senior Java Entwickler.",[439,36199,36200],{},"Wer also Lust hat, sich bei uns einzubringen, kann sich gerne bewerben 😉",[439,36202,36203],{},"Mein Resümee? Auch wenn der Anfang schwer war und ich immer noch sehr gerne an das alte Büro denke, so fühle ich mich\nhier inzwischen pudelwohl. Die Vorteile des neuen Büros sind großartig. Und das tolle Team tut sein übriges dazu.\nsynyx- weiter so 🙂",[439,36205,36206],{},[1002,36207,36210],{"href":36208,"rel":36209},"https://media.synyx.de/uploads//2015/10/gangl.jpg",[1006],[2205,36211],{"alt":36212,"src":36208},"gangl",[439,36214,36215],{},[1002,36216,36219],{"href":36217,"rel":36218},"https://media.synyx.de/uploads//2015/10/rooms.jpg",[1006],[2205,36220],{"alt":36221,"src":36217},"rooms",{"title":469,"searchDepth":507,"depth":507,"links":36223},[],[1031],"2015-10-07T09:53:18","Es ist nun ein Jahr her, dass wir in das Synus Gebäude gezogen sind. Ein Jahr? Ist das wirklich schon so lange her? Kaum\\nzu glauben! Und, in diesem Jahr ist auch sehr viel passiert. Doch zunächst erst mal zurück zum Anfang:","https://synyx.de/blog/vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-buero/",{},"/blog/vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-buero",{"title":36149,"description":36158},"vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-buero","blog/vom-altbau-zu-den-war-rooms-ein-jahr-neues-synyx-buero",[],"Es ist nun ein Jahr her, dass wir in das Synus Gebäude gezogen sind. Ein Jahr? Ist das wirklich schon so lange her? Kaum zu glauben! Und, in diesem Jahr…","XxCOvRlKKHORpkeV8KJhfXOcsVIEzL3KTjqX2FcbSC8",{"id":36237,"title":36238,"author":36239,"body":36240,"category":36350,"date":36351,"description":36352,"extension":1034,"link":36353,"meta":36354,"navigation":916,"path":36355,"seo":36356,"slug":36357,"stem":36358,"tags":36359,"teaser":36361,"__hash__":36362},"blog/blog/spende-fuer-fluechtlinge-in-karlsruhe.md","Spende für Flüchtlinge in Karlsruhe",[418],{"type":432,"value":36241,"toc":36348},[36242,36245,36248,36251,36259,36262,36272,36287,36297,36300,36303,36306,36316,36325],[435,36243,36238],{"id":36244},"spende-für-flüchtlinge-in-karlsruhe",[439,36246,36247],{},"Der Aufruf eines Bekannten, ihm statt eines Geschenkes zum Geburtstag Sachspenden für Flüchtlinge zu überreichen, hat\neine beachtliche Welle geschlagen.",[439,36249,36250],{},"Ein kleiner Aufruf in den bekannten sozialen Medien führte in wenigen Stunden zu über 100 Reaktionen. Auch synyx war\ndabei. Von der Zustimmung getragen wurde kurzer Hand zusätzlich ein öffentlicher Spendenaufruf gestartet.",[439,36252,8663,36253,36258],{},[1002,36254,36257],{"href":36255,"rel":36256},"https://web.archive.org/web/20150518192118/http://vanguar.de:80/cafe/",[1006],"Café Vanguarde",", wo auch die letzte\nsynyx-Winterfeier stattfand, erklärte sich sofort bereit,",[439,36260,36261],{},"als Lager für die Sachspenden zu dienen und rief dazu auf, eine Woche lang während den Öffnungszeiten die Sachspenden\nanzuliefern. synyx zögerte nicht, den notwendigen Sprinter für die Anlieferung der Spenden an das Flüchtlingsheim zu\nspenden und bei der Aktion Hilfe zu stellen. Zusätzlich wurde auch firmenintern eine Sammlung organisiert.",[439,36263,36264],{},[1002,36265,36268],{"href":36266,"rel":36267},"https://media.synyx.de/uploads//2015/09/IMG_1754.jpg",[1006],[2205,36269],{"alt":36270,"src":36271},"Sammelstelle Café Vanguarde","https://media.synyx.de/uploads//2015/09/IMG_1754-1024x683.jpg",[439,36273,36274,36275,36280,36281,36286],{},"Kleine Bedenken, geschürt durch\neinen ",[1002,36276,36279],{"href":36277,"rel":36278},"https://web.archive.org/web/20210422135731/https://fluechtlingshilfe-karlsruhe.de/kleidung-moebel-2/",[1006],"Artikel","\nüber aktuell nicht mehr benötigte Sachspenden konnten durch einen persönlichen Besuch vorort\nim ",[1002,36282,36285],{"href":36283,"rel":36284},"https://fluechtlingshilfe.net.kit.edu/",[1006],"Flüchtlingsheim KIT Campus Ost"," beseitigt werden. So trafen sich einige\nHelfer am Café Vanguarde um die Sachspenden entgegen zunehmen und –wie mit den Verantwortlichen des Flüchtlingsheims\nvereinbart– die Sachspenden vorzusortieren.",[439,36288,36289],{},[1002,36290,36293],{"href":36291,"rel":36292},"https://media.synyx.de/uploads//2015/09/IMG_1752.jpg",[1006],[2205,36294],{"alt":36295,"src":36296},"Vorsortierung der Spenden","https://media.synyx.de/uploads//2015/09/IMG_1752-1024x683.jpg",[439,36298,36299],{},"Nach und nach trafen immer wieder bis zum vereinbarten Termin Spenden ein. Anschließend wurde der Sprinter mit den\nneusortierten Spenden beladen und zum Flüchtlingsheim KIT-Ost gebracht.",[439,36301,36302],{},"Am richtigen Gebäude angekommen, halfen uns sehr viele Flüchtlinge dabei, den Sprinter koordiniert auszuräumen und die\nSpenden in das Lager zu bringen.",[439,36304,36305],{},"Die relativ spontane Aktion ist geglückt und hat sich gelohnt!",[439,36307,36308],{},[1002,36309,36312],{"href":36310,"rel":36311},"https://media.synyx.de/uploads//2015/09/IMG_1758.jpg",[1006],[2205,36313],{"alt":36314,"src":36315},"Erfolgreiche Spendenaktion","https://media.synyx.de/uploads//2015/09/IMG_1758-1024x683.jpg",[439,36317,36318,36319,36324],{},"Man kann an vielen Stellen helfen wenn man Interesse hat. Einige Flüchtlingsheime werden schon ausreichend versorgt. Vor\nallem ",[1002,36320,36323],{"href":36321,"rel":36322},"https://www.facebook.com/kriegsstrasse200",[1006],"diejenigen mit viel Social Media"," Aktivität. Andere wie das neu\nerrichtete Flüchtlingsheim am KIT Campus-Nord hingegen bauen ihre Infrastruktur, wie zum Beispiel eine Kleiderkammer,\nmomentan erst auf und brauchen sicherlich in naher Zukunft Helfer und Spenden. Wichtig ist vorher abzuklären, in welcher\nForm Hilfe zur Zeit am meisten benötigt wird. Dafür gibt es einige Anlaufstellen:",[994,36326,36327,36334,36341],{},[997,36328,36329],{},[1002,36330,36333],{"href":36331,"rel":36332},"http://fluechtlingshilfe-karlsruhe.de/",[1006],"Flüchtlingshilfe Karlsruhe",[997,36335,36336],{},[1002,36337,36340],{"href":36338,"rel":36339},"http://www.amnesty-karlsruhe.de/",[1006],"Amnesty Karlsruhe",[997,36342,36343],{},[1002,36344,36347],{"href":36345,"rel":36346},"http://www.menschenrechtszentrum.de/",[1006],"Menschenrechtszentrum",{"title":469,"searchDepth":507,"depth":507,"links":36349},[],[1031],"2015-09-29T15:10:46","Der Aufruf eines Bekannten, ihm statt eines Geschenkes zum Geburtstag Sachspenden für Flüchtlinge zu überreichen, hat\\neine beachtliche Welle geschlagen.","https://synyx.de/blog/spende-fuer-fluechtlinge-in-karlsruhe/",{},"/blog/spende-fuer-fluechtlinge-in-karlsruhe",{"title":36238,"description":36247},"spende-fuer-fluechtlinge-in-karlsruhe","blog/spende-fuer-fluechtlinge-in-karlsruhe",[36360,35272],"refugeeswelcome","Der Aufruf eines Bekannten, ihm statt eines Geschenkes zum Geburtstag Sachspenden für Flüchtlinge zu überreichen, hat eine beachtliche Welle geschlagen. Ein kleiner Aufruf in den bekannten sozialen Medien führte in…","sJ_4vu9PPQrEaG0YYmpCxEaL3tto2OCB5t3wX-8NI5w",{"id":36364,"title":36365,"author":36366,"body":36367,"category":36734,"date":36735,"description":36736,"extension":1034,"link":36737,"meta":36738,"navigation":916,"path":36739,"seo":36740,"slug":36371,"stem":36741,"tags":36742,"teaser":36744,"__hash__":36745},"blog/blog/synyx-berlin-expert-days-2015.md","synyx @ Berlin Expert Days 2015",[30],{"type":432,"value":36368,"toc":36729},[36369,36372,36381,36384,36390,36394,36397,36407,36410,36413,36416,36419,36423,36438,36447,36450,36467,36474,36481,36500,36510,36513,36523,36539,36560,36570,36587,36591,36594,36617,36635,36649,36659,36669,36699,36710,36720,36723,36726],[435,36370,36365],{"id":36371},"synyx-berlin-expert-days-2015",[439,36373,36374,36375,36380],{},"Am 17. und 18. September 2015 fanden die fünften ",[1002,36376,36379],{"href":36377,"rel":36378},"http://bed-con.org/2015/",[1006],"Berlin Expert Days"," (kurz BED-Con) statt.",[439,36382,36383],{},"Erst beim Schreiben dieses Blog Posts habe ich mit Erstaunen festgestellt, dass es für mich bereits die vierte BED-Con\nwar…",[439,36385,36386,36387,1402],{},"Rückt näher an den Kamin, Kinder, nehmt euch einen Keks, ich werde euch nun die Geschichte erzählen von ",[990,36388,36389],{},"Neun synyxern,\ndie auszogen, die BED-Con unsicher zu machen",[1065,36391,36393],{"id":36392},"mittwoch-16-september-anreise","Mittwoch, 16. September – Anreise",[439,36395,36396],{},"Bereits gegen 13:30 Uhr macht sich unsere lustige Truppe auf den Weg zum Hauptbahnhof. Gewappnet mit Reiseproviant (\nBier), guter Laune und Kartenspielen sind wir bereit für die große Reise Richtung Berlin.",[439,36398,36399],{},[1002,36400,36403],{"href":36401,"rel":36402},"https://media.synyx.de/uploads//2015/09/Nao-e1443019500387.jpg",[1006],[2205,36404],{"alt":36405,"src":36406},"Nao","https://media.synyx.de/uploads//2015/09/Nao-e1443019500387-169x300.jpg",[439,36408,36409],{},"In weiser Voraussicht haben wir eine Direktverbindung gebucht, sind mehr als überpünktlich am Hauptbahnhof und haben bei\nder großen Reisegruppe selbstverständlich auch an Platzreservierungen gedacht. Wir sind der Meinung, dass nun eigentlich\nnichts mehr schiefgehen könne.",[439,36411,36412],{},"Leider haben wir ein winziges Detail übersehen: unsere Platzreservierungen befinden sich im Ruhebereich des ICE.",[439,36414,36415],{},"So dauert es nicht lange bis unsere freudig aufgeregte und mitteilungsfreudige Truppe von einem adrett gekleideten Herrn\nauf diesen Umstand hingewiesen wird. Mehrmals. Glücklicherweise stört sich niemand sonst an Kommunikation und als er\nnach einigen Stationen wieder aussteigt, kann man sich auch wieder unterhalten, ohne flüstern zu müssen.",[439,36417,36418],{},"Mit einer halben Stunde Verspätung erreichen wir gegen 20 Uhr den Hauptbahnhof der Hauptstadt, wo sich unsere Wege\nvorerst einmal trennen, da ich privat unterkomme.",[1065,36420,36422],{"id":36421},"donnerstag-17-september-erster-konferenztag","Donnerstag, 17. September – Erster Konferenztag",[439,36424,36425,36426,36431,36432,36437],{},"In diesem Jahr hat die BED-Con nicht nur einen anderen Termin (Herbst statt Frühjahr), sondern auch eine andere\nLokalität zu bieten. Statt in der ",[1002,36427,36430],{"href":36428,"rel":36429},"http://www.fu-berlin.de/",[1006],"Freien Universität Berlin"," findet die Java-Konferenz\ndieses Jahr in der ",[1002,36433,36436],{"href":36434,"rel":36435},"http://www.urania.de/",[1006],"Urania Berlin"," statt.",[439,36439,36440,36441,36446],{},"Ich habe einen typischen Berliner Anfahrtsweg von ungefähr 50 Minuten vor mir. Die anderen haben sich in\neinem ",[1002,36442,36445],{"href":36443,"rel":36444},"http://www.motel-one.com/de/hotels/berlin/hotel-berlin-tiergarten/",[1006],"Hotel"," eingenistet, von dem aus man einen\nextrem langen Fußmarsch von 2 Minuten hinlegen muss, um die Urania zu erreichen.",[439,36448,36449],{},"Um 08:40 Uhr treffen wir uns vor der Urania. Wir sind alle gespannt auf den kommenden Tag und schmieden Pläne, welche\nVorträge wir heute besuchen werden.",[439,36451,36452,36453,36456,36457,36460,36461,36466],{},"Wie bereits das Jahr zuvor ist auch dieses Jahr das Thema Microservices sehr präsent auf der BED-Con. So auch der erste\nVortrag im Humboldtsaal von ",[448,36454,36455],{},"Leon Rosenberg",". Ich staune nicht schlecht, als ich den riesigen Saal betrete und gefühlt\ntausende von roten Sesseln vorfinde. Ein Gefühl von Kino liegt in der Luft und ich verspüre ein leichtes Verlangen nach\nPopcorn. Es gibt einige Meta-Ratschläge zum Thema ",[448,36458,36459],{},"Worauf es wirklich ankommt bei Microservices",". Es geht um typische\nProbleme und wie man diese lösen bzw. vermeiden kann. Ich amüsiere mich über die Anekdoten von kuriosen Bugs, die der\nSpeaker zum Besten gibt. Als es um Problemvermeidung und Monitoring von Anwendungen geht, stelle ich fest, dass ich Leon\nRosenberg bereits von einer vergangenen BED-Con kenne, nämlich von seinem Talk über ",[1002,36462,36465],{"href":36463,"rel":36464},"http://www.moskito.org/",[1006],"MoSKito",",\neinem sehr hilfreich anmutendem Monitoring Tool für Java-Anwendungen. Ich ärgere mich, dass ich es nach über zwei\nJahren immer noch nicht geschafft habe, mich genauer mit MoSKito auseinander zu setzen und nehme mir fest vor, das\ndemnächst endlich anzugehen.",[439,36468,36469,36470,36473],{},"In der kurzen Pause stärke ich mich mit Kaffee – diesen gibt es neben Tee, Wasser und Cola nämlich zur Selbstbedienung –\nund greife voller Vorfreude zu, als ich Brezeln entdecke. Noch während ich den Gedanken habe ",[990,36471,36472],{},"Juhu keine\nButterbrezeln!",", beiße ich hinein und bemerke, dass die Brezeln mit massig Butter gefüllt sind! Ich frage mich, wer auf\nso eine verrückte Idee wie gefüllte Butterbrezeln kommen kann…",[439,36475,36476,36477,36480],{},"Der Schock über die bizarren Brezeln rückt allerdings schnell in den Hintergrund. Wir irren reichlich verwirrt durch die\nUrania auf der Suche nach Raum A. Mehrere Stockwerke, mehrere Möglichkeiten von Stockwerk zu Stockwerk zu kommen.\nIrgendwann fragen wir uns, ob wir auf der Suche nach Raum ",[29523,36478,36479],{},"neundreiviertel"," A eine geheime Wand übersehen haben.\nGlücklicherweise kann uns die nette BED-Con Crew weiterhelfen und bringt anschließend auch einige Beschilderungen mehr\nan.",[439,36482,36483,36484,36489,36490,36493,36494,36499],{},"Im Zuge der ",[1002,36485,36488],{"href":36486,"rel":36487},"http://jug-karlsruhe.de/",[1006],"Java User Group Karlsruhe"," hatte ",[448,36491,36492],{},"Sven Ruppert"," bereits seinen Vortrag über *\n*Mutation Testing** bei uns im Hause gehalten. Da ich diesen allerdings verpasst habe, will ich die BED-Con nutzen, um\netwas über Mutation Testing zu erfahren. Anhand eines simplen Code Snippets wird aufgezeigt und diskutiert, was denn\neigentlich gute Testabdeckung bedeutet und wie man diese messen und erreichen kann. Dabei wird deutlich gemacht, dass\neine gute Testabdeckung eben nicht nur an guter Code Coverage gemessen werden kann. Mithilfe\nvon ",[1002,36495,36498],{"href":36496,"rel":36497},"http://pitest.org/",[1006],"PIT"," stellt Sven Ruppert dann im praktischen Beispiel die Methode des Mutation Testings vor.\nDer Vortrag macht mich neugierig und ich nehme mir vor, mich nach der BED-Con genauer mit dem Thema zu befassen, auch\nwenn Mutation Testing sicherlich nicht der Heilige Gral für jede Art von Anwendung sein mag.",[439,36501,36502,36503,36505,36506,36509],{},"Kaffee und Wasser fassen und ab in den Humboldtsaal! ",[448,36504,375],{}," erörtert, warum ",[448,36507,36508],{},"Microservices und agile\nSoftwareentwicklung"," gut zusammenpassen. Anhand von Conway’s Law geht er auf die Vor- und Nachteile von Microservices\nein. Der Vortrag ist fast ein bisschen philosophisch anmutend, aber nichtsdestotrotz sehr interessant.",[439,36511,36512],{},"Zur Stärkung gibt es in der Mittagspause belegte Baguettes. Meines hat sowohl auf der Ober- als auch auf der Unterseite\neine kräftige Schicht Butter aufzuweisen. Mir scheint, Butter ist in Berlin ein beliebtes Nahrungsmittel.",[439,36514,36515],{},[1002,36516,36519],{"href":36517,"rel":36518},"https://media.synyx.de/uploads//2015/09/Devoxx4Kids_Bedcon.jpg",[1006],[2205,36520],{"alt":36521,"src":36522},"Devoxx4Kids_Bedcon","https://media.synyx.de/uploads//2015/09/Devoxx4Kids_Bedcon-300x169.jpg",[439,36524,36525,36526,36528,36529,2040,36531,36533,36534,36538],{},"Für mich als synyxer ist es quasi eine heilige Pflicht, dem Vortrag über die ",[448,36527,12123],{}," beizuwohnen, der von\nunseren Kollegen ",[448,36530,173],{},[448,36532,239],{}," gehalten wird. Die Zuschauer sind\ninteressiert, stellen Fragen und sorgen für interessante Diskussionen. Ich bin gespannt, ob der Vortrag Früchte tragen\nwird und wir bald einen weiteren ",[1002,36535,26746],{"href":36536,"rel":36537},"http://www.devoxx4kids.de/veranstaltungen/",[1006]," Sprössling\nheranwachsen sehen werden.",[439,36540,36541,36542,36545,36546,36551,36552,36555,36556,36559],{},"Im Anschluss (bequemerweise muss ich nicht einmal den Raum wechseln) stellt ",[448,36543,36544],{},"Oliver Wehrens"," die Leitplanken\nder ",[1002,36547,36550],{"href":36548,"rel":36549},"http://www.epost.de",[1006],"E-POST"," bei der ",[448,36553,36554],{},"Entwicklung verteilter Systeme"," vor. Ich stelle fest, dass wir in manchen\nProjekten ziemlich ähnliche ",[29523,36557,36558],{},"Probleme"," Herausforderungen haben, bin allerdings heilfroh, dass zumindest die\nStrukturen/Hierarchien bei uns anderer Art sind.",[439,36561,36562],{},[1002,36563,36566],{"href":36564,"rel":36565},"https://media.synyx.de/uploads//2015/09/UX_im_Projekt_Bedcon.jpg",[1006],[2205,36567],{"alt":36568,"src":36569},"UX_im_Projekt_Bedcon","https://media.synyx.de/uploads//2015/09/UX_im_Projekt_Bedcon-300x169.jpg",[439,36571,36572,36573,2040,36576,36579,36580,36583,36584,1402],{},"Zum Abschluss des ersten Konferenztages lehne ich mich in einem der bequemen Sessel des Humboldtsaals zurück und lausche\n",[448,36574,36575],{},"Nicole Charlier",[448,36577,36578],{},"Philipp Kumar"," über ihre Erfahrungen von ",[448,36581,36582],{},"UX im Projekt",". Sie berichten über den\nEntwicklungsprozess einer Android App, die Pflegekräfte bei der Arbeit unterstützen soll. Zuallererst wird die fachliche\nDomäne, sprich der Arbeitsalltag einer Pflegekraft, mit anschaulichen Skizzen vorgestellt. Der Entwicklungsprozess wird\ngenauer erläutert: mehrere Tage Feldstudien, Interviews, Konzeptionen. Alles, um den Endbenutzer im Fokus zu haben und\nbei ihm eine hohe Akzeptanz zu erreichen. In der anschließenden Diskussion gibt es einige kritische Stimmen, wie man\neinen solch hohen Anteil an UX überhaupt durchsetzen/verkaufen kann. Mir gefällt die Argumentation (frei zitiert): ",[990,36585,36586],{},"Man\nmuss nur begreiflich machen, dass es das wert ist, wenn man sich durch eine hohe Qualität abheben will",[1065,36588,36590],{"id":36589},"freitag-18-september-zweiter-konferenztag","Freitag, 18. September – Zweiter Konferenztag",[439,36592,36593],{},"Um 08:50 Uhr treffe ich vorerst nur auf einen meiner Mitreisenden und höre mir erstaunliche Geschichten zum Vorabend in\nKreuzberg an. Man munkelt, es wurde angeblich sogar ein Geheimgang in der Hotel Bar gefunden.",[439,36595,36596,36597,36600,36601,36604,36605,36610,36611,36616],{},"Ich rüste mich mit Kaffee aus und mache mich zum ersten Mal auf den Weg ins Loft, das sich im 3. OG befindet. Ich bin\ngespannt auf den tierischen Vortrag von ",[448,36598,36599],{},"Jeremias Rößler"," mit dem erfrischenden Titel ",[448,36602,36603],{},"Bei uns testen lauter Affen:\nDas Ende der Bananensoftware!",". Der Vortrag erläutert das Monkey Testing Prinzip, ein Verfahren, um eine Anwendung auf\nFehler zu prüfen, indem man wahllos agierende Affen auf diese hetzt, die versuchen Fehler zu verursachen. Die Türme von\nHanoi dienen als Beispiel, um zu erläutern, wie man sich den genetischen Algorithmus zunutze machen kann, um\nintelligente Affen zu erschaffen. Wir lernen auch, warum Fortpflanzung so beliebt ist. Ein eigentlich sehr\nwissenschaftlicher Vortrag, der jedoch durch die kreativen Folien und den sympathischen Speaker keinesfalls trocken ist.\nAm Ende schließt sich der Kreis (da beißt sich der Affe in den Schwanz): das Startup ",[1002,36606,36609],{"href":36607,"rel":36608},"http://www.retest.de/",[1006],"ReTest","\nbietet einen vollautomatisierten Regressionstester an, der eben genau das vorgestellte Monkey Testing Prinzip nutzt.\nPersönlich interessant für mich ist allerdings eher die kurz angesprochene Monkey Testing\nLibrary ",[1002,36612,36615],{"href":36613,"rel":36614},"https://github.com/marmelab/gremlins.js/blob/master/README.md",[1006],"Gremlins.js",". Eine Horde Gremlins auf unsere\nAnwendungen loslassen, das klingt ganz schön verlockend.",[439,36618,36619,36622,36623,36625,36626,36629,36630,36634],{},[990,36620,36621],{},"Und täglich grüßen die Microservices…"," Wieder ein Votrag von ",[448,36624,375],{}," über Microservices, diesmal allerdings\nsteht eher die Technologie im Vordergrund. Es wird gezeigt, wie einfach man ",[448,36627,36628],{},"Microservices mit Spring Boot und Spring\nCloud"," erstellen kann. Ich staune einmal mehr über die immense Magie, die\nin ",[1002,36631,18586],{"href":36632,"rel":36633},"http://projects.spring.io/spring-boot/",[1006]," steckt. Und noch mehr, als ich höre, dass man Spring Boot\nApplikationen inzwischen sogar total easy-going als Unix Services ausführen kann.",[439,36636,36637,36640,36641,36644,36645,36648],{},[990,36638,36639],{},"Loft is in the air…"," Ich erklimme den weiten Weg nach oben ins Loft, um mir einen Vortrag über ",[448,36642,36643],{},"Docker für\nIntegrationtests"," von ",[448,36646,36647],{},"Stefan Hildebrandt"," anzuhören. Leider fehlt mir ein bisschen der rote Faden. Es wird sehr\nausführlich auf die Erzeugung von produktionsnahen Testdaten anhand von Produktionsdatenbanken eingegangen. Aufgrund des\nTitels hatte ich im Vorfeld irgendwie ein paar andere Erwartungen.",[439,36650,36651,36652,36654,36655,36658],{},"Ich bin mir beim nächsten Slot aufgrund der Themen sehr unschlüssig, welchen Vortrag ich mir anhören soll. Während der\nMittagspause werde ich darauf hingewiesen, dass ",[448,36653,351],{}," wohl sehr gute Talks halten soll. Daher folge ich\nmeinen Kollegen in seinen Vortrag über ",[448,36656,36657],{},"Event Sourcing",". Und ich werde nicht enttäuscht. Sehr anschaulich, mit\nkreativen Folien wird Event Sourcing anhand von Spiele-Programmierung erklärt. Das hat sich gelohnt.",[439,36660,36661],{},[1002,36662,36665],{"href":36663,"rel":36664},"https://media.synyx.de/uploads//2015/09/Java_Testing_Bingo.jpg",[1006],[2205,36666],{"alt":36667,"src":36668},"Java_Testing_Bingo","https://media.synyx.de/uploads//2015/09/Java_Testing_Bingo-300x235.jpg",[439,36670,36671,36672,36675,36676,36679,36680,2040,36683,36686,36687,36692,36693,36698],{},"Nach mehreren Runden Buzzword-Bingo (",[990,36673,36674],{},"It’s all about Microservices!",") habe ich nun auch Lust auf eine Runde ",[448,36677,36678],{},"Java\nTesting Bingo",". Tatsächlich verteilen ",[448,36681,36682],{},"Martin Klose",[448,36684,36685],{},"Jan Hartung"," zu Beginn des Vortrags Bingo-Karten. Wir\nerleben eine Reise durch verschiedene Java Testing Libraries, sehen diverse Code Snippets und am Ende bleibt bei mir vor\nallem hängen, dass ich mir ",[1002,36688,36691],{"href":36689,"rel":36690},"http://joel-costigliola.github.io/assertj/",[1006],"AssertJ"," als Alternative\nzu ",[1002,36694,36697],{"href":36695,"rel":36696},"http://hamcrest.org/",[1006],"Hamcrest"," zu Gemüte führen werde.",[439,36700,36701,36702,36705,36706,36709],{},"…und dann ist man plötzlich beim letzten BED-Con Vortrag angekommen. Alle reden über’s Wetter, aber nur wenige tun dies\nmit Requisiten und Sound-Effekten. ",[448,36703,36704],{},"Timmo Freudl-Gierke"," stellt uns in seinem Vortrag ",[448,36707,36708],{},"Sharding Weather"," die\nDomäne Wetter genauer vor und erläutert seine Erfahrungen zum Aufbewahren von großen Datenmengen wie Wetterdaten.",[439,36711,36712],{},[1002,36713,36716],{"href":36714,"rel":36715},"https://media.synyx.de/uploads//2015/09/Bedcon_Truppe.jpg",[1006],[2205,36717],{"alt":36718,"src":36719},"Die BED-Con Truppe","https://media.synyx.de/uploads//2015/09/Bedcon_Truppe-300x225.jpg",[439,36721,36722],{},"Eine weitere BED-Con geht zu Ende…aber die nächste kommt bestimmt 🙂",[439,36724,36725],{},"Bis dahin kann man noch eine Weile zehren von den Erkenntnissen, Ideen und Gedankenanstößen, die man so mit nach Hause\nnimmt.",[439,36727,36728],{},"Es hat sich mal wieder gelohnt!",{"title":469,"searchDepth":507,"depth":507,"links":36730},[36731,36732,36733],{"id":36392,"depth":547,"text":36393},{"id":36421,"depth":547,"text":36422},{"id":36589,"depth":547,"text":36590},[1031],"2015-09-24T10:32:43","Am 17. und 18. September 2015 fanden die fünften Berlin Expert Days (kurz BED-Con) statt.","https://synyx.de/blog/synyx-berlin-expert-days-2015/",{},"/blog/synyx-berlin-expert-days-2015",{"title":36365,"description":36736},"blog/synyx-berlin-expert-days-2015",[36743,20860,9556,389],"bed-con","Am 17. und 18. September 2015 fanden die fünften Berlin Expert Days (kurz BED-Con) statt. Erst beim Schreiben dieses Blog Posts habe ich mit Erstaunen festgestellt, dass es für mich…","euXIf3mGcxaUffJB88acRHQmdKbk9mPnJ5UFtsMwGiA",{"id":36747,"title":36748,"author":36749,"body":36750,"category":37173,"date":37174,"description":37175,"extension":1034,"link":37176,"meta":37177,"navigation":916,"path":37178,"seo":37179,"slug":36754,"stem":37181,"tags":37182,"teaser":37183,"__hash__":37184},"blog/blog/iotcon-2015-berlin.md","IoTCon 2015 Berlin",[397],{"type":432,"value":36751,"toc":37149},[36752,36755,36764,36771,36775,36779,36790,36811,36824,36827,36836,36842,36850,36854,36865,36868,36877,36880,36887,36891,36895,36916,36924,36935,36939,36942,36945,36948,36951,36955,36962,36965,36969,36972,36975,36982,36988,36991,36997,37001,37008,37011,37014,37018,37024,37028,37031,37034,37038,37042,37045,37048,37052,37055,37085,37089,37092,37096,37103,37107,37117,37120,37123,37127,37130,37133,37136,37143,37146],[435,36753,36748],{"id":36754},"iotcon-2015-berlin",[439,36756,36757,36758,36763],{},"From August 31st to September 2nd 2015 I attended the ",[1002,36759,36762],{"href":36760,"rel":36761},"https://iotcon.de/2015/",[1006],"Internet of Things Conference"," at the\nnHow hotel in Berlin. Monday was a workshop day, while Tuesday and Wednesday were the actual conference days with talks\nand keynotes. There was a second workshop day on Thursday, in which I did not participate.",[439,36765,36766,36767,36770],{},"Below the fold you will find a (not",[990,36768,36769],{},"that",") short summary of the workshops and talks I attended.",[3938,36772,36774],{"id":36773},"monday-workshop-day","Monday: Workshop Day",[1065,36776,36778],{"id":36777},"game-of-things","Game of Things",[439,36780,36781,36782,36785,36786,36789],{},"A workshop playing the ",[990,36783,36784],{},"Game of Things:"," A brainstorming game for coming up with new ideas for – well – ",[990,36787,36788],{},"things"," (to\nconnect to the Internet). The players split into teams and the game is split into three phases:",[439,36791,36792,8351,36795,36798,36799,36802,36803,36806,36807,36810],{},[448,36793,36794],{},"Phase 1:",[990,36796,36797],{},"Finding Ideas."," There is ",[990,36800,36801],{},"business driver"," set by the game master or – usually – the employer. All teams\nwork with the same business driver. Next, each team pulls one or more ",[990,36804,36805],{},"technology cards"," from the deck and an ",[990,36808,36809],{},"event\ncard"," that serves as a further constraint. Now the team tries to combine the business driver with their technologies –\nwithin the constraint given by the event card, if possible – to come up with ideas.",[439,36812,36813,8351,36816,36819,36820,36823],{},[448,36814,36815],{},"Phase 2:",[990,36817,36818],{},"Auctioning Ideas."," Each team has 30 seconds to present each of their ideas, then the other teams can ",[990,36821,36822],{},"bid","\non the ideas. The team with the highest bid receives the idea, the team that came up with it receives the money. Money\nearned in an auction can be used in later rounds to buy ideas.",[439,36825,36826],{},"Phases 1 & 2 are repeated several times.",[439,36828,36829,8351,36832,36835],{},[448,36830,36831],{},"Phase 3:",[990,36833,36834],{},"Finale."," Each team ranks the ideas they bought and chooses the best one, possibly combining several ideas\ninto one. They then present their idea and in the end all players – no longer in teams – vote on the ideas to find the\nbest idea of the game. The team that came up with the idea and the team that bought it win the game.",[439,36837,36838,36839],{},"The winning idea of our game was: ",[990,36840,36841],{},"Seamless navigation with virtual graffiti as direction markers (via smartglasses).",[439,36843,36844,36845,36849],{},"The game is free and open source and can be downloaded ",[1002,36846,22916],{"href":36847,"rel":36848},"http://www.maibornwolff.de/game-of-things-download",[1006]," (\nGerman only).",[1065,36851,36853],{"id":36852},"hands-on-iot","Hands on IoT",[439,36855,36856,36857,36864],{},"This was a workshop for using the ",[990,36858,36859],{},[1002,36860,36863],{"href":36861,"rel":36862},"https://www.arduino.cc/en/Main/ArduinoBoardYun",[1006],"Arduino Yún"," to connect simple\nelectronics to the Internet. In this case, we used a pushbutton to send tweets.",[439,36866,36867],{},"The Arduino Yún is an Arduino micro-controller with an embedded Linux system on board, which can use Wifi or Ethernet\nto connect to the Internet. While we used a simple pushbutton, it would be easy to use various sensors to send data over\nthe Internet.",[439,36869,36870,36871,36876],{},"Instead of talking to the twitter API directly we used ",[1002,36872,36875],{"href":36873,"rel":36874},"http://temboo.com/",[1006],"Temboo",", an IoT API service which makes it\nvery easy to connect electronics to a diverse set of web services. It can even generate your Arduino code for you.",[439,36878,36879],{},"All in all, this was a basic introductory workshop to IoT with Arduino.",[439,36881,36882,36883,1402],{},"You can download the\nslides ",[1002,36884,22916],{"href":36885,"rel":36886},"https://www.copy.com/s/t%3Ak9oyktMwsfuQc3Y2%3Bp%3A%252Farduino_iotcon2015_berlin.pdf%3Boid%3A615",[1006],[3938,36888,36890],{"id":36889},"tuesday-conference-day-i","Tuesday: Conference Day I",[1065,36892,36894],{"id":36893},"the-tangible-mind","The Tangible Mind",[439,36896,28973,36897,36900,36901,36904,36905,36908,36909,36912,36913,1402],{},[990,36898,36899],{},"very"," interesting talk about how technology has to become more ",[990,36902,36903],{},"tangible",", how the ",[990,36906,36907],{},"user experience"," is really the\n",[990,36910,36911],{},"human experience"," and has to include ",[990,36914,36915],{},"all senses",[439,36917,36918,36919,1402],{},"The talk is hard to summarize in a short blog post, but you can read a longer blog post on\nthe ",[1002,36920,36923],{"href":36921,"rel":36922},"http://andreakrajewski.com/2015/08/30/the-tangible-mind/",[1006],"speaker’s blog",[439,36925,36926,36927,8351,36932,1402],{},"TL;DR: ",[448,36928,36929],{},[990,36930,36931],{},"We",[990,36933,36934],{},"are the Internet of Things",[1065,36936,36938],{"id":36937},"how-do-i-tell-it-to-my-smartglasses","How do I tell it to my smartglasses?",[439,36940,36941],{},"How do we communicate with our smartglasses? Speech recognition is flexible and hands-free, but unreliable. Gestures\nare flexible and natural but hard to learn and to implement. Using a phone raises the question: Why have smartglasses at\nall? Wearables are mostly still experimental and unreliable or clumsy.",[439,36943,36944],{},"The answer is to use several methods at once to increase reliability.",[439,36946,36947],{},"For example pointing with an arm that wears a smartwatch, looking in the same direction and speaking a command. Or\ndetecting the proximity of another smart item and the user looking at it while making a simple gesture.",[439,36949,36950],{},"There is still a lot of research and development necessary and many technical hurdles to take in this area.",[1065,36952,36954],{"id":36953},"keynote-connected-2020-the-intersection-of-technology-fashion","Keynote: Connected 2020: The Intersection of Technology + Fashion",[439,36956,36957,36958,36961],{},"A short, ",[990,36959,36960],{},"fluffy"," talk about how designers are extremely important for wearables. To create wearables that people will\nwant to wear, technologists and designers need to cooperate and communicate with each other.",[439,36963,36964],{},"Current examples of such cooperation are Misfit & Swarovski or OMSingnal & Ralph Lauren.",[1065,36966,36968],{"id":36967},"privacy-on-the-internet-of-things","Privacy on the Internet of Things",[439,36970,36971],{},"A short talk about the current state of EU privacy laws and how they apply to the IoT.",[439,36973,36974],{},"While the IoT lives from gathering and analyzing data, we have to take great care what data we collect and whom we share\nit with.",[439,36976,36977,36978,36981],{},"Anonymized data can be saved without problems, but personally identifying data requires opt-in by the user. A decision\nto opt-in must be made freely and be well informed ",[990,36979,36980],{},"before"," data collection starts. It shouldn’t be hidden in the terms\nof service or vague.",[439,36983,36984,36985],{},"“Medical” data, which also includes religious or sexual preference, IQ etc. is ",[990,36986,36987],{},"especially protected.",[439,36989,36990],{},"Transferring the data outside the EU is very problematic and has strict requirements.",[439,36992,36993,36994],{},"When more than 9 persons handle data, the company needs a dedicated ",[990,36995,36996],{},"data protection officer**.",[1065,36998,37000],{"id":36999},"hello-ibeacon","Hello iBeacon",[439,37002,37003,37004,37007],{},"A short introduction to iBecaons. iBeacons are small transmitters that use ",[990,37005,37006],{},"Bluetooth Low Energy"," (BLE) to broadcast a\nspecific ID. These can be identified by apps installed on a smartphone. This is useful for e.g. tracking users or\nproviding indoor navigation.",[439,37009,37010],{},"iBeacon uses no authentication and is easy to emulate with Android devices.",[439,37012,37013],{},"Google has launched a competing technology named Eddystone.",[1065,37015,37017],{"id":37016},"connecting-things-via-your-smartphone","Connecting Things via your Smartphone",[439,37019,37020,37021,37023],{},"This talk was about how we can connect ",[990,37022,36788],{}," to the internet without a dedicated connection. To do this, the things\nconnect via BLE to passing smartphones that run a specific app, which forwards the thing’s data to the Internet. This is\nparticularly useful for retail spaces or similar, where employees (with smartphones in their pockets) roam.",[1065,37025,37027],{"id":37026},"smart-home-but-where-is-the-intelligence","Smart Home: But where is the intelligence?",[439,37029,37030],{},"Most smart homes these days only act on direct user input. That’s not very “smart”. Home life, especially for families,\nis too complex to model with simple rules or patterns. Automatically detecting the inhabitants’ situations is unreliable\nand difficult to implement.",[439,37032,37033],{},"A solution would be situation-based rulesets – “recipes” – which the user activates either manually or automatically\nthrough proximity (via beacons or GPS), and which can be shared with other smart home inhabitants. Instead of using\nswitches to activate recipes, a personality á la Siri or Jarvis could be implemented.",[3938,37035,37037],{"id":37036},"wednesday-conference-day-ii","Wednesday: Conference Day II",[1065,37039,37041],{"id":37040},"keynote-design-from-manufacturing","Keynote: Design from Manufacturing",[439,37043,37044],{},"Mengmeng Chen of Seeed Studio talked about how her company helps designers and makers go from a garage prototype to\nmanufacturing tens of thousands units in China. Seeed Studio is based in Shenzhen in China but also has a branch in\nCalifornia, where they can get prototypes and small series done in a matter of days. They also provide experience and\ncontacts in the world of Chinese manufacturing, which is complicated and hard to break into.",[439,37046,37047],{},"Seeed Studio also provides a development platform, including a Hardware Development Kit, an Open Parts Library and\nShared Supply Chain and short turnaround time Prototype Production.",[1065,37049,37051],{"id":37050},"iot-cloud-solutions-the-why-and-how","IoT Cloud Solutions: The why and how",[439,37053,37054],{},"The Internet of Things lives on data, and data needs to be stored and analyzed. Building a data-platform from scratch\nmeans facing a lot of problems that other people have already solved. There is no need to reinvent the wheel, just focus\non what you do best and choose partners for everything else. That is where cloud platforms come into play.",[439,37056,37057,37058,520,37063,520,37068,7267,37073,37078,37079,18175,37082,1402],{},"This talk compared several different such platforms, such\nas ",[1002,37059,37062],{"href":37060,"rel":37061},"https://xively.com/",[1006],"xively",[1002,37064,37067],{"href":37065,"rel":37066},"https://evrythng.com/",[1006],"Evrythng",[1002,37069,37072],{"href":37070,"rel":37071},"https://www.pubnub.com/",[1006],"PubNub",[1002,37074,37077],{"href":37075,"rel":37076},"https://thethings.io/",[1006],"thethings.io"," in the categories:",[990,37080,37081],{},"Understanding Business, Connect the Thing, Learn from\nCustomers, Back-End, Apps, Tools, Interoperability",[990,37083,37084],{},"Support",[1065,37086,37088],{"id":37087},"electronics-for-software-developers","Electronics for Software developers",[439,37090,37091],{},"A very basic crash course that contrasted software development concepts with hardware development concepts. Basically a\nslightly modified “Arduino for beginners” talk.",[1065,37093,37095],{"id":37094},"keynote-mx3d-bridge-project","Keynote: MX3D Bridge project",[439,37097,37098,37099,37102],{},"MX3D is a company that researches 3D printing technologies. They design and sell organically designed furniture and as\ntheir new prestige project decided to print a ",[990,37100,37101],{},"bridge"," across a canal in Amsterdam with their new Multi-Axis Metal 3D\nPrinter.",[1065,37104,37106],{"id":37105},"augmented-reality-state-of-the-union","Augmented Reality – State of the Union",[439,37108,37109,37110,18175,37113,37116],{},"An overview over the current state of AR. The technology has found wide acceptance in ",[990,37111,37112],{},"sales",[990,37114,37115],{},"advertisement"," with\nstatic installations and the occasional smartphone or tablet app. Smartglasses are only in use for professional\napplications like maintenance work.",[439,37118,37119],{},"Apple has bought and closed down Metaio, the provider of the most advanced AR SDK, leaving Wikitude as the new leader in\nSDKs. Other platforms are either way behind or still in development.",[439,37121,37122],{},"The future of AR is looking very good with improved smartglasses coming to market soon and new technologies like\nRGB+Depth information (RGB+D), light estimation, thermal touch and face recognition.",[1065,37124,37126],{"id":37125},"mobile-payment-the-future-of-payment","Mobile Payment: The future of payment",[439,37128,37129],{},"While mobile payment is yet in it’s infancy in Germany, other countries are making great leaps. The major players are\nApple, Google and Samsung, and this competition creates a lot of innovation. Unlike earlier attempts, Google’s new\noffering is independent of manufacturers and network operators.",[439,37131,37132],{},"Mobile Payment has become much more secure with smartcard functionality being emulated on modern smartphones.",[3938,37134,37135],{"id":18381},"Final thoughts",[439,37137,37138,37139,37142],{},"The location was well chosen for the few hundred attendees and the food was really good for the most part. I wish I\ncould have eaten more than one of those ",[990,37140,37141],{},"amazing"," brownies 😉.",[439,37144,37145],{},"The keynotes were all surprisingly light on substance, but most of the other talks were quite interesting, sometimes\neven enlightening. The “Expo” on the other hand was tiny and completely focused on mobile development, catering only to\nthe simultaneous MobileTech Conference.",[439,37147,37148],{},"All in all a decent conference, but considering the price I’m not likely to attend next year. I could probably have\nattended two equally well-hosted conferences for not much more – if any – money.",{"title":469,"searchDepth":507,"depth":507,"links":37150},[37151,37155,37164,37172],{"id":36773,"depth":507,"text":36774,"children":37152},[37153,37154],{"id":36777,"depth":547,"text":36778},{"id":36852,"depth":547,"text":36853},{"id":36889,"depth":507,"text":36890,"children":37156},[37157,37158,37159,37160,37161,37162,37163],{"id":36893,"depth":547,"text":36894},{"id":36937,"depth":547,"text":36938},{"id":36953,"depth":547,"text":36954},{"id":36967,"depth":547,"text":36968},{"id":36999,"depth":547,"text":37000},{"id":37016,"depth":547,"text":37017},{"id":37026,"depth":547,"text":37027},{"id":37036,"depth":507,"text":37037,"children":37165},[37166,37167,37168,37169,37170,37171],{"id":37040,"depth":547,"text":37041},{"id":37050,"depth":547,"text":37051},{"id":37087,"depth":547,"text":37088},{"id":37094,"depth":547,"text":37095},{"id":37105,"depth":547,"text":37106},{"id":37125,"depth":547,"text":37126},{"id":18381,"depth":507,"text":37135},[1030],"2015-09-18T13:55:51","From August 31st to September 2nd 2015 I attended the Internet of Things Conference at the\\nnHow hotel in Berlin. Monday was a workshop day, while Tuesday and Wednesday were the actual conference days with talks\\nand keynotes. There was a second workshop day on Thursday, in which I did not participate.","https://synyx.de/blog/iotcon-2015-berlin/",{},"/blog/iotcon-2015-berlin",{"title":36748,"description":37180},"From August 31st to September 2nd 2015 I attended the Internet of Things Conference at the\nnHow hotel in Berlin. Monday was a workshop day, while Tuesday and Wednesday were the actual conference days with talks\nand keynotes. There was a second workshop day on Thursday, in which I did not participate.","blog/iotcon-2015-berlin",[],"From August 31st to September 2nd 2015 I attended the Internet of Things Conference at the nHow hotel in Berlin. Monday was a workshop day, while Tuesday and Wednesday were…","tVFeEXG1Hvv-Ftl46yLXQ7BzYziKw3KPf3hxvvaTP7Y",{"id":37186,"title":37187,"author":37188,"body":37189,"category":37359,"date":37360,"description":37361,"extension":1034,"link":37362,"meta":37363,"navigation":916,"path":37364,"seo":37365,"slug":37193,"stem":37366,"tags":37367,"teaser":37374,"__hash__":37375},"blog/blog/how-to-monitor-jaxrsjersey-applications.md","How to monitor JAXRS/Jersey applications",[42],{"type":432,"value":37190,"toc":37357},[37191,37194,37197,37208,37216,37221,37224,37227,37236,37239,37298,37301,37308,37311,37328,37338,37345,37352,37355],[435,37192,37187],{"id":37193},"how-to-monitor-jaxrsjersey-applications",[439,37195,37196],{},"If you nowadays visit a conference, you still might get into contact with sessions where people are talking about\nmonitoring or at least some aspects of it and ALM (application lifecycle management) is a really important discipline a\nteam or project should should take into account right from the beginning and no, this doesn’t mean that you should trim\nor optimize prematurely, but to have an eye on it. Next to the developers and operators we can identify many more\nstakeholders who are interested in the data, but generally they prefer a different view on the data.",[994,37198,37199,37202,37205],{},[997,37200,37201],{},"Who is the audience, who uses the API in what version?",[997,37203,37204],{},"How can I economize resources, but for specific cases only?",[997,37206,37207],{},"How can I use the data to prevent accidents or control specific nodes?",[439,37209,37210,37211,37215],{},"The code can be found at ",[1002,37212,27413],{"href":37213,"rel":37214},"https://github.com/synyx/meter.git",[1006]," and this article shall, simply spoken, show the\nmotivation behind it. So, in one sentence I would say:",[11947,37217,37218],{},[439,37219,37220],{},"We want fine grained statistics about things that happen without writing much integration code and to partition the\ndata at runtime using the provided input.",[439,37222,37223],{},"Enabling monitoring ‘always’ require us to follow the same pattern (do something before and optionally do something\nafter), so it would be nice to simply not do the same things over and over again. Monitoring can be seen as a classical\ncross cutting concern and even if we loose some control at implementation level, we can profit on less maintenance\neffort and a better system design – which is a good trade in my opinion.",[439,37225,37226],{},"When I hear the words ‘cross cutting concerns’ then instantly AOP (aspect oriented programming) comes into my mind and\nthose techniques shall pave the way as described in the sentence above. It can be further used independently of the\nunderlying technology – of course we need some technology glue to wire the aspects, but this must generally be done only\nonce.",[439,37228,37229,37230,37235],{},"Many public APIs follow the REST pattern today. So we decided to go with Jersey first as it’s a great framework for\nbuilding enterprise REST services. Jersey uses HK2 internally and you can vary almost every part at runtime with a\nfluent java API – If you know Google Guice then you might get an impression now. HK2 supports AOP through the libraries\nfrom the AOP Alliance and you can hook this process easily following the guidelines of\nfrom ",[1002,37231,37234],{"href":37232,"rel":37233},"https://hk2.java.net/2.2.0/aop-example.html",[1006],"aop-example",". That’s a pretty good news and a mighty joinpoint for\nmetrics, a quality library to gather runtime statistics.",[439,37237,37238],{},"Right now, our recipe contains Jersey, Metrics, AOP, and Java annotations as markup and if we plug everything together\nwe achieve something like this:",[464,37240,37242],{"className":709,"code":37241,"language":711,"meta":469,"style":469}," @GET\n @Metric (\n timers = @Timer,\n histograms = @Histogram (value = \"#size\", measure = BambooResponseSize.class),\n counters = @Counter (value = \"{color}\", kind = Kind.Error)\n )\n @Path (\"{name}\")\n public String echo (@PathParam (\"name\") String name, @QueryParam (\"locale\") String locale, @DefaultValue (\"Green\") @QueryParam (\"color\") Color color) {\n validate (color);\n return service.call (name + \"::\" + locale);\n }\n\n",[471,37243,37244,37249,37254,37259,37264,37269,37274,37279,37284,37289,37294],{"__ignoreMap":469},[474,37245,37246],{"class":476,"line":477},[474,37247,37248],{}," @GET\n",[474,37250,37251],{"class":476,"line":507},[474,37252,37253],{}," @Metric (\n",[474,37255,37256],{"class":476,"line":547},[474,37257,37258],{}," timers = @Timer,\n",[474,37260,37261],{"class":476,"line":584},[474,37262,37263],{}," histograms = @Histogram (value = \"#size\", measure = BambooResponseSize.class),\n",[474,37265,37266],{"class":476,"line":607},[474,37267,37268],{}," counters = @Counter (value = \"{color}\", kind = Kind.Error)\n",[474,37270,37271],{"class":476,"line":642},[474,37272,37273],{}," )\n",[474,37275,37276],{"class":476,"line":663},[474,37277,37278],{}," @Path (\"{name}\")\n",[474,37280,37281],{"class":476,"line":694},[474,37282,37283],{}," public String echo (@PathParam (\"name\") String name, @QueryParam (\"locale\") String locale, @DefaultValue (\"Green\") @QueryParam (\"color\") Color color) {\n",[474,37285,37286],{"class":476,"line":700},[474,37287,37288],{}," validate (color);\n",[474,37290,37291],{"class":476,"line":913},[474,37292,37293],{}," return service.call (name + \"::\" + locale);\n",[474,37295,37296],{"class":476,"line":920},[474,37297,1276],{},[439,37299,37300],{},"One nice thing to mention is, that everything managed by HK2 can be annotated and therefore measured – including\nresource methods and services from the DI container – fine grained control at method level :).",[439,37302,37303,37304,37307],{},"So what do I mean with ",[990,37305,37306],{},"a partition at runtime"," then?",[439,37309,37310],{},"You can find some the JAXRS annotations in the previous example, e.g. PathParam, QueryParam and of course, Jersey knows\nthe interpretation of the parameters, but do we know as well? Yes and that’s pretty awesome, as it allows us to",[994,37312,37313,37316,37319,37322,37325],{},[997,37314,37315],{},"build metrics which are partitioned by customer levels : .., silver, gold, platinum",[997,37317,37318],{},"build metrics for versioned APIs: /v1/, …, /v9/ — anyone using v1, costs?",[997,37320,37321],{},"build metrics to track clients by geography, cookie, header",[997,37323,37324],{},"build metrics that measure errors only",[997,37326,37327],{},"build metrics for if-you-can-name-it-you-can-measure-it things.",[439,37329,37330],{},[990,37331,37332,37333],{},"A conversion takes place prior, so you can run every custom evaluation\nbeforehand: ",[1002,37334,37337],{"href":37335,"rel":37336},"https://jersey.java.net/documentation/latest/user-guide.html#d0e2152",[1006],"userguide",[439,37339,37340,37341,37344],{},"And thats’ what I really like the most 🙂 – You have access to runtime values from ALL services, resources managed by\nJersey/HK2 to configure the ",[448,37342,37343],{},"usecase"," you want.",[439,37346,37347,37348,37351],{},"If you like to contribute or participate on items mentioned on the roadmap or issue something, thats not on the roadmap\n🙂 then feel free to visit us at ",[1002,37349,27413],{"href":37213,"rel":37350},[1006],", or even if you want to try out the example\nproject to get a first impression.",[439,37353,37354],{},"Feedback is highly welcome and many thanks from me to the developers of Jersey/HK2 and Metrics for their great work –\nnice piece of software.",[1024,37356,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":37358},[],[1030],"2015-07-29T09:02:17","If you nowadays visit a conference, you still might get into contact with sessions where people are talking about\\nmonitoring or at least some aspects of it and ALM (application lifecycle management) is a really important discipline a\\nteam or project should should take into account right from the beginning and no, this doesn’t mean that you should trim\\nor optimize prematurely, but to have an eye on it. Next to the developers and operators we can identify many more\\nstakeholders who are interested in the data, but generally they prefer a different view on the data.","https://synyx.de/blog/how-to-monitor-jaxrsjersey-applications/",{},"/blog/how-to-monitor-jaxrsjersey-applications",{"title":37187,"description":37196},"blog/how-to-monitor-jaxrsjersey-applications",[37368,37369,711,37370,37371,37372,37373],"aop","hk2","jaxrs","jersey","metrics","rest","If you nowadays visit a conference, you still might get into contact with sessions where people are talking about monitoring or at least some aspects of it and ALM (application…","9VLoatX0lXioeJDq5DXTaDizrmyuyEy9Kq7czFYrBAU",{"id":37377,"title":37378,"author":37379,"body":37380,"category":37396,"date":37397,"description":37398,"extension":1034,"link":37399,"meta":37400,"navigation":916,"path":37401,"seo":37402,"slug":37384,"stem":37403,"tags":37404,"teaser":37405,"__hash__":37406},"blog/blog/synyx-sportelt-unser-lauf-beim-b2run.md","synyx sportelt: Unser Lauf beim B2Run!",[178],{"type":432,"value":37381,"toc":37394},[37382,37385,37388],[435,37383,37378],{"id":37384},"synyx-sportelt-unser-lauf-beim-b2run",[439,37386,37387],{},"Am 11. Juni fand in Karlsruhe der B2Run statt, die siebte Auflage der deutschlandweiten Firmenlaufmeisterschaften in 12\nStädten. Dieses Jahr nahmen über 7000 Läufer statt – und immerhin 9 Läufer davon stellten wir. Als sich vor dem Lauf\nnach und nach mehr Teilnehmer am Stadion einfanden, war das für uns als erstmalige Teilnehmer durchein ein\neindrucksvoller Anblick. Entsprechend unserer vermuteten Laufleistung haben wir uns in den verschiedenen Startblöcken\neinsortiert und harrten gespannt der Dinge, die da kommen werden.",[439,37389,37390],{},[2205,37391],{"alt":37392,"src":37393},"B2Run2","https://media.synyx.de/uploads//2015/07/B2Run2-300x169.jpg",{"title":469,"searchDepth":507,"depth":507,"links":37395},[],[1031],"2015-07-06T09:53:23","Am 11. Juni fand in Karlsruhe der B2Run statt, die siebte Auflage der deutschlandweiten Firmenlaufmeisterschaften in 12\\nStädten. Dieses Jahr nahmen über 7000 Läufer statt – und immerhin 9 Läufer davon stellten wir. Als sich vor dem Lauf\\nnach und nach mehr Teilnehmer am Stadion einfanden, war das für uns als erstmalige Teilnehmer durchein ein\\neindrucksvoller Anblick. Entsprechend unserer vermuteten Laufleistung haben wir uns in den verschiedenen Startblöcken\\neinsortiert und harrten gespannt der Dinge, die da kommen werden.","https://synyx.de/blog/synyx-sportelt-unser-lauf-beim-b2run/",{},"/blog/synyx-sportelt-unser-lauf-beim-b2run",{"title":37378,"description":37387},"blog/synyx-sportelt-unser-lauf-beim-b2run",[],"Am 11. Juni fand in Karlsruhe der B2Run statt, die siebte Auflage der deutschlandweiten Firmenlaufmeisterschaften in 12 Städten. Dieses Jahr nahmen über 7000 Läufer statt – und immerhin 9 Läufer…","FgOagTI03W0mGG4USkYAaUSEjWzw6XDAwR38sW0UbI4",{"id":37408,"title":37409,"author":37410,"body":37411,"category":37607,"date":37608,"description":37609,"extension":1034,"link":37610,"meta":37611,"navigation":916,"path":37612,"seo":37613,"slug":37415,"stem":37615,"tags":37616,"teaser":37623,"__hash__":37624},"blog/blog/devoxx-poland-2015-summary.md","Devoxx Poland 2015 Summary",[391],{"type":432,"value":37412,"toc":37605},[37413,37416,37437,37447,37450,37455,37458,37475,37485,37490,37493,37496,37499,37509,37514,37517,37520,37530,37535,37538,37541,37546,37549,37552,37557,37560,37563,37566,37571,37574,37577,37580,37583,37586,37590,37593,37602],[435,37414,37409],{"id":37415},"devoxx-poland-2015-summary",[439,37417,37418,37419,37424,37425,37430,37431,37436],{},"So that’s it. Three days, 2.000 Developers from 20 countries, over 140 speakers from around the world, and one\noutstanding beautiful city. It is for the first time, when ",[1002,37420,37423],{"href":37421,"rel":37422},"http://devoxx.pl",[1006],"Devoxx Poland"," (previously known\nas ",[1002,37426,37429],{"href":37427,"rel":37428},"http://2014.33degree.org",[1006],"33rd Degree","), one of the most recognizable European Java Conference took place in Krakow,\nthe city of the polish kings, and one of the most important places in whole polish history. It took place from Monday to\nWednesday last week in the ",[1002,37432,37435],{"href":37433,"rel":37434},"http://www.icekrakow.pl/",[1006],"ICE Conference Center",", which is located directly by the Vistula\nriver, with beautiful view over the Wawel Royal Castle.",[439,37438,37439],{},[1002,37440,37443],{"href":37441,"rel":37442},"https://media.synyx.de/uploads//2015/06/IMG_20150624_110556.jpg",[1006],[2205,37444],{"alt":37445,"src":37446},"Inside the ICE Conference Center ","https://media.synyx.de/uploads//2015/06/IMG_20150624_110556-300x225.jpg",[439,37448,37449],{},"Now it’s time to write some short summary. Below is my personal list of conclusions and analysis about the current state\nof Java industry and where it is going to, based on that what I’ve heard and seen during the conference.",[439,37451,37452],{},[448,37453,37454],{},"1. The rise of Microservices",[439,37456,37457],{},"You might say – “yep, of course”. It is obvious for everyone who is following the development of Java & Web Ecosystem\nthat microservices are the hottest and fancies “new” “technology” of the year 2015. I counted that around 10 talks (\nfrom around 100 full-time presentations) were completely dedicated to them. Another couple or so discussed tools\nemerged to make them easier to monitor, deploy and deal with them. And nearly every other talk mentioned them.",[439,37459,37460,37461,37464,37465,37469,37470,37474],{},"I think we are in the peak phase. I remember two years ago at",[448,37462,37463],{},"GeeCON 2013","as I saw the\nfirst ",[1002,37466,24834],{"href":37467,"rel":37468},"http://2013.geecon.org/speakers/sam-newman.html",[1006]," about it. Now it explodes, and you could hear at Devoxx\neverything about it:from theory and principles, through architecture, best practices, tools supporting it, monitoring,\ndevops, to live coding demos. Most of this talks were very enthusiastic about it, although in the next few months I\nexpect some more skeptical or at least balanced talks. There was\nactually ",[1002,37471,24777],{"href":37472,"rel":37473},"https://web.archive.org/web/20150503201315/http://cfp.devoxx.pl:80/2015/talk/MZA-9564/Modularity_in_post_microservice_world",[1006],"\nlike this.",[439,37476,37477],{},[1002,37478,37481],{"href":37479,"rel":37480},"https://media.synyx.de/uploads//2015/06/IMG_20150623_154822.jpg",[1006],[2205,37482],{"alt":37483,"src":37484},"Microservices Live-Coding Demo","https://media.synyx.de/uploads//2015/06/IMG_20150623_154822-300x225.jpg",[439,37486,37487],{},[448,37488,37489],{},"2. Reactive and Resilient by default",[439,37491,37492],{},"The second most important topic at Devoxx was the resiliency and reactive programming, together with durability,\nasynchronous programming, circuit breaking and back pressure, which are all actually strongly connected with\nmicroservices.",[439,37494,37495],{},"Conclusion from the talks I have seen is quite obvious: if something can crash, it eventually will, most probably in the\nworst moment. That is why resiliency and recovery mechanisms are so important and should be not only a feature, but a\nmust, especially in the era of microservices.",[439,37497,37498],{},"The application (or actually the whole container or server) should be able to crash at any time and the supervisor\nshould take care of it. The other parts of the system should be able to continue their operation normally almost as if\nnothing had happened, circuit-breaking the failed system and using fall-back mechanisms, providing some simplified\ndata from other sources. And as soon the failed part of the system has been recovered, it should automatically back to\nnormal.",[439,37500,37501],{},[1002,37502,37505],{"href":37503,"rel":37504},"https://media.synyx.de/uploads//2015/06/PANO_20150622_155838.jpg",[1006],[2205,37506],{"alt":37507,"src":37508},"Main Room","https://media.synyx.de/uploads//2015/06/PANO_20150622_155838-300x123.jpg",[439,37510,37511],{},[448,37512,37513],{},"3. Functional Programming breaks (slowly) though",[439,37515,37516],{},"Third most popular and still very hot topic is functional programming (or rather, a soft of FP). Thank to lambda\nexpressions, streams API, CompletableFutur, (semi)closures and tools like RxJava programmers slowly adopt more and more\nfunctional concepts and start to think of computation rather as a pipeline processing of immutable data instead of\nthinking of it as a mutating of objects’ state.",[439,37518,37519],{},"Furthermore, more and more developers found it increasingly important to write the code in such a way that there is no\nmutable state, including code at the method-level. I guess this is also a side-effect of using the IoC Containers,\nwhere many objects are simply the global singletons, and introducing the state in it would case a serious performance\nproblems and bugs.",[439,37521,37522],{},[1002,37523,37526],{"href":37524,"rel":37525},"https://media.synyx.de/uploads//2015/06/PANO_20150624_174232.jpg",[1006],[2205,37527],{"alt":37528,"src":37529},"View over Wawel Castle from ICE Center","https://media.synyx.de/uploads//2015/06/PANO_20150624_174232-300x88.jpg",[439,37531,37532],{},[448,37533,37534],{},"4. Java 8 throttles the rise of the new languages",[439,37536,37537],{},"Two, three years ago, if you attend a Java conference, one of the most important thing which would be discussed was “Is\nthe Scala/Groovy/xyz the next Java?” or “What are the Java alternatives”. You won’t here it anymore. In some cases you\nwould hear that Scala/Groovy/xyz is better for implementing some kind of stuff like mathematical computations or data\npipeline processing or so. Or that some languages are better at concurrency and parallelism. Or that some languages are\nbetter for writing tests.",[439,37539,37540],{},"But there is no more doubt that Java will be soon replaced by other languages whatsoever, because with Java 8 and many (\nespecially “reactive”) mature libraries Java is simply good enough for most cases. At least for now.",[439,37542,37543],{},[448,37544,37545],{},"5. Java-Community focus on backend",[439,37547,37548],{},"Again, two, three years ago there very many talks about Mobile, Android, Web, Nodejs frameworks, new Javascript\nclient-side frameworks, and so called “Full Stack Developer”. In the past years on the java-Conference you can attend\nto many diverse talks – from Android Programming, through NodeJS and Javascript-Backend solutions and tool, to all the\nJavascript-Frontend stuff like AngularJS, EmberJS etc. It is not the case any more.",[439,37550,37551],{},"They all moved out to their own separate conferences, because no one doubts anymore that anyone can be a good Java (\nBackend) Developer, a good JS-Frontend Developer and Mobile Developer at once. There are simple to many problems to\nsolve on the “Backend”-Side, so one has to focus on Java-Code and (probably not so long and more) on “Data”.",[439,37553,37554],{},[448,37555,37556],{},"6. Big Data and NoSQL are here",[439,37558,37559],{},"Big Data and NoSQL were of course present, but it is not the same “Big Data” and the same NoSQL which it was for 3-4\nyears. Today we are not talking about what it is, what is the theory, etc. Today it is obvious for everyone, that there\nis nothing like “Big Data” and “NoSQL” databases.",[439,37561,37562],{},"There are only databases which better scale and handle some particular amount and type of data in particular\ncircumstances. That’s it, and the only thing we should do is learn how to recognize which data better fit to be stored\nin database X or Y, and how to use it properly. And the conference showed exactly this, that there are no SQL and NoSQL\ndatabases, but only that handle better data of the given characteristic.",[439,37564,37565],{},"There are simply databases which doing one thing better than another, for example better handle transactions and\nrelations between data, or scale very well but support no relations, or maybe are design to store and query text\ndocuments or graphs of objects.",[439,37567,37568],{},[448,37569,37570],{},"7. Spring is all what you need",[439,37572,37573],{},"It was probably for the first time I didn’t see any talk about alternative approach to Spring-Based Technologies (Java\nEE excluding).",[439,37575,37576],{},"This year there was nothing about any new or old frameworks. No Play Framework, dropwizard. No other smaller fancy\ntechnologies like RatPack or Spark Framework. Nothing. Zero. It has been always at least a couple talks about different\nframeworks and alternative approaches. Not this time. There wasn’t even Grails.",[439,37578,37579],{},"The same applies for the data-persistence solutions like ORMs for instance.",[439,37581,37582],{},"No new features in Hibernate, JPA. Nothing about jOOQ or myBatis/iBatis. Now it’s all about Spring Data. There is Spring\nData JPA, Spring Data MongoDB. Spring Data Neo4J, Cassandra, Redis, Elasticsearch, any more. There are also Spring\nsolutions for Big Data – Spring XD and for the microservices aka cloud stuff – Spring Cloud.",[439,37584,37585],{},"Spring is the only thing which you seems to need. Thus I’m waiting for the “Spring Developer” job titles instead of\n“Java Developer”, just like “SharePoint Developer” or “Liferay Developer” already.",[439,37587,37588],{},[448,37589,9392],{},[439,37591,37592],{},"Despite of many new buzzwords and “new” technologies, there was not technological revolution, not even close. Maybe\nmicroservices will revolutionize the way we are designing systems but they will do it not because they are a\nrevolutionary technology, but rather by combining many other technologies together instead of introducing something what\nis really new.",[439,37594,37595,37596,37601],{},"I think it might be true that the Java Industry and the IT world in general is in\nthe",[1002,37597,37600],{"href":37598,"rel":37599},"https://vimeo.com/130981099#t=2m56s",[1006],"inflection point",". New technologies aren’t a game-changer and every new\ntechnology needs more and more time to spread across the industry, so it feels like a little bit stagnant.",[439,37603,37604],{},"I have such a feeling that we have now a little bit time to catch our breath, right after the Cloud, Mobile, Big Data,\nAsynchronous, Functional and DevOps era and to prepare ourselves for the Next Big Thing, which is probably waiting for\nus around the corner and will pop up in the least expected moment.",{"title":469,"searchDepth":507,"depth":507,"links":37606},[],[1030],"2015-07-02T10:34:54","So that’s it. Three days, 2.000 Developers from 20 countries, over 140 speakers from around the world, and one\\noutstanding beautiful city. It is for the first time, when Devoxx Poland (previously known\\nas 33rd Degree), one of the most recognizable European Java Conference took place in Krakow,\\nthe city of the polish kings, and one of the most important places in whole polish history. It took place from Monday to\\nWednesday last week in the ICE Conference Center, which is located directly by the Vistula\\nriver, with beautiful view over the Wawel Royal Castle.","https://synyx.de/blog/devoxx-poland-2015-summary/",{},"/blog/devoxx-poland-2015-summary",{"title":37409,"description":37614},"So that’s it. Three days, 2.000 Developers from 20 countries, over 140 speakers from around the world, and one\noutstanding beautiful city. It is for the first time, when Devoxx Poland (previously known\nas 33rd Degree), one of the most recognizable European Java Conference took place in Krakow,\nthe city of the polish kings, and one of the most important places in whole polish history. It took place from Monday to\nWednesday last week in the ICE Conference Center, which is located directly by the Vistula\nriver, with beautiful view over the Wawel Royal Castle.","blog/devoxx-poland-2015-summary",[37617,37618,37619,711,15201,35896,37620,37621,37622,1426],"devoxx","devoxx-conference","devoxx-poland","reactive","resilent","rxjava","So that’s it. Three days, 2.000 Developers from 20 countries, over 140 speakers from around the world, and one outstanding beautiful city. It is for the first time, when Devoxx…","swhEzT6ky1uX0PSmt0ZAtGErUnMflFyFGUirSDhS_pw",{"id":37626,"title":37627,"author":37628,"body":37629,"category":37651,"date":37652,"description":37653,"extension":1034,"link":37654,"meta":37655,"navigation":916,"path":37656,"seo":37657,"slug":37633,"stem":37658,"tags":37659,"teaser":37660,"__hash__":37661},"blog/blog/unser-girls-day-2015.md","Unser Girls' Day 2015",[178],{"type":432,"value":37630,"toc":37649},[37631,37634,37637,37640,37643],[435,37632,37627],{"id":37633},"unser-girls-day-2015",[439,37635,37636],{},"Der 23. April war ein besonderer Tag für uns bei synyx. Im Rahmen des Girls Day 2015, hatten wir 12 Schülerinnen im\nAlter von 10 bis 14 Jahren aus der Region Karlsruhe bei uns zu Gast.",[439,37638,37639],{},"Wie wir in einem vorherigen Blogbeitrag bereits geschrieben haben, findet der Girls Day deutschlandweit einmal jährlich\nstatt. Ziel ist es, jungen Mädchen Berufsfelder aufzuzeigen und Interesse an Themen zu wecken, die fälschlicherweise oft\nnoch als typische “Männerberufe” angesehen werden.",[439,37641,37642],{},"Zwar haben wir bei synyx durch unsere regelmäßigen Devoxx4Kids-Veranstaltungen Erfahrung damit, junge Menschen an das\nThema IT heranzuführen, allerdings war dies unsere erste Teilnahme am Girls Day, und das erste Mal, dass wir\ntechnikaffine Kids in unseren “live” Bürobetrieb einbinden. Entsprechend groß war die Spannung, was uns erwarten wird (\nwobei die Spannung sicherlich auch bei den Mädels vorhanden gewesen war).",[439,37644,37645],{},[2205,37646],{"alt":37647,"src":37648},"20150423_121024","https://media.synyx.de/uploads//2015/06/20150423_121024-300x263.jpg",{"title":469,"searchDepth":507,"depth":507,"links":37650},[],[1031],"2015-06-16T10:38:43","Der 23. April war ein besonderer Tag für uns bei synyx. Im Rahmen des Girls Day 2015, hatten wir 12 Schülerinnen im\\nAlter von 10 bis 14 Jahren aus der Region Karlsruhe bei uns zu Gast.","https://synyx.de/blog/unser-girls-day-2015/",{},"/blog/unser-girls-day-2015",{"title":37627,"description":37636},"blog/unser-girls-day-2015",[],"Der 23. April war ein besonderer Tag für uns bei synyx. Im Rahmen des Girls Day 2015, hatten wir 12 Schülerinnen im Alter von 10 bis 14 Jahren aus der…","BukfK_6zih9WPUkO99nYvPc1d-msoWmWSupqj5e6Zho",{"id":37663,"title":37664,"author":37665,"body":37666,"category":37766,"date":37767,"description":37768,"extension":1034,"link":37769,"meta":37770,"navigation":916,"path":37771,"seo":37772,"slug":37670,"stem":37774,"tags":37775,"teaser":37777,"__hash__":37778},"blog/blog/lets-add-some-value-part2.md","Let's add some value (part2)",[83],{"type":432,"value":37667,"toc":37764},[37668,37671,37681,37684,37687,37690,37693,37696,37699,37702,37705,37708,37713,37722,37725,37728,37733,37736,37739,37744,37747,37750,37753,37756,37759],[435,37669,37664],{"id":37670},"lets-add-some-value-part2",[439,37672,37673,37674,37680],{},"In the ",[1002,37675,37679],{"href":37676,"rel":37677,"title":37678},"http://blog.synyx.de/2014/11/lets-add-some-value/",[1006],"Let’s add some value!","first part"," of my postings I talked\nabout the disadvantages of breaking epics down into technical stories and why it is preferable to create real user\nstories which enable us to deliver real value in each iteration.",[439,37682,37683],{},"In this part I´ll present objections that I have come across while talking with developers and product owners (POs)\nabout ‘creating value in each iteration’.",[439,37685,37686],{},"**#1 “We waste time and money by developing things which we do not need anymore once we finish the envisioned feature.”\n**",[439,37688,37689],{},"Sometimes we have an ideal scenario where we want to develop a big feature which can be broken down into smaller\nfeatures that each both generate real value and also naturally brings us closer towards our goal. But sometimes the only\nway to generate value early is by choosing sort of an alternative path which does not directly work towards our\nenvisioned goal.",[439,37691,37692],{},"Imagine a typical project where our goal is to make data which is currently managed via Excel available for customers in\nan online tool. But since the data is very complex it is not that simple to implement a good solution for the data\nadministration. So the team offers the idea to implement a CSV import as a fast possibility to get the data into the\nsystem. The PO however rejects the idea, reasoning that the import functionality will not be needed anymore once the\ndata administration is fully implemented.",[439,37694,37695],{},"As you may guess I did not make this scenario up. The PO I talked to in this situation just saw the extra work which did\nnot bring the project significantly closer to his original goal. My main point was that there was a chance, that we\nwould not even need the data administration once we had the CSV import since the users could go on managing their data\nwith their accustomed tools. But as it became clear that this was not an option I argued that the advantage of having a\nusable product very early would outweigh the extra cost of building the import feature.",[439,37697,37698],{},"The end of the story: Up till today the data admistration still does not cover all relevant cases and many parts of the\ndata are therefore still managed both in the new online tool and in the old Excel sheets. This might of course be a\nsign, that the import feature would also never have covered all the special cases but it would have been usable much\nearlier. The users waited over half a year for a usable feature because building the data administration took that long.",[439,37700,37701],{},"The lessons we should learn:",[439,37703,37704],{},"– Having a usable product early is worth some extra effort.",[439,37706,37707],{},"– An interim solution may prove to become a viable long-term solution.",[439,37709,37710],{},[448,37711,37712],{},"#2 “By starting with a small and simple implementation to solve problems early we can not get to a sophisticated\nfinal solution.”",[439,37714,37715,37716,37721],{},"Of course it is challenging for a team to build software which scales from a small solution to a large complex system\nwhich is still consistent. Therefore the big vision presented by the PO and future requirements which are already known\nshould always be considered when making technical decisions. But if we are always worried about developing the perfect\nsystem we must be careful not to produce an overengineered solution which does more than we really need. Features which\ndo not generate value are waste (see ",[1002,37717,37720],{"href":37718,"rel":37719},"http://en.wikipedia.org/wiki/Lean_manufacturing",[1006],"lean manufacturing",") and while\nhigh code quality generates value by ensuring expandability and maintainability, no one needs artificially bloated\nsolutions.",[439,37723,37724],{},"Also adding new requirements for a ‘complete’ product which was specifically designed to solve one epic story, can lead\nto more problems than adding requirements for a software which was developed step by step from the start.",[439,37726,37727],{},"I think the fundamental misconception behind this objection is the notion of software beeing eventually finished.\nBecause if we have a “final solution” in mind we automatically think that we can define all its requirements and based\non that a perfect plan. If such a perfect plan is possible for your complex problem: do not use an iterative approach!",[439,37729,37730],{},[448,37731,37732],{},"#3 “Only the complete feature will be adapted because partial solutions do not add enough value compared to existing\nsolutions”",[439,37734,37735],{},"This may of course be a valid argument from the PO, especially from a marketing perspective. Often enough I saw projects\nwhere a technical approach was chosen for a feature because “we can not deliver parts of it anyway”. If our product can\nnot go live after an iteration we can only generate virtual value. For example by providing a usable version for a small\ngroup of test users (beta version). Early feedback from real users should be valued highly!",[439,37737,37738],{},"Despite everything we should always be able to potentially go live because the situation may change or we may even find\na partial solution which would add value for some users at least. But this is only possible if we provide real features\nafter each iteration instead of technical parts which only function as prerequisites for real features in the future.",[439,37740,37741],{},[448,37742,37743],{},"#4 “My problem can not be broken down because it is just too complex”",[439,37745,37746],{},"This is the last objection I want to talk about. It is the one I hear the most and therefore it is also the one which\ninspired me to write this whole thing.",[439,37748,37749],{},"I admit it… sometimes it may just not be possible to build something that can be used by someone to solve a real problem\nafter one iteration.",[439,37751,37752],{},"But my point is that we come to this conclusion much too fast most of the time because it is easy. By telling ourselves\nthat the thing we are building is just too complex, we make it easier for us to fail. We do not have to commit to\ndeliver anything because we already convinced our stakeholders that it is not possible. If the whole project takes\nlonger than expected: “Well, we already told you that the task is very complex”. Agile was invented to tackle complex\nproblems! Great people all around the world use agile methods to solve things which are likely much more complex than\nthe problem we are working on right now. But if we are not able to deliver value, we are not agile.",[439,37754,37755],{},"If we want to prove that agile frameworks really do offer a great benefit for our business we should always strive for\nthe maximum satisfaction of our stakeholders.",[439,37757,37758],{},"Let us surprise our customers by delivering working software instead of explaining why they have to wait some more time\nfor it.",[439,37760,37761],{},[448,37762,37763],{},"Finding ways to generate real value early may be hard but it is worth the effort.",{"title":469,"searchDepth":507,"depth":507,"links":37765},[],[1030],"2015-06-03T16:20:25","In the first part of my postings I talked\\nabout the disadvantages of breaking epics down into technical stories and why it is preferable to create real user\\nstories which enable us to deliver real value in each iteration.","https://synyx.de/blog/lets-add-some-value-part2/",{},"/blog/lets-add-some-value-part2",{"title":37664,"description":37773},"In the first part of my postings I talked\nabout the disadvantages of breaking epics down into technical stories and why it is preferable to create real user\nstories which enable us to deliver real value in each iteration.","blog/lets-add-some-value-part2",[9546,37776,9557],"processes","In the first part of my postings I talked about the disadvantages of breaking epics down into technical stories and why it is preferable to create real user stories which…","gu5llGQnrBczvGBCYzR23C23GWqEHxoMN1RhTMhunhQ",{"id":37780,"title":37781,"author":37782,"body":37783,"category":38050,"date":38051,"description":38052,"extension":1034,"link":38053,"meta":38054,"navigation":916,"path":38055,"seo":38056,"slug":37787,"stem":38058,"tags":38059,"teaser":38060,"__hash__":38061},"blog/blog/entwicklertag-karlsruhe-2015.md","Entwicklertag Karlsruhe 2015",[97,332],{"type":432,"value":37784,"toc":38038},[37785,37788,37796,37798,37802,37805,37823,37838,37843,37847,37860,37865,37867,37871,37880,37883,37886,37895,37901,37905,37932,37936,37939,38011,38013,38021,38024,38033],[435,37786,37781],{"id":37787},"entwicklertag-karlsruhe-2015",[439,37789,13082,37790,37795],{},[1002,37791,37794],{"href":37792,"rel":37793},"http://entwicklertag.de/karlsruhe/2015/",[1006],"Karlsruher Entwicklertage"," hatten Geburtstag! Zehn Jahre alt wurden sie\nund das mussten wir synyxler uns anschauen. Wir freuten uns auf hochwertige Talks und das Wiedersehen mit vielen\nKarlsruher Kollegen und bekannten Gesichtern der nationalen Entwicklerszene. Und wir wurden nicht enttäuscht! Es gab\ndiesmal sechs parallele Tracks so dass niemand zu kurz kam. Egal ob DevOp, Coder, Scrum-Guru, Sicherheitsfanatiker oder\nBuzzword-Jongleur – jeder kam auf seine Kosten. Die thematische Aufteilung in Conference Day und Agile Day kam den\njeweiligen Präferenzen entgegen, auch wenn der Agile Day tatsächlich nur teilweise das Thema “Agile” bediente,\nvermutlich aus Mangel an Speakern in diesem Bereich. Im Folgenden gehen wir auf ein paar gelungene Talks der beiden Tage\nein.",[3938,37797,32679],{"id":32678},[1065,37799,37801],{"id":37800},"spock-und-geb-übersichtlich-und-nachvollziehbarer-testen-für-alle","Spock und Geb: Übersichtlich und nachvollziehbarer testen für alle!",[439,37803,37804],{},"Wer schonmal Web-Integrationstests mit Selenium geschrieben hat, der kennt neben den vielen Vorzügen auch dessen\nProbleme – unter anderem in punkto Testfalldefinition, Wartbarkeit, Reporting.",[439,37806,37807,2040,37812,37816,37817,37822],{},[1002,37808,37811],{"href":37809,"rel":37810},"https://twitter.com/tokraft",[1006],"Tobias Kraft",[1002,37813,296],{"href":37814,"rel":37815},"https://twitter.com/RalfDMueller",[1006]," stellten\neine ",[1002,37818,37821],{"href":37819,"rel":37820},"http://entwicklertag.de/karlsruhe/2015/spock-und-geb-bersichtlich-und-nachvollziehbar-testen-f-r-alle",[1006],"interessante Alternative","\nvor, die es mit etwas Initialaufwand möglich macht, dass der Product Owner (PO) ohne technische Kenntnisse Testfälle\ngeneriert. Diese werden von den Entwicklern mit wenigen Zeilen Groovy Code ausimplementiert und liefern danach\nansehnliche und aussagekräftige Reports an den PO zurück.",[439,37824,37825,37826,37831,37832,37837],{},"Der verwendete Stack besteht aus dem\nGroovy-Testtool ",[1002,37827,37830],{"href":37828,"rel":37829},"http://www.next-gamer.de/wp-content/uploads/2015/02/mr-spock.jpg",[1006],"Spock",", dem Webtesting\nFramework ",[1002,37833,37836],{"href":37834,"rel":37835},"https://web.archive.org/web/20170227233745/http://www.gebish.org/testing",[1006],"Geb",", einer Auswahl an\nverschiedenen Reporting Tools und Überraschung! – Excel! Schließlich soll auch der Fachbereich ein Werkzeug in die Hand\nbekommen, mit dem er sich wohl fühlt.",[439,37839,37840],{},[2205,37841],{"alt":469,"src":37842},"https://media.synyx.de/uploads//2015/05/excel_spez.png",[1065,37844,37846],{"id":37845},"java-web-security-anti-patterns","Java Web-Security Anti-Patterns",[439,37848,37849,8351,37854,37859],{},[1002,37850,37853],{"href":37851,"rel":37852},"https://twitter.com/dschadow",[1006],"Dominik Schadows",[1002,37855,37858],{"href":37856,"rel":37857},"http://entwicklertag.de/karlsruhe/2015/java-web-security-anti",[1006],"Vortrag zur Sicherheit im Web","\nwar nichts für schwache Nerven, denn wer ihn gelassen und ruhigen Gewissens besuchte, der kam unter Umständen\nnägelkauend und zähneklappernd wieder raus. Die Session war wie ein guter Horrorfilm und das eigene Projekt war das\nMonster! Dominik warf anschauliche Fallbeispiele auf die Leinwand, die klar zeigten, wie man Sicherheit im Web NICHT\nmacht und so mancher hat darin garantiert sein eigenes Machwerk wiedererkannt. Gibt es ein Threat Model? Ist die\nAuthentifizierung langsam genug, um Brute Force zu verhindern? Wird die Session ID zur rechten Zeit erneuert? Wer diese\nund viele weitere Fragen mit “Ja” beantworten konnte war einer der wenigen Glücklichen, die den Talk mit einem guten\nGefühl verlassen konnten. Die Session war augenöffnend und einer der wertvollsten Beiträge zur Konferenz.",[439,37861,37862],{},[2205,37863],{"alt":469,"src":37864},"https://media.synyx.de/uploads//2015/05/password_scream.png",[3938,37866,32704],{"id":32701},[1065,37868,37870],{"id":37869},"software-die-jeder-mag-schnell-und-innovativ","Software , die jeder mag – schnell und innovativ",[439,37872,37873,37874,37879],{},"Jürgen Lind und ",[1002,37875,37878],{"href":37876,"rel":37877},"https://twitter.com/bdam",[1006],"Adam Egger"," stellten ein Konzept vor, welches sich von der\nSoftwareentwicklung mit drei Phasen – Anforderung, Entwicklung und Testen – deutlich unterscheidet. Ihr Konzept sieht\neine starke Kollaboration aller beteiligten Gruppen vor und ist in vier Phasen aufgeteilt.",[439,37881,37882],{},"In der ersten Phase sollen der Nutzer und dessen Bedürfnisse sowie die Domäne verstanden werden, sodass die Sicht des\nKunden eingenommen werden kann. Die Probleme des Kunden zu analysieren und definieren ist Teil der folgenden\nanalytischen Phase. Unterstützt werden diese Phasen von verschiedenen Methodiken wie z.B. ‘5-Why’. Diese Methode zielt\ndarauf ab den Kern des Problems zu finden, indem man immer wieder die ‘Warum?’ Frage stellt, wodurch das eigentliche\nProblem zum Vorschein gebracht werden soll.",[439,37884,37885],{},"Nachdem die Probleme identifiziert wurden, wird ein Lösungsentwurf angestrebt. Dabei helfen die ‘Crazy 8s’. Bei dieser\nMethode faltet man ein Blatt Papier dreimal, sodass acht Rechtecke entstehen. Diese Rechtecke werden mit acht\nverschiedenen Entwürfen gefüllt, wobei potentielle Lösungsentwürfe entstehen.",[439,37887,37888,37889,37894],{},"Die vierte Phase schließt das Modell mit einem Prototyp ab. Dieser kann auf verschiedenen Wegen entstehen – zum Beispiel\nals Skribble auf einem Blatt Papier. Softwarelösungen wie ’Prototyper’, ",[1002,37890,37893],{"href":37891,"rel":37892},"https://popapp.in",[1006],"‘POP’"," und ähnliches sind\nAlternativen.",[439,37896,37897],{},[2205,37898],{"alt":37899,"src":37900},"jukebox","https://media.synyx.de/uploads//2015/05/phases.png",[1065,37902,37904],{"id":37903},"funktionale-programmierung","Funktionale Programmierung",[439,37906,37907,37908,37913,37914,37919,37920,37925,37926,37931],{},"Gleich zwei Talks befassten sich mit dem Thema funktionale\nProgrammierung. ",[1002,37909,37912],{"href":37910,"rel":37911},"https://twitter.com/NicoleRauch",[1006],"Nicole Rauch"," bot\neinen ",[1002,37915,37918],{"href":37916,"rel":37917},"http://entwicklertag.de/karlsruhe/2015/jetzt-funkts-funktionale",[1006],"gelungenen Einstieg"," in die Thematik indem sie\ntypische Sprachkonstrukte verschiedener funktionaler Sprachen mit den objektorientierten Pedants verglich. Dabei wurde\nschnell klar, welche Vorzüge Sprachen wie Haskell und Scala gegenüber großen Playern wie Java haben können. Auf diese\nVorzüge ging ",[1002,37921,37924],{"href":37922,"rel":37923},"https://twitter.com/sperbsen",[1006],"Michael Sperber"," anhand\neines ",[1002,37927,37930],{"href":37928,"rel":37929},"http://entwicklertag.de/karlsruhe/2015/funktionale",[1006],"anschaulichen Beispiels"," ausführlicher ein. Er erklärte,\nwarum grundlegende funktionale Konzepte wie Immutability eine tolle Sache sind und warum wir viel mehr Software mit\nfunktionalen statt objektorientierten Sprachen entwickeln sollten.",[3938,37933,37935],{"id":37934},"was-gabs-noch","Was gabs noch?",[439,37937,37938],{},"Eine Menge Talks, die richtig gut waren aber für die wir in so einem kleinen Blogeintrag nicht genug Platz haben:",[994,37940,37941,37950,37964,37973,37987,37996],{},[997,37942,37943,37944,37949],{},"Hardy Ferentschik, der wunderbar\ndetailliert ",[1002,37945,37948],{"href":37946,"rel":37947},"http://entwicklertag.de/karlsruhe/2015/jmh-micro-benchmarking",[1006],"beschreiben konnte",", wie man mit\nMicro-Benchmarking auf der JVM prüft ob Code A oder Code B schneller ist.",[997,37951,37952,37957,37958,37963],{},[1002,37953,37956],{"href":37954,"rel":37955},"https://twitter.com/hschwentner",[1006],"Henning Schwentner",", der mit\neiner ",[1002,37959,37962],{"href":37960,"rel":37961},"http://entwicklertag.de/karlsruhe/2015/value-objects-next-big",[1006],"Demonstration der Möglichkeiten von Value Types","\neinen Blick in die Zukunft Javas warf",[997,37965,37966,37967,37972],{},"Dr. Mana Taghdiri, die in hohem Tempo\nverschiedene ",[1002,37968,37971],{"href":37969,"rel":37970},"http://entwicklertag.de/karlsruhe/2015/methodologies-and-tools",[1006],"Tools und Methoden"," zur automatischen\nTestgenerierung beleuchtete",[997,37974,37975,37980,37981,37986],{},[1002,37976,37979],{"href":37977,"rel":37978},"https://twitter.com/tim_roes",[1006],"Tim Roes",", der einen\nschnellen ",[1002,37982,37985],{"href":37983,"rel":37984},"http://entwicklertag.de/karlsruhe/2015/web-today-and-tomorrow",[1006],"Überblick"," über vergangene, aktuelle und\nzukünftige Webtechnologien verschaffte und diese mit Codebeispielen veranschaulichte",[997,37988,37989,37990,37995],{},"Gebhard Ebeling und Mario Krahmer, die versuchten\ndie ",[1002,37991,37994],{"href":37992,"rel":37993},"http://entwicklertag.de/karlsruhe/2015/mut-zur-l-cke-testl-cken",[1006],"Waage zwischen absoluter Sicherheit und kalkuliertem Risiko","\nbei der Testabdeckung zu finden",[997,37997,37998,37999,38004,38005,38010],{},"Prof. Dr. Jörn Müller-Quade vom ",[1002,38000,38003],{"href":38001,"rel":38002},"http://www.kit.edu/index.php",[1006],"KIT",",\nder ",[1002,38006,38009],{"href":38007,"rel":38008},"https://entwicklertag.de/karlsruhe/2015/beweisbare-sicherheit-von.html",[1006],"Beweisbare Sicherheit von der Verschlüsselung bis hin zum Softwareschutz","\nvorstellte. Dabei zeigte er wie man Sicherheit anhand von Modellen und Annahmen beweisen und somit ein Verständnis für\nSicherheit entwickeln kann.",[3938,38012,1385],{"id":1384},[439,38014,38015,38016,1402],{},"Der Besuch hat sich gelohnt, wir kommen gerne wieder! Die Talks waren durch die Bank von ordentlicher bis hervorragender\nQualität und die parallelen Tracks mit großer Bandbreite an Themen ließen keine Wünsche offen. Wir konnten deutlich mehr\nnützliche Infos in unseren Arbeitsalltag mitnehmen als bei der noch nicht so\netablierten ",[1002,38017,38020],{"href":38018,"rel":38019},"http://blog.synyx.de/2015/02/entwicklertag-frankfurt-2015/",[1006],"Schwesterkonferenz in Frankfurt",[439,38022,38023],{},"Die Keynotes des Conference Day gingen für unseren Geschmack zu sehr in Richtung Buzzword-Bingo, was aber Holger\nKoschek und Rolf Drähter am Agile Day durch ihre Sangeskraft locker wieder ausbügelten.",[439,38025,38026,38027,38032],{},"Auch am Umfeld mit Catering, Kaffee, Ganggesprächen, Zeitplanung und Organisation gab es nix zu meckern. Vielen Dank an\nden Ausrichter ",[1002,38028,38031],{"href":38029,"rel":38030},"https://www.andrena.de/",[1006],"andrena",", der sich zu seinem zwanzigjährigen Bestehen eine würdige\nGeburtstagskonferenz modelliert hat!",[439,38034,38035],{},[2205,38036],{"alt":37899,"src":38037},"https://media.synyx.de/uploads//2015/05/jukebox.jpg",{"title":469,"searchDepth":507,"depth":507,"links":38039},[38040,38044,38048,38049],{"id":32678,"depth":507,"text":32679,"children":38041},[38042,38043],{"id":37800,"depth":547,"text":37801},{"id":37845,"depth":547,"text":37846},{"id":32701,"depth":507,"text":32704,"children":38045},[38046,38047],{"id":37869,"depth":547,"text":37870},{"id":37903,"depth":547,"text":37904},{"id":37934,"depth":507,"text":37935},{"id":1384,"depth":507,"text":1385},[1030],"2015-05-29T14:01:30","Die Karlsruher Entwicklertage hatten Geburtstag! Zehn Jahre alt wurden sie\\nund das mussten wir synyxler uns anschauen. Wir freuten uns auf hochwertige Talks und das Wiedersehen mit vielen\\nKarlsruher Kollegen und bekannten Gesichtern der nationalen Entwicklerszene. Und wir wurden nicht enttäuscht! Es gab\\ndiesmal sechs parallele Tracks so dass niemand zu kurz kam. Egal ob DevOp, Coder, Scrum-Guru, Sicherheitsfanatiker oder\\nBuzzword-Jongleur – jeder kam auf seine Kosten. Die thematische Aufteilung in Conference Day und Agile Day kam den\\njeweiligen Präferenzen entgegen, auch wenn der Agile Day tatsächlich nur teilweise das Thema “Agile” bediente,\\nvermutlich aus Mangel an Speakern in diesem Bereich. Im Folgenden gehen wir auf ein paar gelungene Talks der beiden Tage\\nein.","https://synyx.de/blog/entwicklertag-karlsruhe-2015/",{},"/blog/entwicklertag-karlsruhe-2015",{"title":37781,"description":38057},"Die Karlsruher Entwicklertage hatten Geburtstag! Zehn Jahre alt wurden sie\nund das mussten wir synyxler uns anschauen. Wir freuten uns auf hochwertige Talks und das Wiedersehen mit vielen\nKarlsruher Kollegen und bekannten Gesichtern der nationalen Entwicklerszene. Und wir wurden nicht enttäuscht! Es gab\ndiesmal sechs parallele Tracks so dass niemand zu kurz kam. Egal ob DevOp, Coder, Scrum-Guru, Sicherheitsfanatiker oder\nBuzzword-Jongleur – jeder kam auf seine Kosten. Die thematische Aufteilung in Conference Day und Agile Day kam den\njeweiligen Präferenzen entgegen, auch wenn der Agile Day tatsächlich nur teilweise das Thema “Agile” bediente,\nvermutlich aus Mangel an Speakern in diesem Bereich. Im Folgenden gehen wir auf ein paar gelungene Talks der beiden Tage\nein.","blog/entwicklertag-karlsruhe-2015",[],"Die Karlsruher Entwicklertage hatten Geburtstag! Zehn Jahre alt wurden sie und das mussten wir synyxler uns anschauen. Wir freuten uns auf hochwertige Talks und das Wiedersehen mit vielen Karlsruher Kollegen…","QIfpLDM6lljLsBBxaU9AHA1jAqQItGOaTTn3Fhg8yjE",{"id":38063,"title":38064,"author":38065,"body":38066,"category":38178,"date":38179,"description":469,"extension":1034,"link":17890,"meta":38180,"navigation":916,"path":38181,"seo":38182,"slug":38070,"stem":38183,"tags":38184,"teaser":38185,"__hash__":38186},"blog/blog/schulesynyx-the-self-training-company.md","schule@synyx – the self-training company",[97],{"type":432,"value":38067,"toc":38174},[38068,38071,38075,38078,38081,38088,38092,38099,38102,38105,38108,38113,38130,38135,38152,38157,38171],[435,38069,38064],{"id":38070},"schulesynyx-the-self-training-company",[3938,38072,38074],{"id":38073},"the-training-issue","The training issue",[439,38076,38077],{},"One issue that every company has to deal with is the training of its employees. I encountered different attitudes\nregarding this subject in different companies and wondered what is a healthy approach for a software project company to\npursue.",[439,38079,38080],{},"In software development it is not sufficient to offer one workshop per year and call it a “training program”. It is also\nnot enough to have one or two R&D dudes per 50 developers that keep ahead of new technologies and tell the worker\ndrones from time to time what to use for their projects. The result of half-assed concepts like this will be that\nmotivated, willing-to-learn developers look for other jobs and you will eventually remain with a bunch of static\nby-the-book workers who have no interest in learning. Relying solely on this can lead a company into deprecation\nwithin few years.",[439,38082,38083,38084,38087],{},"A more healthy approach is to maintain a constant mindset of learning and innovation and you have to pull ",[990,38085,38086],{},"all"," of your\ndevelopers into it. The progress should come from them and out of their own motivation, it must not be dictated by\nmanagement. Sadly that is a very rare condition. It requires not only an open-minded management but also employees that\nare eager to learn new things and are curious about new ways of solving problems – traits that every good developer\nshould have in this fast-moving industry.",[3938,38089,38091],{"id":38090},"the-synyx-approach","The synyx approach",[439,38093,38094,38095,38098],{},"Here at synyx we have pretty good prerequisites to maintain this condition. Our bosses encourage us to spend work time\non our own education and the general mentality among the employees is that learning new stuff is cool. Among other\nthings we have one important tool here at synyx to keep this mindset alive. It is called “",[448,38096,38097],{},"schule@synyx","” and is\nessentially an employee self-training program, that works like this:",[439,38100,38101],{},"Every Friday afternoon there is a reserved time slot of 1-2 hours in our largest meeting room. One employee voluntarily\ngives a talk about a specific work-related topic, that he chooses himself, and all other employees are free to attend.\nThe talk can have tutorial-, workshop- or just informational character. The subjects spread out on a wide variety of\nsoftware development related fields. Examples from the recent time are talks about systemd, JavaScript linting, system\narchitecture, Puppet, Android development, NoSQL modeling, UX, Spring security, Docker. These talks touch the topics\noperations, application development, GUI design, software quality, mobile and persistence from different angles on\ndifferent expert levels. During and after the talk discussions arise about different aspects of the subject, that are\nsometimes continued into after hours beer time.",[439,38103,38104],{},"Neither the speaker nor the attendees have to spend money or their free time on this – the preparation and attendance\nbelong to the 20% part of synyx’s 80/20 work time model.",[439,38106,38107],{},"I can not emphasize enough how valuable this is to us! Just think about how everyone benefits from it:",[439,38109,38110],{},[448,38111,38112],{},"The company benefits from …",[994,38114,38115,38118,38121,38124,38127],{},[997,38116,38117],{},"… expert knowledge spread throughout the company",[997,38119,38120],{},"… new methods and technologies introduced into the company’s knowledge pool",[997,38122,38123],{},"… maintaining a high level of knowledge diversity among the developers",[997,38125,38126],{},"… a huge motivation boost among the employees",[997,38128,38129],{},"… a growing and up-to-date pool of potentially quality talks for conferences, customers and external educational\npurposes like universities or user groups",[439,38131,38132],{},[448,38133,38134],{},"The employees …",[994,38136,38137,38140,38143,38146,38149],{},[997,38138,38139],{},"… as project developers have to deal with all of those fields anyway and gain a wider knowledge portfolio just by\nattending the talks",[997,38141,38142],{},"… get to learn cool new things every week presented in a convenient way",[997,38144,38145],{},"… get a chance to self-dependently shape their training and education",[997,38147,38148],{},"… can exchange different opinions on the subjects in the subsequent discussions",[997,38150,38151],{},"… have no pressure or obligation to deliver something. No boss will ever order employee X to do a talk about Y on day\nZ",[439,38153,38154],{},[448,38155,38156],{},"The speaker (who imo is the major beneficiary) …",[994,38158,38159,38162,38165,38168],{},[997,38160,38161],{},"… gains speaking experience in front of a small, familiar crowd",[997,38163,38164],{},"… gets the chance to aquire deep knowledge in one subject that he is interested in",[997,38166,38167],{},"… receives feedback and discussion input about his subject from the attendees",[997,38169,38170],{},"… introduces himself as know-how holder, maybe even expert on the subject",[439,38172,38173],{},"I would call that a solid win-win-win situation. And it greatly helps to establish the vital learning and innovation\nmindset mentioned in the introduction. Incomprehensibly self-dependent training elements like this seem to be the\nexception in the industry, so I consider us synyx employees pretty lucky about our situation 🙂",{"title":469,"searchDepth":507,"depth":507,"links":38175},[38176,38177],{"id":38073,"depth":507,"text":38074},{"id":38090,"depth":507,"text":38091},[1030],"2015-05-04T13:33:56",{},"/blog/schulesynyx-the-self-training-company",{"title":38064,"description":469},"blog/schulesynyx-the-self-training-company",[],"The training issue One issue that every company has to deal with is the training of its employees. I encountered different attitudes regarding this subject in different companies and wondered…","1M3fqYlcQkWaInHIzGwS1CCyMX64l6YdfMAavnoUoPM",{"id":38188,"title":38189,"author":38190,"body":38191,"category":38267,"date":38268,"description":38269,"extension":1034,"link":38270,"meta":38271,"navigation":916,"path":38272,"seo":38273,"slug":38195,"stem":38274,"tags":38275,"teaser":38276,"__hash__":38277},"blog/blog/wertbeitrag-der-it-input-aus-leipzig.md","Wertbeitrag der IT – Input aus Leipzig",[312],{"type":432,"value":38192,"toc":38265},[38193,38196,38199,38209,38217,38221,38229,38232,38242,38247,38250,38253,38256,38259,38262],[435,38194,38189],{"id":38195},"wertbeitrag-der-it-input-aus-leipzig",[439,38197,38198],{},"Am 26. und 27. März war ich in Leipzig Teilnehmer bei der Konferenz „Wertbeitrag der IT“. Das Thema der\nWertbeitragsermittlung beschäftigt mich seit einigen Jahren und ich bin gerade tatsächlich dabei, einen Leitfaden mit\neinem pragmatischen Ansatz zur Wertbeitragsermittlung zu schreiben. Von daher hat der Zeitpunkt ideal gepasst und ich\nwar sehr gespannt, welche Erkenntnisse ich nach den zwei Tagen mit nach Hause nehmen kann.",[439,38200,38201],{},[1002,38202,38205],{"href":38203,"rel":38204},"https://media.synyx.de/uploads//2015/04/Fassade_Hainstra%C3%9Fe.jpg",[1006],[2205,38206],{"alt":38207,"src":38208},"Softwareforen Leipzig","https://media.synyx.de/uploads//2015/04/Fassade_Hainstra%C3%9Fe-300x225.jpg",[439,38210,38211,38212,38216],{},"Die Konferenz wurde von den ",[1002,38213,38207],{"href":38214,"rel":38215},"http://www.softwareforen.de",[1006]," und deren Geschäftsführer André Köhler\nausgetragen. Zentral in der Innenstadt gelegen, in einem charmanten Altbau mit neu gestalteten Räumlichkeiten, bestem\nCatering und sehr guter Organisation konnte man sich gut aufgehoben fühlen. Die Softwareforen Leipzig veranstalten\nregelmäßig IT-spezifische Konferenzen und ich kann sie als Veranstalter durch aus empfehlen.",[439,38218,38219],{},[448,38220,11447],{},[439,38222,38223,38224,38228],{},"Alle Vorträge und Referenten können unter\ndiesem ",[1002,38225,28027],{"href":38226,"rel":38227},"http://www.softwareforen.de/portal/de/veranstaltung/softwareforen_konferenzen/wertbeitrag_der_it/themenarchiv_11/konferenz_2/konferenz_3.xhtml",[1006],"\neingesehen werden – zumindest in Kurzform. In meinen Augen waren die beiden Vorträge von Prof. Dr. Tim Weitzel (\nOtto-Friedrich-Universität Bamberg) „Wertbeitrag der IT – Wie man die IT nutzt, damit die IT nutzt“ und von Prof. Dr.\nFrederik Ahlemann (Universität Duisburg-Essen) „Benefits Management – Ein Ansatz zur Nutzengenerierung aus\nIT-Investitionen“ am wertvollsten. Sie haben deutlich gemacht, dass der Wertbeitrag der IT eben nicht in der IT zu\nsuchen ist sondern beim Fachbereich und eben genau dieser sich um den Nutzen/Wert kümmern muss.",[439,38230,38231],{},"Interessant zu sehen war, dass es nicht DIE Berechnungsmethodik gibt, mit der der Wertbeitrag der IT zu ermitteln ist.\nUnd, dass mehrheitlich leider immer noch die Wertbeitragsermittlung aus einem Rechtfertigungsgrund (die IT kostet!)\ngewünscht wird – als Steuerungsinstrument wäre sinnvoller!",[439,38233,38234],{},[1002,38235,38238],{"href":38236,"rel":38237},"https://media.synyx.de/uploads//2015/04/Konferenz_WBIT_01.jpg",[1006],[2205,38239],{"alt":38240,"src":38241},"Eröffnung der Konferenz","https://media.synyx.de/uploads//2015/04/Konferenz_WBIT_01-300x200.jpg",[439,38243,38244],{},[448,38245,38246],{},"Meine Erkenntnisse",[439,38248,38249],{},"Die Vorträge in Leipzig haben einige meiner Gedanken zum Thema der Wertbeitragsermittlung bestätigt:",[439,38251,38252],{},"1. Es gibt nicht DIE Methode",[439,38254,38255],{},"2. Der Fachbereich ist der Schlüssel",[439,38257,38258],{},"3. Das Ergebnis einer Wertbeitragsermittlung ist von außen betrachtet subjektiv",[439,38260,38261],{},"Und, es haben sich neue Fragen gestellt. Was ist eigentlich genau der Unternehmenserfolg, von dem ich den Wertbeitrag\nder IT bemessen will? Wie setzt er sich zusammen? Die Antworten auf diese Fragen sind meiner Meinung nach Voraussetzung\nfür eine sinnvolle Berechnung. Aber, nach meiner Erfahrung gibt es diese Antworten in der Regel nicht! Im Moment bin ich\nmir sogar unschlüssig ob es überhaupt sinnvoll ist, eine Wertbeitragsberechnung anzugehen. Wo liegt der Nutzen und ist\ner diesen Aufwand wert? Oder sollte man nicht lieber davon ausgehen, dass die IT erst effizientes Arbeiten ermöglicht\nund das als Tatsache verstehen?",[439,38263,38264],{},"In diesem Sinne freue ich mich immer über einen Austausch mit allen Mitstreitern und Interessierten am Thema Wertbeitrag\nder IT.",{"title":469,"searchDepth":507,"depth":507,"links":38266},[],[1031],"2015-04-24T11:32:16","Am 26. und 27. März war ich in Leipzig Teilnehmer bei der Konferenz „Wertbeitrag der IT“. Das Thema der\\nWertbeitragsermittlung beschäftigt mich seit einigen Jahren und ich bin gerade tatsächlich dabei, einen Leitfaden mit\\neinem pragmatischen Ansatz zur Wertbeitragsermittlung zu schreiben. Von daher hat der Zeitpunkt ideal gepasst und ich\\nwar sehr gespannt, welche Erkenntnisse ich nach den zwei Tagen mit nach Hause nehmen kann.","https://synyx.de/blog/wertbeitrag-der-it-input-aus-leipzig/",{},"/blog/wertbeitrag-der-it-input-aus-leipzig",{"title":38189,"description":38198},"blog/wertbeitrag-der-it-input-aus-leipzig",[],"Am 26. und 27. März war ich in Leipzig Teilnehmer bei der Konferenz „Wertbeitrag der IT“. Das Thema der Wertbeitragsermittlung beschäftigt mich seit einigen Jahren und ich bin gerade tatsächlich…","cOAgJ_4ix4AsrtNIzd21aiN3AQw3twTrXlTyOF_bXqo",{"id":38279,"title":38280,"author":38281,"body":38282,"category":38361,"date":38362,"description":38363,"extension":1034,"link":38364,"meta":38365,"navigation":916,"path":38366,"seo":38367,"slug":38286,"stem":38369,"tags":38370,"teaser":38376,"__hash__":38377},"blog/blog/auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg.md","Auch die zweite Devoxx4Kids in Karlsruhe war ein Erfolg",[172],{"type":432,"value":38283,"toc":38359},[38284,38287,38300,38310,38313,38316,38326,38329,38337,38346,38356],[435,38285,38280],{"id":38286},"auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg",[439,38288,38289,38290,38294,38295,38299],{},"Am 11.04.2015 fand die zweite ",[1002,38291,12123],{"href":38292,"rel":38293},"http://devoxx4kids.org/deutschland",[1006]," in Karlsruhe statt. Die Workshops\nwaren die gleichen wie beim letzten Mal im\nSeptember (",[1002,38296,8234],{"href":38297,"rel":38298},"http://blog.synyx.de/2014/09/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids/",[1006]," könnt\nihr nachlesen, was wir genau gemacht haben). So wollten wir auch Kindern die Gelegenheit bieten, welche beim ersten\nEvent keinen Platz mehr bekommen haben, an den Workshops teilzunehmen.",[439,38301,38302],{},[1002,38303,38306],{"href":38304,"rel":38305},"https://media.synyx.de/uploads//2015/04/kids_ges.jpg",[1006],[2205,38307],{"alt":38308,"src":38309},"kids_ges","https://media.synyx.de/uploads//2015/04/kids_ges-300x200.jpg",[439,38311,38312],{},"Dieses Mal hieß es 24 Plätze an wissbegierige Mädchen und Jungen zu vergeben. Was uns sehr freute, es haben sich sehr\nviele Mädchen angemeldet, im Verhältnis mehr als das letzte Mal. Das lässt annehmen, dass sich auch die weibliche\nGeneration langsam aber sicher dem Thema „Technik und Informatik“ annimmt und sich dafür interessiert.",[439,38314,38315],{},"Wir hatten auch zwei „Wiederholungstäter“, sprich zwei Kids, welche bereits im September da waren. Wir hatten leichte\nBedenken, dass die beiden sich vielleicht langweilen würden, das war aber zum Glück nicht der Fall. Wir hatten allgemein\nden Eindruck, als hätten die Kids wieder viel Spaß beim Lernen gehabt. Zumindest hat das auch das erste Feedback,\nwelches wir noch am gleichen Tag erhalten haben, ergeben. Das freut uns natürlich außerordentlich und bestätigt uns in\nunserem Handeln und bestärkt uns weiterhin die Devoxx4Kids zu organisieren und anzubieten.",[439,38317,38318],{},[1002,38319,38322],{"href":38320,"rel":38321},"https://media.synyx.de/uploads//2015/04/bild_2.jpg",[1006],[2205,38323],{"alt":38324,"src":38325},"bild_2","https://media.synyx.de/uploads//2015/04/bild_2-300x200.jpg",[439,38327,38328],{},"Es war auch klasse, dass die Karlshochschule uns wieder ihre Räumlichkeiten zur Verfügung gestellt hat. Die Räume sind\nmit allem, was wir für unsere Workshops brauchen, ausgestattet. Auch das Karlscafé öffnete wieder seine Pforten für uns\nund versorgte und mit leckerem, heißem Kaffee 😉",[439,38330,38331,38332,38336],{},"An dieser Stelle möchten wir uns bedanken. Danke, dass so viele tolle Kinder da waren. Danke an die vielen Helfer und\nMentoren. Insbesondere Oliver Milke und Alexander Bischoff, die den Kinder wieder das „Internet der Dinge“\nmittels ",[1002,38333,36025],{"href":38334,"rel":38335},"http://www.tinkerforge.com/de",[1006]," näher gebracht haben. Die beiden engagieren sich sehr für die\nDevoxx4Kids, waren auch mit auf der JavaLand4Kids und werden bei uns im September ebenfalls wieder dabei sein. Ihr seid\nuns echt ans Herz gewachsen.",[439,38338,38339,38340,38345],{},"Aber auch den Sponsoren ist zu danken. ",[1002,38341,38344],{"href":38342,"rel":38343},"http://www.exensio.de/",[1006],"Exensio"," hat mit dem Bronze-Sponsering auch zu diesem\ntollen Event beigetragen. Zu guter Letzt danken wir unserem Arbeitgeber, der synyx GmbH & Co. KG. Es ist nicht\nselbstverständlich, dass eine Firma so viel Energie, Mittel und Arbeitszeit in ein Non-Profit Event steckt.",[439,38347,38348],{},[1002,38349,38352],{"href":38350,"rel":38351},"https://media.synyx.de/uploads//2015/04/copter.jpg",[1006],[2205,38353],{"alt":38354,"src":38355},"copter","https://media.synyx.de/uploads//2015/04/copter-300x200.jpg",[439,38357,38358],{},"Wir hoffen für September, wenn es neue Workshops geben wird, genauso viele Helfer und Sponsoren finden zu können. Wir\nwollen wieder 32 Kindern die Möglichkeit bieten, sich mit dem Thema mit Spaß zu beschäftigen. Wer noch Ideen für neue\nWorkshops hat oder vielleicht auch als Mentor dabei sein will, kann sich gerne bei uns melden. Wir freuen uns über jede\nUnterstützung.",{"title":469,"searchDepth":507,"depth":507,"links":38360},[],[1031],"2015-04-17T17:15:17","Am 11.04.2015 fand die zweite Devoxx4Kids in Karlsruhe statt. Die Workshops\\nwaren die gleichen wie beim letzten Mal im\\nSeptember (hier könnt\\nihr nachlesen, was wir genau gemacht haben). So wollten wir auch Kindern die Gelegenheit bieten, welche beim ersten\\nEvent keinen Platz mehr bekommen haben, an den Workshops teilzunehmen.","https://synyx.de/blog/auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg/",{},"/blog/auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg",{"title":38280,"description":38368},"Am 11.04.2015 fand die zweite Devoxx4Kids in Karlsruhe statt. Die Workshops\nwaren die gleichen wie beim letzten Mal im\nSeptember (hier könnt\nihr nachlesen, was wir genau gemacht haben). So wollten wir auch Kindern die Gelegenheit bieten, welche beim ersten\nEvent keinen Platz mehr bekommen haben, an den Workshops teilzunehmen.","blog/auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg",[4216,38371,38372,38373,24653,38374,38375],"kindertagung","nao","quadcopter","tinkerforge","worksho","Am 11.04.2015 fand die zweite Devoxx4Kids in Karlsruhe statt. Die Workshops waren die gleichen wie beim letzten Mal im September (hier könnt ihr nachlesen, was wir genau gemacht haben). So…","98BLDeVa87Sj3FXorUer_52zQXhr86yg-8bmB3OTX0M",{"id":38379,"title":38380,"author":38381,"body":38382,"category":38440,"date":38441,"description":38442,"extension":1034,"link":38443,"meta":38444,"navigation":916,"path":38445,"seo":38446,"slug":38386,"stem":38447,"tags":38448,"teaser":38450,"__hash__":38451},"blog/blog/javaland-2015.md","Javaland – 2015",[15],{"type":432,"value":38383,"toc":38438},[38384,38387,38390,38393,38396,38399,38402,38414,38417,38420,38423,38426,38429,38432,38435],[435,38385,38380],{"id":38386},"javaland-2015",[439,38388,38389],{},"Dieses Jahr hatte ich endlich einmal die Gelegenheit einen Tag auf der Javaland, einer recht neuen Konferenz im\ndeutschsprachigen Raum, zu verbringen. Spannend ist hier vor allem die Location, das Phantasialand in Brühl. Ich war\nsehr gespannt, ob die Location praktikabel ist oder nicht… Natürlich ist das Socializing bei solchen Konferenzen auch\nein wichtiger Aspekt 🙂 Auch dies versprach auf der Javaland, durch die iJUG mitorganisiert, sehr spannend zu werden. Zu\nguter Letzt war es auch so, dass meine halbe Twitter Timeline schon seit Tagen viel @javalandconf im Angebot hatte 🙂",[439,38391,38392],{},"Und so begab es sich, dass ich mich wieder einmal zu unmenschlichen Zeiten morgens am Karlsruher Bahnhof mit ein paar\nKollegen auf den Weg nach Brühl bei Köln aufmachte.",[439,38394,38395],{},"Mein erster Talk an diesem Tag war Michael Hunger, der über die Neo4j sprach. Eigentlich ist das für mich nichts Neues,\nwir arbeiten selbst schon einige Zeit mit Neo4j, jedoch erhoffte ich mir Neuigkeiten aus der Entwicklung und ich wurde\nnicht enttäuscht. Nicht nur, das NeoTechnologies weiter an Cypher (der Neo4J Abfrage Sprache) feilt, sie versprechen\nmittlerweile auch, dass Cypher schneller als die Java-API ist. Auch an der allgemeinen Performance der Neo4J wurde\ngefeilt und Michael versprach mit dem nächsten Release eine Geschwindigkeitssteigerung um den Faktor 5. Meiner Meinung\nnach sind das super Aussichten und es beweist, dass man derzeit mit der Entscheidung für den Einsatz einer Neo4J sehr\ngut aufgestellt ist.",[439,38397,38398],{},"Der zweite Talk den ich hörte war von Florian Hopf, einem Ex-synyxer, und somit war ich sehr gespannt, was er Neues zu\nElasticSearch zu berichten hatte. Letztlich verstand er es hervorragend, verschiedene Anwendungsfälle aufzuzeigen und\nsomit die Augen für ganz andere Einsatzszenarien von ElasticSearch zu öffnen. Spannend auch der Vergleich zwischen dem\nELK (ElasticSearch, Logstash, Kibana) Stack und Graylog als monolithischere Alternative. Fazit: spannender, informativer\nund abwechslungsreicher Talk, obwohl Flo krank angereisst ist!",[439,38400,38401],{},"Danach habe ich mir den Devoxx4Kids Ableger der JavaLand einmal genauer angeschaut und meine Kollegen von synyx besucht,\nwelche den Event mit veranstaltet haben.",[439,38403,38404,38405,18762,38409,38413],{},"14 Mädchen und Jungen der dritten und vierten Klasse der Max & Moritz Grundschule in St. Augustin hatten das erste Mal\ndie Gelegenheit, an drei unterschiedlichen Workshops (Quadcopter, Tinkerforge und Scratch) teilzunehmen. Sie erlernten\nspielerisch den Umgang mit dem Raspberry Pi, einen Mini-Computer, und konnten erste Einblicke in die unterschiedlichen\nMöglichkeiten der Programmierung erhalten. Die Kids hatten sehr viel Spaß, wenn auch das Geräusch der Achterbahnen ab\nund zu die Aufmerksamkeit auf sich zog. Die ",[1002,38406,26746],{"href":38407,"rel":38408},"http://www.devoxx4kids.org/deutschland",[1006],[1002,38410,36025],{"href":38411,"rel":38412},"http://www.tinkerforge.com",[1006]," freuen sich schon auf ein weiteres Event im nächsten Jahr.",[439,38415,38416],{},"Nach dem Mittagessen ging es dann mit „Pipelines Zeichnen ist nicht schwer, Pipelines bauen dagegen sehr“ weiter,\nwelches mir leider zu Feature lastig war. Sicherlich wusste der Speaker sehr genau, was er da vortrug, aber letztlich\nkann ich mir eine Vergleichsmatrix zwischen verschiedenen Systemen zur Softwaredelivery selbst im Internet anschauen,\nich habe mir mehr Real World Problems versprochen und habe somit die restliche Zeit mit Networking verbracht 🙂 Für\nKollegen, denen die Continous Gedanken neu sind, war es sicherlich ein sinnvoller Vortrag.",[439,38418,38419],{},"Als nächsten und für mich dann auch letzten kompletten Vortag stand Distributed Log Aggregation und Metrics auf dem\nProgramm. Hier war ich natürlich besonders gespannt, da ich selbst vor einigen Monaten ein paar Artikel rund um diese\nThemen im JavaMagazin veröffentlicht hatte.",[439,38421,38422],{},"Der Vortrag wurde von Tammo van Heesen, ein damaliger Mitschreiber zu diesem Themengebiet im JavaMagazin, zusammen mit\nAlex Heusingfeld, welcher auch beinahe mal synyxer geworden wäre, gehalten.",[439,38424,38425],{},"Kurz und knapp gesagt war es ein spannender, informativer und unterhaltsamer und sehr guter Vortrag, der meine damaligen\nAussagen zum Großteil bestätigt.",[439,38427,38428],{},"Zu guter letzt wollte ich mir eigentlich noch ein Round-Up zu JSR371 MVC in JavaEE8 abholen. Allerdings gab ich das\nnach kurzer Zeit, aufgrund der für mich nicht schlüssigen Gründe FÜR den JSR, auf. Ich denke hier sollten die führenden\nKollegen noch ein paar mehr Runden drehen, ansonsten wird sich das an der Community vorbei entwickeln.",[439,38430,38431],{},"Das Networking kam auf der Javaland leider etwas zu kurz, da die Wege zwischen den Sessions recht weit sein können,\ninsbesondere wenn man sich auch die Devoxx4Kids mit anschauen wollte, was natürlich aus synyx Sicht besonders spannend\nwar 🙂 Leider waren aber eben genau die Devoxx4Kids Sessions komplett außerhalb des Phantasialands in einem Hotel\nuntergebracht, sodass zum einen die Wege enorm weit waren und zum anderen leider die Sichtbarkeit für meinen Geschmack\nVIEL zu gering ausgefallen ist. Ich hoffe dass dies beim nächsten Mal besser wird.",[439,38433,38434],{},"Um das Bild vollends abzurunden: das Catering war durchaus gelungen, auch wenn hier die Vegetarier/Vegane Freunde ein\nschweres Los durch lange Wartezeiten hatten. Sogar an die Sojamilch an allen Kaffeeständen wurde gedacht und das ist\ngroßartig! Hier können sich andere Konferenzen durchaus eine Scheibe abschneiden.",[439,38436,38437],{},"Somit würde ich sagen, bis 2016 im Javaland!",{"title":469,"searchDepth":507,"depth":507,"links":38439},[],[1031],"2015-04-15T18:31:50","Dieses Jahr hatte ich endlich einmal die Gelegenheit einen Tag auf der Javaland, einer recht neuen Konferenz im\\ndeutschsprachigen Raum, zu verbringen. Spannend ist hier vor allem die Location, das Phantasialand in Brühl. Ich war\\nsehr gespannt, ob die Location praktikabel ist oder nicht… Natürlich ist das Socializing bei solchen Konferenzen auch\\nein wichtiger Aspekt 🙂 Auch dies versprach auf der Javaland, durch die iJUG mitorganisiert, sehr spannend zu werden. Zu\\nguter Letzt war es auch so, dass meine halbe Twitter Timeline schon seit Tagen viel @javalandconf im Angebot hatte 🙂","https://synyx.de/blog/javaland-2015/",{},"/blog/javaland-2015",{"title":38380,"description":38389},"blog/javaland-2015",[4216,11539,9556,38449],"phantasialand","Dieses Jahr hatte ich endlich einmal die Gelegenheit einen Tag auf der Javaland, einer recht neuen Konferenz im deutschsprachigen Raum, zu verbringen. Spannend ist hier vor allem die Location, das…","EuATM86YB9ssWiUT88o8h7JFGvfN8FZuhEPZO3Kr9wU",{"id":38453,"title":38454,"author":38455,"body":38456,"category":38469,"date":38470,"description":469,"extension":1034,"link":38471,"meta":38472,"navigation":916,"path":38473,"seo":38474,"slug":38475,"stem":38476,"tags":38477,"teaser":38478,"__hash__":38479},"blog/blog/it-ist-keine-mannersache.md","IT ist keine Männersache",[178],{"type":432,"value":38457,"toc":38467},[38458,38461],[435,38459,38454],{"id":38460},"it-ist-keine-männersache",[439,38462,38463],{},[2205,38464],{"alt":38465,"src":38466},"GirlsDay","https://media.synyx.de/uploads//2015/03/GirlsDay-300x84.jpg",{"title":469,"searchDepth":507,"depth":507,"links":38468},[],[1031],"2015-03-04T09:44:55","https://synyx.de/blog/it-ist-keine-mannersache/",{},"/blog/it-ist-keine-mannersache",{"title":38454,"description":469},"it-ist-keine-mannersache","blog/it-ist-keine-mannersache",[],"Mädchen und Technik, das passt nicht zusammen? Wir finden: Ganz im Gegenteil, das passt sogar sehr gut! Dieses Jahr nimmt synyx am Girls‘Day teil, mit dem Ziel Schülerinnen an Berufsfelder…","NPAP7dRXupMNEXkUZ__3TWuGg_1MBFBmYl_KavIpSH4",{"id":38481,"title":38482,"author":38483,"body":38484,"category":38744,"date":38745,"description":38746,"extension":1034,"link":38747,"meta":38748,"navigation":916,"path":38749,"seo":38750,"slug":38488,"stem":38752,"tags":38753,"teaser":38755,"__hash__":38756},"blog/blog/entwicklertag-frankfurt-2015.md","Entwicklertag Frankfurt 2015",[97,332],{"type":432,"value":38485,"toc":38737},[38486,38489,38531,38535,38549,38559,38563,38571,38580,38595,38604,38608,38616,38626,38629,38632,38642,38651,38654,38658,38700,38710,38712,38715,38723,38732],[435,38487,38482],{"id":38488},"entwicklertag-frankfurt-2015",[439,38490,38491,38492,38497,38498,38503,38504,38509,38510,38513,38514,38519,38520,18762,38525,38530],{},"Während der ",[1002,38493,38496],{"href":38494,"rel":38495},"http://www.entwicklertag.de/",[1006],"Karlsruher Entwicklertag"," der ",[1002,38499,38502],{"href":38500,"rel":38501},"http://www.andrena.de/",[1006],"andrena objects ag","\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\ndie hohe Qualität der Veranstaltung ab als ",[1002,38505,38508],{"href":38506,"rel":38507},"http://sdq.ipd.kit.edu/people/ralf_reussner/",[1006],"Professor Ralf Reussner","\nvom ",[1002,38511,38003],{"href":38001,"rel":38512},[1006]," einen\neindrucksvollen ",[1002,38515,38518],{"href":38516,"rel":38517},"https://entwicklertag.de/frankfurt/2015/opening-session-was-brauchen-softwaretechniker-um-ingenieure-zu-werden",[1006],"Einblick in die Informatik-Forschung","\nbot. Mit den von ihm betreuten Forschungsprojekten ",[1002,38521,38524],{"href":38522,"rel":38523},"http://www.palladio-simulator.com/",[1006],"Palladio",[1002,38526,38529],{"href":38527,"rel":38528},"https://sdqweb.ipd.kit.edu/wiki/Vitruvius",[1006],"Vitruvius (WIP)"," stellte er mächtige Werkzeuge vor, mit denen man schon\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.",[1065,38532,38534],{"id":38533},"leadership-hacks","Leadership Hacks",[439,38536,38537,38538,38543,38544,38548],{},"Eine der originellsten und interessantesten Sessions war ",[1002,38539,38542],{"href":38540,"rel":38541},"https://twitter.com/benjamin",[1006],"Benjamin Reitzammers"," Talk\nüber ",[1002,38545,38534],{"href":38546,"rel":38547},"https://entwicklertag.de/frankfurt/2015/5-leadership-hacks-oder-wie-ich-meine-ideen-umgesetzt-bekomme",[1006],".\nStatt der erwarteten Tricks zur Mitarbeitermanipulation lieferte er eine Anleitung, wie man durch sensibles und\nrücksichtsvolles Teamplayerverhalten seine Kollegen auf die eigene Seite bringt. Dabei spielen so einfache Dinge wie\nMittagessen und Zuhören schon eine große Rolle. Er appellierte in diesem Zusammenhang an Eigenschaften wie Sensibilität,\nVerlässlichkeit, Selbstreflexion und Kommunikation. Seine wichtigste Message: Don’t be a Rockstar!",[439,38550,38551],{},[1002,38552,38555],{"href":38553,"rel":38554},"https://media.synyx.de/uploads//2015/02/rockstar.png",[1006],[2205,38556],{"alt":38557,"src":38558},"rockstar","https://media.synyx.de/uploads//2015/02/rockstar-1024x747.png",[1065,38560,38562],{"id":38561},"pecha-kucha","Pecha Kucha",[439,38564,38565,38566,38570],{},"Eine für uns neue Vortragstechnik, welche wir auf den Frankfurter Entwicklertagen kennenlernen durften, war\ndas ",[1002,38567,38562],{"href":38568,"rel":38569},"http://de.wikipedia.org/wiki/Pecha_Kucha",[1006],". Bei dieser Form des Vortrages wird die Anzahl der Folien\nauf 20 sowie die Projektionszeit je Folie auf 20 Sekunden begrenzt. Das Format versprach viel Informationen in kurzer\nZeit und so war es auch. Zudem gab es viel zu lachen. Ein durchaus gelungenes Format mit tollen Vorträgen.",[439,38572,38573,38574,38579],{},"Dr. Michael Eichberg gelang es in seinem\nVortrag ",[1002,38575,38578],{"href":38576,"rel":38577},"http://www.entwicklertag.de/frankfurt/2015/your-jdk-devil-dark",[1006],"Your JDK – The Devil is in the Dark"," mit kurzen\nund durchaus amüsanten Codeausschnitten des OpenJDK 8 einen Einblick in die kleinen Abgründe der Programmierung zu\nvermitteln. Unser Vorschlag für den Leadership Hack #6:Your JDK – The Devil is in the Dark mit einem Kollegen\nanschauen.",[439,38581,38582,38583,38588,38589,38594],{},"Im Votrag ",[1002,38584,38587],{"href":38585,"rel":38586},"http://www.entwicklertag.de/frankfurt/2015/its-all-about-fun",[1006],"It’s All About The Fun"," von Jens Schauder gab\nes einen kurzen Abriss über die Wichtigkeit des Spaßes in einem Unternehmen und dass dieser nie zu kurz kommen darf.\nAuch durchaus ungeliebte Aufgaben können mit der ",[1002,38590,38593],{"href":38591,"rel":38592},"http://de.wikipedia.org/wiki/Pomodoro-Technik",[1006],"Pomodoro-Technik"," und\neinem ‘Bonbon’ nach dieser Tätigkeit zu einem positiven Effekt führen. Ein schöner Vortrag dem wir uns nur anschließen\nkönnen. Spaß bei der Arbeit zeigt uns, warum wir uns dazu entschlossen haben, unser Hobby zum Beruf zu machen.",[439,38596,38597,38598,38603],{},"Jeder hat ihn in der Firma und kennt ihn nur zu gut.\nDen ",[1002,38599,38602],{"href":38600,"rel":38601},"http://www.entwicklertag.de/frankfurt/2015/der-prozess-sheriff-eine-unliebsame-spezies",[1006],"Prozess Sheriff",". Er\nbeharrt darauf den Prozess strikt und ohne Kompromisse zu folgen. Ob dieser in einem agilen Umfeld mit Scrum noch eine\nBerechtigung hat stellte uns Steffen Brandt vor. Ein netter Talk um sich an seine Kollegen zu erinnern und ein wenig zu\nschmunzeln.",[1065,38605,38607],{"id":38606},"antifragile-software-für-die-welt-des-21-jahrhunderts","Antifragile Software für die Welt des 21. Jahrhunderts",[439,38609,38610,38611,38615],{},"Der Nachmittag startete mit einem sehr spannenden Vortrag von Johannes\nLink, ",[1002,38612,38607],{"href":38613,"rel":38614},"https://entwicklertag.de/frankfurt/2015/keynote-antifragile-software-f%C3%BCr-die-welt-des-21-jahrhunderts",[1006],",\nüber die Antifragilität von Systemen und was dies bedeutet. Fragil ist wahrscheinlich jedem von uns ein Begriff. Es\nbeschreibt die Eigenschaft eines Systems, welches durch starke Belastungen von außen beschädigt oder sogar zerstört\nwerden könnte. Entscheidend dabei ist, dass dieses Verhalten deterministisch anhand der Stärke der Belastung wiederholt\nhervorgerufen werden kann.",[439,38617,38618],{},[1002,38619,38622],{"href":38620,"rel":38621},"https://media.synyx.de/uploads//2015/02/fragil.png",[1006],[2205,38623],{"alt":38624,"src":38625},"fragil","https://media.synyx.de/uploads//2015/02/fragil-1024x762.png",[439,38627,38628],{},"Natürliche Systeme wie zum Beispiel die Menschheit sind hingegen antifragil. Sie werden durch Belastungen ihrer Umwelt\nzunächst stärker, bis die Belastung zu extrem wird und eine negative Wirkung eintritt.",[439,38630,38631],{},"Wenn man dieses Konzept auf Softwaresysteme anwenden könnte, würde dies bedeuten, dass ein System sich selbst\nstabilisiert und anhand von, zum Beispiel, unüblichen Anfragen lernen würde. Das System würde robuster werden. Dieser\nAnsatz ist neu und sehr spannend und wirft die Frage auf, ob man ein System erschaffen kann, welches z.B. anhand von\nzyklisch wiederkehrenden selbst initiierten DoS Attacken antifragiler werden würde, sozusagen eine DoS Impfung erhält?",[439,38633,38634],{},[1002,38635,38638],{"href":38636,"rel":38637},"https://media.synyx.de/uploads//2015/02/hormesis2.png",[1006],[2205,38639],{"alt":38640,"src":38641},"hormesis","https://media.synyx.de/uploads//2015/02/hormesis2-1024x767.png",[439,38643,38644,38645,38650],{},"In der Natur wird dieses Phänomen ",[1002,38646,38649],{"href":38647,"rel":38648},"http://de.wikipedia.org/wiki/Hormesis",[1006],"Hormesis"," genannt. Bei der ein Organismus,\nbzw. in unserem Fall ein System, eine positive Wirkung bei Belastung erfährt. Erst bei einer Überdosis ist die\nFunktionalität nicht mehr gewährleistet und das System kann seinen Dienst nicht mehr erfüllen.",[439,38652,38653],{},"Zusammengefasst ein sehr guter Vortrag und ein spannendes Themengebiet. Mehr davon!",[1065,38655,38657],{"id":38656},"weitere-vorträge","Weitere Vorträge",[439,38659,38660,38661,38664,38665,38670,38671,18762,38676,38681,38682,38687,38688,38693,38694,38699],{},"Auch von den restlichen Vorträgen waren einige erwähnenswert. Hagen Buchwald vom\nVeranstalter ",[1002,38662,38502],{"href":38500,"rel":38663},[1006]," erläuterte auf hohem Niveau und mit vielen Erfahrungen im\nGepäck inwiefern der Pfad eines Unternehmens zur Agilität von\nder ",[1002,38666,38669],{"href":38667,"rel":38668},"https://entwicklertag.de/frankfurt/2015/agile-transition-core-culture-matters",[1006],"Unternehmenskultur"," abhängt. Den\ngoldenen Pixel für den kreativsten Vortrag bekommen ",[1002,38672,38675],{"href":38673,"rel":38674},"https://twitter.com/jensbroos",[1006],"Jens Broos",[1002,38677,38680],{"href":38678,"rel":38679},"https://twitter.com/mrupilo",[1006],"Martin Ruprecht"," verliehen, die durch eine beeindruckende Anzahl von Analogien\nbelegten, was\ndie ",[1002,38683,38686],{"href":38684,"rel":38685},"https://entwicklertag.de/frankfurt/2015/was-wir-software-entwickler-vom-modernen-fussball-lernen-k%C3%B6nnen",[1006],"Softwareentwicklung mit Fußball","\ngemein hat und was wir uns bei unserer täglichen Arbeit von den Kickern abschauen sollten. Einer der wenigen Vorträge\nmit Code auf den Folien war der Talk von ",[1002,38689,38692],{"href":38690,"rel":38691},"https://twitter.com/dasniko",[1006],"Niko Köbler",", der verschiedene Wege und\nToolchains\naufzeigte, ",[1002,38695,38698],{"href":38696,"rel":38697},"https://entwicklertag.de/frankfurt/2015/nodejs-auf-der-jvm-nodyn-und-avatarjs-im-vergleich",[1006],"Node.js auf der JVM","\nzu nutzen, wobei anscheinend Nashörner aller Art eine größere Rolle spielen.",[439,38701,38702],{},[1002,38703,38706],{"href":38704,"rel":38705},"https://media.synyx.de/uploads//2015/02/aufstellung.png",[1006],[2205,38707],{"alt":38708,"src":38709},"aufstellung","https://media.synyx.de/uploads//2015/02/aufstellung-1024x769.png",[1065,38711,1385],{"id":1384},[439,38713,38714],{},"Wieder zurück auf unserem heimischen Bürostuhl können wir sagen, dass der Entwicklertag Frankfurt eine nette kleine\nKonferenz ist, die man sich anschauen sollte, wenn man in der Nähe ist.",[439,38716,38717,38718,38722],{},"Die Qualität der Talks war hoch, die Atmosphäre angenehm und das Catering ließ kaum Wünsche offen. Die Folien\nsind ",[1002,38719,8234],{"href":38720,"rel":38721},"http://www.entwicklertag.de/frankfurt/2015/programm",[1006]," öffentlich zugänglich. Das Einzige Manko dieses Jahr:\nEs wurden für unseren Geschmack zu wenige Informationen vermittelt, die unsere konkrete tägliche Arbeit als Entwickler\nverbessern können. Zu einem großen Anteil wurden die Themen in Form abstrakter Konzepte auf hohem Level behandelt.\nObwohl wir das generell begrüßen und die Themen dank der guten Speaker interessant präsentiert wurden, haben wir mehr\nkonkrete Beispiele und Vermittlung von Best Practices vermisst. Das Fazit der Konferenz ist daher: Neat stuff, but too\nmeta. Relevant XKCD:",[439,38724,38725],{},[1002,38726,38729],{"href":38727,"rel":38728},"http://xkcd.com/1447/",[1006],[2205,38730],{"alt":469,"src":38731},"http://imgs.xkcd.com/comics/meta-analysis.png",[439,38733,38734],{},[990,38735,38736],{},"Quelle: xkcd.com",{"title":469,"searchDepth":507,"depth":507,"links":38738},[38739,38740,38741,38742,38743],{"id":38533,"depth":547,"text":38534},{"id":38561,"depth":547,"text":38562},{"id":38606,"depth":547,"text":38607},{"id":38656,"depth":547,"text":38657},{"id":1384,"depth":547,"text":1385},[1030],"2015-02-26T17:41:10","Während der Karlsruher Entwicklertag der andrena objects ag\\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\\ndie hohe Qualität der Veranstaltung ab als Professor Ralf Reussner\\nvom KIT einen\\neindrucksvollen Einblick in die Informatik-Forschung\\nbot. Mit den von ihm betreuten Forschungsprojekten Palladio\\nund Vitruvius (WIP) stellte er mächtige Werkzeuge vor, mit denen man schon\\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.","https://synyx.de/blog/entwicklertag-frankfurt-2015/",{},"/blog/entwicklertag-frankfurt-2015",{"title":38482,"description":38751},"Während der Karlsruher Entwicklertag der andrena objects ag\nschon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum\nzweiten Mal nach Frankfurt. Fünf synyx-Kollegen dachten sich: “Die nehmen wir doch mit!” und bestiegen den ICE in\nRichtung Deutschlands Bankenmetropole. Die Entwicklergemeinde wurde dort in einem Gebäude der Goethe-Universität\nempfangen und mit ausreichend Kaffee und Mate für den ganzen Tag versorgt. Bereits in der Opening Session zeichnete sich\ndie hohe Qualität der Veranstaltung ab als Professor Ralf Reussner\nvom KIT einen\neindrucksvollen Einblick in die Informatik-Forschung\nbot. Mit den von ihm betreuten Forschungsprojekten Palladio\nund Vitruvius (WIP) stellte er mächtige Werkzeuge vor, mit denen man schon\nvor der Implementierung eines Projekts die Auswirkungen von Designentscheidungen abschätzen kann. Nach diesem gelungenen\nAuftakt begann die eigentliche Konferenz mit in drei Tracks strukturierten Talks.","blog/entwicklertag-frankfurt-2015",[9546,13219,8276,38754,9556,9557,14723],"etffm","Während der Karlsruher Entwicklertag der andrena objects ag schon seit 2010 ein etabliertes Event in Karlsruhe ist, brachte der Veranstalter die Konferenz dieses Jahr erst zum zweiten Mal nach Frankfurt.…","Y5Ik2VCxHSvhdNBoZcEzMwcUcLB9z9vouEobTc4_YPk",{"id":38758,"title":38759,"author":38760,"body":38761,"category":38831,"date":38832,"description":38833,"extension":1034,"link":38834,"meta":38835,"navigation":916,"path":38836,"seo":38837,"slug":38765,"stem":38838,"tags":38839,"teaser":38840,"__hash__":38841},"blog/blog/null-prozent-mitarbeiterfluktuation-in-den-letzten-3-jahren.md","Null-Prozent-Mitarbeiterfluktuation in den letzten 3 Jahren",[312],{"type":432,"value":38762,"toc":38829},[38763,38766,38769,38772,38775,38796,38799,38802,38805,38808,38811,38814,38817,38826],[435,38764,38759],{"id":38765},"null-prozent-mitarbeiterfluktuation-in-den-letzten-3-jahren",[439,38767,38768],{},"Wo gibt’s das noch im Jahr 2015 und in der Softwareindustrie? synyx ist mit aktuell fast 50 Mitarbeitern keine kleine\nEntwicklerbude mehr. Seit 2012 ist unsere Mitarbeiterzahl um ca. 1/3 angestiegen.",[439,38770,38771],{},"Obwohl jeder Entwickler mehrere Jobangebote pro Woche von Headhuntern über Xing, LinkedIn und andere Kanäle erhält,\nbleiben die Kollegen bei synyx. Und das ist gut für das Unternehmen! Es gibt weniger Unruhe, die Zusammenarbeit in den\nTeams wird immer besser, Wissen bleibt erhalten und wird ausgebaut und das kommt letztendlich auch dem Kunden zugute.\nDieser kann auch nach Jahren auf die gleichen Mitarbeiter „zurückgreifen“.",[439,38773,38774],{},"Wie erreicht man eine hohe Mitarbeiterbindung? Auch, wenn ich jetzt u.a. unseren Marktbegleitern helfe, möchte ich doch\nein paar Tipps los werden, wie man die Mitarbeiterbindung verbessern kann. Also gut aufgepasst liebe Geschäftsführer und\nVorstände 😉",[439,38776,38777,38778,38784,38785,38790,38791,1402],{},"1. Gehalt – offensichtlich! Es muss den Lebensumständen entsprechen und so hoch sein, dass ich als Mitarbeiter nicht\nmehr drüber nachdenken muss. Das ist eine Voraussetzung! Ein zu niedriges Gehalt demotiviert. Aaaaber, ein hohes Gehalt\nist nicht der Grund, warum Mitarbeiter im Unternehmen bleiben. Motivation/Mitarbeiterbindung über Bonus und ähnliche\nvariable Gehaltsbestandteile zu fördern funktioniert nicht in unserer Branche, zumindest nicht längerfristig. Siehe dazu\nu.a.\nden ",[1002,38779,38783],{"href":38780,"rel":38781,"title":38782},"http://www.ted.com/talks/dan_pink_on_motivation/transcript?language=en",[1006],"TED talk Dan Pink","TED Vortrag von Dan Pink",",\nBücher von ",[1002,38786,38789],{"href":38787,"rel":38788,"title":38789},"http://de.wikipedia.org/wiki/Lutz_von_Rosenstiel",[1006],"Lutz von Rosenstiel"," oder\ndie ",[1002,38792,38795],{"href":38793,"rel":38794,"title":38795},"http://www.haygroup.com/downloads/de/Mitarbeiter_sind_kauflich_Ihre_Motivation_nicht.pdf",[1006],"Studie der HayGroup zur Arbeitsmotivation",[439,38797,38798],{},"2. Die Richtigen einstellen! Im Zweifelsfall zählt die Persönlichkeit eines Bewerbers mehr als seine Job- oder\nTechnikerfahrung. Wenn er vom menschlichen in das Unternehmen passt, kann ich ihm immer noch das fehlende Handwerkszeug\nbeibringen. Der umgekehrte Fall ist eher schwierig.",[439,38800,38801],{},"3. Das richtige Werkzeug! Ein gut ausgestatteter Arbeitsplatz mit einem guten Stuhl, großen Monitoren, Dockingstation,\nNotebook mit ordentlich RAM, freier Wahl des Betriebssystems und der IDE sowie freien Getränken und Kaffee kosten nicht\ndie Welt und tragen ungemein dazu bei, effektiv und angenehm arbeiten zu können.",[439,38803,38804],{},"4. Weiterbildung! Damit ist nicht gemeint, dass man Bücher kauft, die ausgeliehen werden und nach der Arbeit studiert\nwerden können. Wenn man will, dass sich Mitarbeiter weiter entwickeln muss man ihnen die Möglichkeit dazu einräumen. Das\nbedeutet man muss Geld und Zeit (wieder Geld) zur Verfügung stellen. Tut man dies gar nicht, verliert das Unternehmen\nseine Wettbewerbsfähigkeit, tut man es auf dem Rücken der Mitarbeiter (siehe Zeit!) wird das Unternehmen das Know-how\nnicht behalten können und – „Trommelwirbel“ – verliert seine Wettbewerbsfähigkeit. Your choice?!",[439,38806,38807],{},"5. Flexible Arbeitsmodelle! Je nach persönlicher und familiärer Situation kann es sein, dass man zeitweise von zuhause\narbeiten will, man nicht mehr hunderte Kilometer pro Woche unterwegs sein kann oder auch einfach nur weniger arbeiten\nmöchte. Technisch und organisatorisch lässt sich dies meistens regeln. Nicht immer von heute auf morgen, aber mit einem\ngewissen Vorlauf, so dass es letztendlich für Arbeitgeber und Arbeitnehmer passt. Der Mitarbeiter wird es Danken!",[439,38809,38810],{},"6. Auf Projekte verzichten! Es ist besser auf manche Projekte zu verzichten, wenn die Bedingungen nicht zu den\nMitarbeitern passen. Das können lange vor Ort Einsätze sein, bei denen der Arbeitsort weit vom eigentlichen\nLebensmittelpunkt entfernt ist. Oder, das können auch Projekte bei Firmen sein, mit deren Branche sich Mitarbeiter\nüberhaupt nicht identifizieren können. Macht man trotzdem solche Projekte, wird sich der Gewinn über kurz oder lang in\nLuft auflösen, weil er von HR-Akquise und Know-how-Aufbaukosten aufgefressen wird.",[439,38812,38813],{},"7. Kommunikation und Mitbestimmung! Wenn der Azubi ohne Angst beim Chef klopfen und ihn etwas Fragen kann, und der ihm\naufrichtig zuhört, dann kann man davon ausgehen, dass in der Beziehung vieles richtig läuft.",[439,38815,38816],{},"8. Sinnvolle Unternehmensziele! Jetzt muss ich manche Geschäftsführer und Vorstände enttäuschen: „Wachstum in Höhe von\nx % als DAS Ziel zu definieren macht überhaupt gar keinen Sinn“! Wachstum kann ein Mittel zum Zweck sein, um ein\nanderes, sinnvolles Ziel zu erreichen, aber alleine stehend ohne Grenze ist Wachstum in einer begrenzten Welt ziemlich\nSinn befreit.",[439,38818,38819,38820,38825],{},"Diese Liste lässt sich beliebig lang fortführen und beschreibt nur ein paar Dinge, die meiner Meinung nach bei synyx\nrichtig gemacht werden. Letztendlich sind es viele Puzzleteile, die alle einen Beitrag für Wohlbefinden, Motivation und\nIdentifikation leisten. Passend dazu, schau mal auf",[1002,38821,38824],{"href":38822,"rel":38823,"title":38824},"http://www.synyx.de/jobs/",[1006],"synyx Jobs",";-).",[439,38827,38828],{},"Meinungen? „Feuer frei“!",{"title":469,"searchDepth":507,"depth":507,"links":38830},[],[1031],"2015-02-12T17:22:22","Wo gibt’s das noch im Jahr 2015 und in der Softwareindustrie? synyx ist mit aktuell fast 50 Mitarbeitern keine kleine\\nEntwicklerbude mehr. Seit 2012 ist unsere Mitarbeiterzahl um ca. 1/3 angestiegen.","https://synyx.de/blog/null-prozent-mitarbeiterfluktuation-in-den-letzten-3-jahren/",{},"/blog/null-prozent-mitarbeiterfluktuation-in-den-letzten-3-jahren",{"title":38759,"description":38768},"blog/null-prozent-mitarbeiterfluktuation-in-den-letzten-3-jahren",[],"Wo gibt’s das noch im Jahr 2015 und in der Softwareindustrie? synyx ist mit aktuell fast 50 Mitarbeitern keine kleine Entwicklerbude mehr. Seit 2012 ist unsere Mitarbeiterzahl um ca. 1/3…","iugHOeLccKJ3spWSYN0tHv9-lbpVNDYBXNvfLyAfeko",{"id":38843,"title":38844,"author":38845,"body":38846,"category":39040,"date":39041,"description":38853,"extension":1034,"link":39042,"meta":39043,"navigation":916,"path":39044,"seo":39045,"slug":39046,"stem":39047,"tags":39048,"teaser":39049,"__hash__":39050},"blog/blog/rancid-on-ubuntu-14-10.md","RANCID on Ubuntu 14.10",[178],{"type":432,"value":38847,"toc":39038},[38848,38851,38854,38857,38865,38870,38873,38878,38881,38892,38897,38900,38908,38911,38929,38932,38937,38940,38957,38962,38965,38985,38990,38993,38998,39001,39006,39009,39012,39017,39020,39031],[435,38849,38844],{"id":38850},"rancid-on-ubuntu-1410",[439,38852,38853],{},"Just a quick one today…",[439,38855,38856],{},"RANCID (Really Awesome New Cisco config Differ) is a software to monitor a routers software and hardware configuration,\nand to maintain history of configuration changes by using CVS.",[439,38858,38859,38860,1402],{},"If you need more information about Rancid, ",[1002,38861,38864],{"href":38862,"rel":38863},"http://www.shrubbery.net/rancid/",[1006],"you can take a look at their website",[439,38866,38867],{},[448,38868,38869],{},"Installing RANCID",[439,38871,38872],{},"Installing Rancid is easy:",[11947,38874,38875],{},[439,38876,38877],{},"root@[server]:/# apt-get install rancid",[439,38879,38880],{},"After the installation, we can check for a new group and user on the system:",[11947,38882,38883,38886,38889],{},[439,38884,38885],{},"root@[server]:/# cat /etc/group /etc/passwd | grep rancid",[439,38887,38888],{},"rancid❌133:",[439,38890,38891],{},"rancid❌122:133::/var/lib/rancid:/bin/bash",[439,38893,38894],{},[448,38895,38896],{},"Configuring RANCID",[439,38898,38899],{},"First, let’s create groups to organize our devices, such as “switches” and “router” groups, and/or you might want to\ngroup them by their location.",[11947,38901,38902,38905],{},[439,38903,38904],{},"root@[server]:/# vi /etc/rancid/rancid.conf",[439,38906,38907],{},"LIST_OF_GROUPS=”switches”",[439,38909,38910],{},"To receive Email-Notifications about configuration changes, we need to add email aliases in our /etc/aliases file.",[11947,38912,38913,38916,38919,38922],{},[439,38914,38915],{},"root@[server]:/# vi /etc/aliases",[439,38917,38918],{},"rancid-admin-switches: rancid-switches",[439,38920,38921],{},"rancid-switches: admins",[439,38923,38924,38925],{},"admins: ",[1002,38926,38928],{"href":38927},"mailto:admin@your-domainname.xyz","admin@your-domainname.xyz",[439,38930,38931],{},"Now we need to run rancid-cvs, to create the CVS folder structure that our device configurations will be stored in.\nMake sure to run this command as the RANCID user.",[11947,38933,38934],{},[439,38935,38936],{},"root@[server]:/# sudo su -c /var/lib/rancid/bin/rancid-cvs -s /bin/bash -l rancid",[439,38938,38939],{},"You should now see a bunch of new directories in /var/lib/rancid, named after the groups you defined earlier (in our\nexample, this would be /var/lib/rancid/switches. Inside each of these directories, there will be a file named router.db.\nThis is where we tell RANCID what devices exist for each group. The format of the device definition is “hostname:type:\nstatus”, where “hostname” is the FQDN or IP, “type” is the type of the device and “status” is up or down.",[11947,38941,38942,38945,38948,38951,38954],{},[439,38943,38944],{},"root@[server]:/# vi /var/lib/rancid/switches/router.db",[439,38946,38947],{},"switch1.your-domainname.xyz:hp:up",[439,38949,38950],{},"switch2.your-domainname.xyz:hp:up",[439,38952,38953],{},"1.2.3.4:hp:up",[439,38955,38956],{},"1.2.3.5:hp:down",[439,38958,38959],{},[448,38960,38961],{},"Login & Authentication",[439,38963,38964],{},"Next, we edit /var/lib/rancid/.cloginrc to tell RANCID how to access the devices. Depending on your devices, this might\nor might not be fairly easy or quite complicated. It’s best to man cloginrc to see all available options that you can\nuse. In our example, we use a simple HP-Router setup:",[11947,38966,38967,38970,38973,38976,38979,38982],{},[439,38968,38969],{},"root@[server]:/# vi /var/lib/rancid/switches/.cloginrc",[439,38971,38972],{},"add method switch1.your-domainname.xyz {ssh}",[439,38974,38975],{},"add cyphertype switch1.your-domainname.xyz {3des}",[439,38977,38978],{},"add user switch1.your-domainname.xyz {username}",[439,38980,38981],{},"add password switch1.your-domainname.xyz {password} {enable_password}",[439,38983,38984],{},"add autoenable switch1.your-domainname.xyz",[439,38986,38987],{},[448,38988,38989],{},"Testing our setup",[439,38991,38992],{},"We can test our setup by using clogin with a configuration and device specified:",[11947,38994,38995],{},[439,38996,38997],{},"root@[server]:/# /usr/lib/rancid/bin/clogin -f /var/lib/rancid/.cloginrc switch1.your-domainname.xyz",[439,38999,39000],{},"If you have done everything right, you will end up in enable mode on the specified device. It’s time to test the real\nthing now: Let’s go ahead and manually invoke a rancid-run.",[11947,39002,39003],{},[439,39004,39005],{},"root@[server]:/# sudo su -c /var/lib/rancid/bin/rancid-run -s /bin/bash -l rancid",[439,39007,39008],{},"This command may take a while to run, depending on how many devices you have configured. After it finished, you should\nreceive Emails from RANCID sent to the addresses that you specified earlier. You can now also review the logfiles in\n/var/log/rancid to see if there are any problems, and check for the downloaded configuration files in\n/var/lib/rancid//configs.",[439,39010,39011],{},"You might end up running into a problem though, where logging in with clogin works fine, but when trying to actually run\na rancid-run, it will leave you with a “timeout” or “password incorrect” error. I found out this can cause quite a\nheadache and is caused by rancid-run depending on certain options in the .cloginrc, that are not necessary when testing\nthe .cloginrc options with clogin itself. Again, i can only point out to man cloginrc for further information and\ntroubleshooting.",[439,39013,39014],{},[448,39015,39016],{},"Finalizing RANCID",[439,39018,39019],{},"Finally, we want to automate RANCID, by creating a cronjob that calls “rancid-run” on a regular basis. Depending on\nyour needs, you can run it every 15 Minutes, once a Week or any time period in between. In this example, we trigger a\nrancid-run every half hour:",[11947,39021,39022,39025,39028],{},[439,39023,39024],{},"root@[server]:/# sudo su -c “/usr/bin/crontab -e -u rancid”",[439,39026,39027],{},"# m h dom mon dow command",[439,39029,39030],{},"/30 * * * * /usr/bin/rancid-run",[439,39032,39033,39034,1402],{},"If you end up running into any problems or just want more information, you can also check the RANCID mailing\nlist: ",[1002,39035,39036],{"href":39036,"rel":39037},"http://www.shrubbery.net/pipermail/rancid-discuss/",[1006],{"title":469,"searchDepth":507,"depth":507,"links":39039},[],[9045,1030],"2015-02-06T16:01:42","https://synyx.de/blog/rancid-on-ubuntu-14-10/",{},"/blog/rancid-on-ubuntu-14-10",{"title":38844,"description":38853},"rancid-on-ubuntu-14-10","blog/rancid-on-ubuntu-14-10",[],"Just a quick one today… RANCID (Really Awesome New Cisco config Differ) is a software to monitor a routers software and hardware configuration, and to maintain history of configuration changes…","gGFzDiMIJZsRw6kQUomE_I1g0DSc6k3V74jqlV6Fqc8",{"id":39052,"title":39053,"author":39054,"body":39055,"category":39307,"date":39308,"description":39309,"extension":1034,"link":39310,"meta":39311,"navigation":916,"path":39312,"seo":39313,"slug":39059,"stem":39315,"tags":39316,"teaser":39318,"__hash__":39319},"blog/blog/javascript-linting-tool-evaluation.md","Javascript Linting Tool Evaluation",[268,332],{"type":432,"value":39056,"toc":39298},[39057,39060,39073,39098,39107,39110,39114,39182,39185,39188,39201,39216,39219,39236,39239,39242,39253,39257,39260,39263,39266,39277,39281,39284,39293,39295],[435,39058,39053],{"id":39059},"javascript-linting-tool-evaluation",[439,39061,39062,39063,39068,39069,39072],{},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘",[1002,39064,39067],{"href":39065,"rel":39066},"http://www.javaposse.com",[1006],"The Java Posse","‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across ",[1002,39070,389],{"href":26130,"rel":39071},[1006],", using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:",[994,39074,39075,39083,39090],{},[997,39076,39077,39082],{},[1002,39078,39081],{"href":39079,"rel":39080},"http://jslint.com",[1006],"JSLint"," by Doug Crockford himself",[997,39084,39085],{},[1002,39086,39089],{"href":39087,"rel":39088},"https://developers.google.com/closure/utilities/",[1006],"Closure Linter by Google",[997,39091,39092,39097],{},[1002,39093,39096],{"href":39094,"rel":39095},"http://eslint.org",[1006],"ESLint",", the new kid on the block",[439,39099,39100,39101,39106],{},"…as well as ",[1002,39102,39105],{"href":39103,"rel":39104},"http://jshint.com/",[1006],"JSHint"," itself, of course.",[439,39108,39109],{},"We drew up a quick spreadsheet for evaluating the tools and came up with the following.",[3938,39111,39113],{"id":39112},"criteria","Criteria",[994,39115,39116,39122,39128,39134,39140,39146,39152,39158,39164,39170,39176],{},[997,39117,39118,39121],{},[448,39119,39120],{},"Performance"," How long does it take to run over our example project, a single page webapp with a couple of thousands\nof JavaScript LOC?",[997,39123,39124,39127],{},[448,39125,39126],{},"Licensing"," Does the license meet our requirements (and those of our customers, of course)?",[997,39129,39130,39133],{},[448,39131,39132],{},"Project health/adoption"," How healthy is the project? Is it on Github, and is it well maintained?",[997,39135,39136,39139],{},[448,39137,39138],{},"Completeness of configurations"," Does the tool cover all our use-cases for a linting tool?",[997,39141,39142,39145],{},[448,39143,39144],{},"Productivity (rule set creation / project setup)"," When creating a new project, is it difficult to create a matching\nruleset? Does the tool come with a reasonable default rule set, or do you need to set up all the checks yourself?",[997,39147,39148,39151],{},[448,39149,39150],{},"Productivity (active software development)"," During active development, does the tool assist the developer in\nwriting quality code, or does it bully you to the point where you’d rather abolish using a linting tool at all?",[997,39153,39154,39157],{},[448,39155,39156],{},"Quality of Documentation/Tutorials/Self Help"," How good is the project documentation? When the tool breaks the build\nwith a certain error message, how difficult is it to find reliable information on the error in question (why does it\noccur, why is it a bad practice, how to fix it)?",[997,39159,39160,39163],{},[448,39161,39162],{},"Ability to integrate with existing projects"," Is it possible to integrate the linting tool in an existing projects\nwithout making changes to the project to comply to the rules?",[997,39165,39166,39169],{},[448,39167,39168],{},"Integration with build tool"," Is it possible to integrate the linting tool into your build chain to receive direct\nfeedback?",[997,39171,39172,39175],{},[448,39173,39174],{},"ES6 support"," How well does the project support future versions of the language?",[997,39177,39178,39181],{},[448,39179,39180],{},"Pluggable"," Is it possible to extend the given rule set with custom checks?",[3938,39183,39105],{"id":39184},"jshint",[439,39186,39187],{},"The first tool we looked at was the already-familiar JSHint. We already knew what was bothering us about it:",[994,39189,39190,39198],{},[997,39191,39192,39193,39197],{},"Its hard to find the documentation for a certain error message. While the error messages itself are mostly\nself-explanatory, it can be somewhat difficult to find out how to deactivate or customize a certain rule. For\nexample, ",[1002,39194,39195],{"href":39195,"rel":39196},"http://jshint.com/docs/options/",[1006]," JSHint has both ‘enforcing’ and ‘relaxing’ rules. While setting a ‘enforcing’\nrule to true turns it on, setting a ‘relaxing’ rule to true deactivates it.",[997,39199,39200],{},"More often than not, using JSHint can be frustrating. For example, we had ‘maxdepth’",[439,39202,39203,39204,39207,39208,39211,39212,39215],{},"set to 3, meaning a maximum of three nested blocks of code was allowed. In case one of those blocks was a ‘",[990,39205,39206],{},"for … in","‘\nstatement, JSHint would (correctly) complain that its body should be wrapped in an ‘",[990,39209,39210],{},"if(obj.hasOwnProperty(key))","\n‘-check to filter out unwanted properties. However, doing so meant introducing another nested block, and if that pushed\nthe total depth beyond ‘",[990,39213,39214],{},"maxdepth","‘, JSHint would fail the build. The solution was usually to introduce private helper\nfunctions, which can make otherwise trivial code difficult to read (since you have to skip blocks of code).",[439,39217,39218],{},"Of course, that is not really the fault of JSHint (seeing that it only did what it was told to do), but it was a rather\nbig annoyance that caused us to re-evaluate our JavaScript linting practices in the first place.",[994,39220,39221],{},[997,39222,39223,39224,39229,39230,39235],{},"Being a fork of JSLint, JSHint has the same license containing the\ninfamous ",[1002,39225,39228],{"href":39226,"rel":39227,"title":39228},"http://en.wikipedia.org/wiki/JSLint#License",[1006],"JSLint License"," ‘Good, not evil’ statement.\nWhile we understand its humorous intent (and being\na ",[1002,39231,39234],{"href":39232,"rel":39233,"title":39234},"https://synyx.de/unternehmen/verantwortung_csr/",[1006],"fairly social responsible company",",\nwe wholeheartedly support it), we were worried that some corporate lawyer might not approve of our use of a tool bound\nto such a license.",[3938,39237,39081],{"id":39238},"jslint",[439,39240,39241],{},"After JSHint we decided to evaluate the old guy in the gang, JSLint. It was the first linting tool for JavaScript, and\nit feels like that. From our opinion JSLint has two major problems, besides the license (see JSHint):",[994,39243,39244,39247,39250],{},[997,39245,39246],{},"The website of JSLint is very old-school and does not contain any explanations of the ruleset or any information to\nget a link between the error messages provided by JSLint and the problem in the code. So you have to search through\nthe internet to find any third-party explanation that will help you to fix the problem.",[997,39248,39249],{},"Some of the rules of JSLint are, at least, strangely named. There is a rule to forbid (or allow, if deactivated)\n‘stupidity’. The project’s web page does not provide any explanation (again) – resorting to Google, we found out that\n‘stupidity’ referred to the usage of synchronous functions in Node.js’s file system module.",[997,39251,39252],{},"The rule set is very strict and, when you look through the GitHub issues and pull requests, it is very hard to\nparticipate in the JSLint project. That’s why the community is very small and there are a lot more people active in\nJSHint and other projects and bring in their ideas there. Maybe that is why JSLint does not provide a rule similar to\n‘latedef’ from JSHint or ‘no-use-before-define’ from ESLint. Without this rule it is very hard to structure your\ncode with private named-functions at the end without assigning the function to a variable at the start (and that\nwould not be what we want).",[3938,39254,39256],{"id":39255},"closure-linter","Closure Linter",[439,39258,39259],{},"The Closure Linter is part of Google’s Closure tool set. It was designed for internal use and provides very little\noptions for customization. Since following the rules enforced by it seems to be mandatory within Google, that is\ncertainly an acceptable practice. However, since it is (for example) not possible to change the default maximum line\nlength of 80 characters, we quickly decided not to look into the tool any more.",[3938,39261,39096],{"id":39262},"eslint",[439,39264,39265],{},"When looking at ESLint, we were quick to decide that we might be looking at a potential winner:",[994,39267,39268,39271,39274],{},[997,39269,39270],{},"While the project is (by far) the youngest (or as others might put it: the least mature) of the four tools we looked\nat, it is also the best maintained. 100+ contributors on Github and a roughly monthly release schedule speak for\nthemselves.",[997,39272,39273],{},"Applying it to our example web app, we were surprised to find out that it was sufficient to write about ten lines of\nconfiguration to perform the same amount of checks that required around a hundred lines in JSHint. Of course, that\nmight only mean that we have the same idea of quality JavaScript code as the tool’s authors, but nevertheless it meant\nthat the tool would be quite easy to adopt into our development process.",[997,39275,39276],{},"The output is quite handy: It prints both a one-line human readable error message as well as an error code for a\nquick lookup in the documentation.",[3938,39278,39280],{"id":39279},"evaluation","Evaluation",[439,39282,39283],{},"The criteria from above has been weighted from five to 15, from not important to important, and the tools got a 0, 0.5\nor 1 if it does not, almost or absolute fulfill the criterion.",[439,39285,39286],{},[1002,39287,39290],{"href":39288,"rel":39289},"https://media.synyx.de/uploads//2015/02/jsLinting2.png",[1006],[2205,39291],{"alt":39292,"src":39288},"jsLinting",[3938,39294,9392],{"id":9391},[439,39296,39297],{},"As you can see in the image above, ESLint proved to be the winner of our evaluation. We decided that its major flaw, the\npotential immaturity, was acceptable to us since by its nature, it would only be used during (internal) development. The\nease of use, both because of the robust and reasonable default rule set and the high-quality documentation, outweighed\nany concern by far. We are looking forward to adopt ESLint into our development tool chain over the coming weeks and\nmonths!",{"title":469,"searchDepth":507,"depth":507,"links":39299},[39300,39301,39302,39303,39304,39305,39306],{"id":39112,"depth":507,"text":39113},{"id":39184,"depth":507,"text":39105},{"id":39238,"depth":507,"text":39081},{"id":39255,"depth":507,"text":39256},{"id":39262,"depth":507,"text":39096},{"id":39279,"depth":507,"text":39280},{"id":9391,"depth":507,"text":9392},[1030,1412],"2015-02-03T09:45:38","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\\nus with three alternatives:","https://synyx.de/blog/javascript-linting-tool-evaluation/",{},"/blog/javascript-linting-tool-evaluation",{"title":39053,"description":39314},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:","blog/javascript-linting-tool-evaluation",[15546,39262,39279,15201,29604,39184,39238,39317,14723],"linting","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the legendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate alternatives to…","zh2oNr6CSunDzGSlgFkQ_s4GJBtXW2VSdjPx7nLpsEc",{"id":39321,"title":39322,"author":39323,"body":39324,"category":39677,"date":39678,"description":39679,"extension":1034,"link":39680,"meta":39681,"navigation":916,"path":39682,"seo":39683,"slug":39685,"stem":39686,"tags":39687,"teaser":39692,"__hash__":39693},"blog/blog/running-proxmox-ve-3-3-on-a-software-raid.md","Running Proxmox VE 3.3 on a software RAID",[178],{"type":432,"value":39325,"toc":39675},[39326,39329,39337,39342,39345,39350,39353,39364,39367,39372,39377,39380,39421,39424,39435,39438,39475,39478,39486,39489,39527,39530,39550,39553,39573,39576,39590,39593,39619,39622,39630,39633,39641,39644,39655,39658,39672],[435,39327,39322],{"id":39328},"running-proxmox-ve-33-on-a-software-raid",[439,39330,39331,39332,39336],{},"During installation of Proxmox VE 3.3 (available ",[1002,39333,22916],{"href":39334,"rel":39335},"http://proxmox.com/downloads/category/iso-images-pve",[1006],"), you are\nonly given the choice of what disk you want to use for the installation process, but not what disk layout you want to\nuse. Now, there are several ways of installing Proxmox on a software RAID. One of them is to run through the standard\ninstallation process and add a software RAID afterwards, using mdadm.",[439,39338,39339],{},[448,39340,39341],{},"This blog post is just to show how setting up a software RAID on a fresh Proxmox installation worked for me! Feel free\nto try the following steps on a clean installation – however, I recommend not to perform these actions on a live\nproduction environment! I do not take responsibility for any data loss that might occur! Also, keep in mind that running\nProxmox VE on a software RAID is neither supported nor recommended by Proxmox!",[439,39343,39344],{},"Now, that we got the disclaimer out of the way, let’s get started:",[439,39346,39347],{},[448,39348,39349],{},"Installing Proxmox",[439,39351,39352],{},"Follow through the installation, using the provided ISO installer. Keep track of what disk you use as the installation\ndisk – in my case, this would be /dev/sda. Once you complete the installation, run the following steps to get an\nup-to-date Proxmox VE Version to work with.",[11947,39354,39355,39358,39361],{},[439,39356,39357],{},"root@[server]:/# apt-get update",[439,39359,39360],{},"root@[server]:/# apt-get dist-upgrade",[439,39362,39363],{},"root@[server]:/# apt-get upgrade",[439,39365,39366],{},"Next step, if you haven’t done that already, is to install mdadm",[11947,39368,39369],{},[439,39370,39371],{},"root@[server]:/# apt-get install mdadm",[439,39373,39374],{},[448,39375,39376],{},"Setting up the software RAID mirror",[439,39378,39379],{},"After finishing the installation, this is the partitioning scheme I had to work with:",[11947,39381,39382,39385,39388,39391,39394,39397,39400,39403,39406,39409,39412,39415,39418],{},[439,39383,39384],{},"root@[server]:/# parted",[439,39386,39387],{},"GNU Parted 2.3",[439,39389,39390],{},"Using /dev/sda",[439,39392,39393],{},"Welcome to GNU Parted! Type ‘help’ to view a list of commands.",[439,39395,39396],{},"(parted) print",[439,39398,39399],{},"Model: ATA HGST HUS724020AL (scsi)",[439,39401,39402],{},"Disk /dev/sda: 2000GB",[439,39404,39405],{},"Sector size (logical/physical): 512B/512B",[439,39407,39408],{},"Partition Table: gpt",[439,39410,39411],{},"Number Start End Size File system Name Flags",[439,39413,39414],{},"1 1049kB 2097kB 1049kB primary bios_grub",[439,39416,39417],{},"2 2097kB 537MB 535MB ext3 primary boot",[439,39419,39420],{},"3 537MB 2000GB 2000GB primary lvm",[439,39422,39423],{},"Partitions 2 and 3 are the ones that need to be mirrored. Let’s prepare the second disk by copying the partition table\nfrom sda to sdb and changing the partition types to RAID.",[11947,39425,39426,39429,39432],{},[439,39427,39428],{},"root@[server]:/# sgdisk -R=/dev/sdb /dev/sda",[439,39430,39431],{},"root@[server]:/# sgdisk -t 2:fd00 /dev/sdb",[439,39433,39434],{},"root@[server]:/# sgdisk -t 3:fd00 /dev/sdb",[439,39436,39437],{},"Checking the partition table on sdb, it should now look like this:",[11947,39439,39440,39442,39444,39446,39448,39451,39454,39456,39458,39461,39463,39465,39467,39469,39472],{},[439,39441,39384],{},[439,39443,39387],{},[439,39445,39390],{},[439,39447,39393],{},[439,39449,39450],{},"(parted) select /dev/sdb",[439,39452,39453],{},"Using /dev/sdb",[439,39455,39396],{},[439,39457,39399],{},[439,39459,39460],{},"Disk /dev/sdb: 2000GB",[439,39462,39405],{},[439,39464,39408],{},[439,39466,39411],{},[439,39468,39414],{},[439,39470,39471],{},"2 2097kB 537MB 535MB primary raid",[439,39473,39474],{},"3 537MB 2000GB 2000GB primary raid",[439,39476,39477],{},"We now need to create the needed RAID arrays. Because we booted from a non-RAID partition, we are going to add a\nmissing disk into the RAID for now. This missing disk will later be replaced by sda.",[11947,39479,39480,39483],{},[439,39481,39482],{},"root@[server]:/# mdadm –create /dev/md0 –level=1 –raid-disks=2 missing /dev/sdb2",[439,39484,39485],{},"root@[server]:/# mdadm –create /dev/md1 –level=1 –raid-disks=2 missing /dev/sdb3",[439,39487,39488],{},"You might run into an error, saying “mdadm: cannot open /dev/sdb2: Device or resource busy”. In this case, make sure you\ndon’t have running RAID sets already, which could happen if your disks on this machine have been used in a RAID array\nbefore. If this is the case, you want to wipe out the first 512 bytes of your disk to clear any existing RAID metadata –\nthis was a big headache for me, took me a while to find out what the problem was.",[11947,39490,39491,39494,39497,39500,39503,39506,39509,39512,39515,39518,39521,39524],{},[439,39492,39493],{},"root@[server]:/# cat /proc/mdstat",[439,39495,39496],{},"Personalities : [raid1]",[439,39498,39499],{},"md1 : active raid1 sdb2[1]",[439,39501,39502],{},"523968 blocks super 1.2 [2/1] [_U]",[439,39504,39505],{},"[…]",[439,39507,39508],{},"unused devices:",[439,39510,39511],{},"root@[server]:/# mdadm –stop /dev/md0",[439,39513,39514],{},"mdadm: stopped /dev/md0",[439,39516,39517],{},"root@[server]:/# mdadm –stop /dev/md1",[439,39519,39520],{},"mdadm: stopped /dev/md1",[439,39522,39523],{},"root@[server]:/# dd if=/dev/zero of=/dev/sda bs=512 count=1",[439,39525,39526],{},"root@[server]:/# dd if=/dev/zero of=/dev/sdb bs=512 count=1",[439,39528,39529],{},"We need to format md0, copy grub onto it and set it up as our /boot mountpoint.",[11947,39531,39532,39535,39538,39541,39544,39547],{},[439,39533,39534],{},"root@[server]:/# mkfs.ext3 /dev/md0",[439,39536,39537],{},"root@[server]:/# mkdir /mnt/tmp",[439,39539,39540],{},"root@[server]:/# mount /dev/md0 /mnt/tmp",[439,39542,39543],{},"root@[server]:/# cp -ax /boot/* /mnt/tmp",[439,39545,39546],{},"root@[server]:/# umount /mnt/tmp",[439,39548,39549],{},"root@[server]:/# rmdir /mnt/tmp",[439,39551,39552],{},"Let’s not forget to edit the fstab, so it will mount /boot from md0 from now on. I commented out the previous /boot\nentry, just in case…",[11947,39554,39555,39558,39561,39564,39567,39570],{},[439,39556,39557],{},"/dev/pve/root / ext3 errors=remount-ro 0 1",[439,39559,39560],{},"/dev/pve/data /var/lib/vz ext3 defaults 0 1",[439,39562,39563],{},"#UUID=1e2982a5-75b8-4e62-a5dc-7a49226d842f /boot ext3 defaults 0 1",[439,39565,39566],{},"/dev/md0 /boot ext3 defaults 0 1",[439,39568,39569],{},"/dev/pve/swap none swap sw 0 0",[439,39571,39572],{},"proc /proc proc defaults 0 0",[439,39574,39575],{},"Also, you need to edit /etc/default/mdadm",[11947,39577,39578,39581,39584,39587],{},[439,39579,39580],{},"-AUTOSTART=false",[439,39582,39583],{},"+AUTOSTART=true",[439,39585,39586],{},"-INITRDSTART=’none’",[439,39588,39589],{},"+INITRDSTART=’all’",[439,39591,39592],{},"Now we need to change a few grub entries and finally reinstall grub.",[11947,39594,39595,39598,39601,39604,39607,39610,39613,39616],{},[439,39596,39597],{},"root@[server]:/# echo ‘GRUB_DISABLE_LINUX_UUID=true’ >> /etc/default/grub",[439,39599,39600],{},"root@[server]:/# echo ‘GRUB_PRELOAD_MODULES=”raid dmraid”‘ >> /etc/default/grub",[439,39602,39603],{},"root@[server]:/# echo raid1 >> /etc/modules",[439,39605,39606],{},"root@[server]:/# echo raid1 >> /etc/initramfs-tools/modules",[439,39608,39609],{},"root@[server]:/# grub-install /dev/sda",[439,39611,39612],{},"root@[server]:/# grub-install /dev/sdb",[439,39614,39615],{},"root@[server]:/# update-grub",[439,39617,39618],{},"root@[server]:/# update-initramfs -u",[439,39620,39621],{},"Once you rebooted the server, you can check if everything went as expected",[11947,39623,39624,39627],{},[439,39625,39626],{},"root@[server]:/# mount | grep boot",[439,39628,39629],{},"/dev/md0 on /boot type ext3 (rw,relatime,errors=continue,user_xattr,acl,barrier=0,data=ordered)",[439,39631,39632],{},"Next, let’s change /dev/sda2 to a RAID partition and add it into the array.",[11947,39634,39635,39638],{},[439,39636,39637],{},"root@[server]:/# sgdisk -t 2:fd00 /dev/sda",[439,39639,39640],{},"root@[server]:/# mdadm –add /dev/md0 /dev/sda2",[439,39642,39643],{},"The root partition for Proxmox is an LVM partition, so we need to create a logical volume on /dev/md1 and move the LVM\npartition over.",[11947,39645,39646,39649,39652],{},[439,39647,39648],{},"root@[server]:/# pvcreate /dev/md1",[439,39650,39651],{},"root@[server]:/# vgextend pve /dev/md1",[439,39653,39654],{},"root@[server]:/# pvmove /dev/sda3 /dev/md1",[439,39656,39657],{},"Note that this will take a looooooooong time – it took several hours for me! After you sucessfully moved the partition,\nyou can remove /dev/sda3 from the volume and add /dev/sda3 to the RAID array.",[11947,39659,39660,39663,39666,39669],{},[439,39661,39662],{},"root@[server]:/# vgreduce pve /dev/sda3",[439,39664,39665],{},"root@[server]:/# pvremove /dev/sda3",[439,39667,39668],{},"root@[server]:/# sgdisk -t 3:fd00 /dev/sda",[439,39670,39671],{},"root@[server]:/# mdadm –add /dev/md1 /dev/sda3",[439,39673,39674],{},"Finally, you can check your RAID and should see the 2 partitions healing. This is it, you are now running your Proxmox\nVE on a software RAID!",{"title":469,"searchDepth":507,"depth":507,"links":39676},[],[9045],"2015-01-30T10:28:12","During installation of Proxmox VE 3.3 (available here), you are\\nonly given the choice of what disk you want to use for the installation process, but not what disk layout you want to\\nuse. Now, there are several ways of installing Proxmox on a software RAID. One of them is to run through the standard\\ninstallation process and add a software RAID afterwards, using mdadm.","https://synyx.de/blog/running-proxmox-ve-3-3-on-a-software-raid/",{},"/blog/running-proxmox-ve-3-3-on-a-software-raid",{"title":39322,"description":39684},"During installation of Proxmox VE 3.3 (available here), you are\nonly given the choice of what disk you want to use for the installation process, but not what disk layout you want to\nuse. Now, there are several ways of installing Proxmox on a software RAID. One of them is to run through the standard\ninstallation process and add a software RAID afterwards, using mdadm.","running-proxmox-ve-3-3-on-a-software-raid","blog/running-proxmox-ve-3-3-on-a-software-raid",[39688,39689,39690,39691],"mdadm","proxmox","raid","virtualisation","During installation of Proxmox VE 3.3 (available here), you are only given the choice of what disk you want to use for the installation process, but not what disk layout…","bra0RXCo6O-w7zg-rkAW_MJVNmRQzvtAK6ExJNEmZ8M",{"id":39695,"title":39696,"author":39697,"body":39698,"category":39963,"date":39964,"description":39965,"extension":1034,"link":39966,"meta":39967,"navigation":916,"path":39968,"seo":39969,"slug":39702,"stem":39971,"tags":39972,"teaser":39978,"__hash__":39979},"blog/blog/time-series-data-is-the-the-new-big-data.md","Time Series Data is the the new Big Data",[238,178],{"type":432,"value":39699,"toc":39950},[39700,39703,39724,39745,39749,39758,39762,39780,39789,39793,39811,39815,39825,39839,39842,39846,39849,39857,39860,39864,39873,39877,39880,39884,39893,39897,39900,39933,39936],[435,39701,39696],{"id":39702},"time-series-data-is-the-the-new-big-data",[439,39704,39705,39706,39711,39712,39716,39717],{},"On 22 November 2014, the ",[1002,39707,39710],{"href":39708,"rel":39709},"https://2014.nosql-matters.org/bcn/homepage/",[1006],"NoSQL matters conference"," took place in\nBarcelona at the ",[1002,39713,35683],{"href":39714,"rel":39715},"http://www.uab-casaconvalescencia.org/en/index.php",[1006],", which is doubtless one of\nthe most beautiful locations for a conference! The Casa was declared a Historical Artistic Monument in 1978, and a World\nCultural Heritage Site in 1997, and these great halls are a great place for great\nspeakers.",[1002,39718,39721],{"href":39719,"rel":39720},"https://media.synyx.de/uploads//2014/11/20141122_090344.jpg",[1006],[2205,39722],{"alt":39723,"src":39719},"20141122_090344",[439,39725,39726,39727,39732,39733,39738,39739,39744],{},"This year, Ellen Friedman (",[1002,39728,39731],{"href":39729,"rel":39730},"https://twitter.com/ellen_friedman",[1006],"@Ellen_Friedman",") and Ted\nDunning (",[1002,39734,39737],{"href":39735,"rel":39736},"https://twitter.com/ted_dunning",[1006],"@ted_dunning",") were among them, and it is always a pleasure to listen the\nboth of them pointing out actual society-changing trends in modern big data and NoSQL technologies. Ellen Friedman held\nthe keynote, and their common topic was the necessity of processing time series data. Ellen Friedman has experience in a\nwide range of scientific fields. She is a committer for the Apache Mahout project, and contributes to Apache Drill. Ted\nDunning is also involved in various Apache projects, as a committer and PMC member of Mahout, ZooKeeper and Drill, and\nis also mentor for Apache Storm, DataFu, Flink and Optiq. Both are working for ",[1002,39740,39743],{"href":39741,"rel":39742},"http://mapr.com",[1006],"MapR"," at the moment.\nMapR also held a training at the training day of NoSQL matters Barcelona.",[3938,39746,39748],{"id":39747},"there-is-time-series-data-everywhere","There is (time series) data everywhere",[439,39750,39751,39752,39757],{},"Time series data processing and real time data analysis are a big issue nowadays, and topic of many of the last years\nNoSQL conference talks. The world gets more and more distributed, there are sensors everywhere, reporting thousands of\nmeasurement each second. The so called ",[1002,39753,39756],{"href":39754,"rel":39755},"http://en.wikipedia.org/wiki/Internet_of_Things",[1006],"Internet of Things"," (IoT)\nproduces an enormous amount of data every day: From smart meters in plants to smart shirts for athletes, almost every\nobject in our everyday life has the ability to emit data. But how to store and query the data efficiently? And first of\nall: Why do we need all the data and what to with it?",[1065,39759,39761],{"id":39760},"the-history-of-time-series-data","The history of Time Series Data",[439,39763,39764,39765,520,39768,18175,39770,39773,39774,39779],{},"Time series are an old idea, the city of Barcelona stores data about the citizens extensively since the 13th Century. An\nimpressive example of a ",[448,39766,39767],{},"crowdsourced",[448,39769,25317],{},[448,39771,39772],{},"big data"," analysis project dates back about 170\nyears: ",[1002,39775,39778],{"href":39776,"rel":39777},"http://en.wikipedia.org/wiki/Matthew_Fontaine_Maury",[1006],"Matthew Fountaine Maury",", a mariner with the United States\nNavy in the mid-19th century, who was forced to desk-work after a leg injury left him unfit for sea duty, devoted his\ntime to the study of navigation, meteorology, winds, and currents. As officer-in-charge of the Navy office in\nWashington, DC, Maury became a librarian of the many unorganized log books and records in 1842. There, he sought to\nimprove seamanship through sorting the available, yet unorganized information in his office, analyzing roughly one\nbillion data points by hand!",[439,39781,39782,39783,39788],{},"His thorough analysis resulted in his ",[1002,39784,39787],{"href":39785,"rel":39786},"http://icoads.noaa.gov/maury.pdf",[1006],"wind and weather charts",". Maury made them\nwidely available by sharing them among other Captains, on the condition that they report and share their own logs back\nto his office, therefore providing a constant data base to continuously improve his charts. In 1848, Captain Jackson was\nable to shorten his journey from Baltimore to Rio de Janeiro by more than a month by exploiting Maurys charts. After\nthat Maurys charts spread among Captains, and in 1853 the data was the basis for the fastest voyage from New York to San\nFrancisco, made by the Flying Cloud under the female navigator Eleanor Creesy – a record that lasted for over a hundred\nyears.",[1065,39790,39792],{"id":39791},"see-with-your-eyes-closed-think-with-your-eyes-open","See with your eyes closed – think with your eyes open",[439,39794,39795,39796,39799,39800,39803,39804,39807,39808,1402],{},"This example does not only show how time series data analysis can be used for informed ",[448,39797,39798],{},"data-driven decisions",", it\nalso shows that thinking about your use-cases and data is important. What is it you want to achieve, and what does your\ndata tell you? Ellen Friedman proposed to close your eyes for a moment or two, and think about the data you gathered,\n",[990,39801,39802],{},"look"," at it the right way and let it tell you what is in it. With your eyes open again, try to find out more about it,\nsearch for trends and hidden secrets – it is basically like a crime story. Maurys vision of the charts was ",[990,39805,39806],{},"eyes closed\nseeing",", his keen observation and focus on the details ",[990,39809,39810],{},"eyes open thinking",[1065,39812,39814],{"id":39813},"big-data-in-the-blink-of-an-eye","Big data in the blink of an eye",[439,39816,39817,39818,39820,39821,39824],{},"Todays sensors emit much more data than Maury had available to take into account for his charts. Thousands to millions\nof data points are collected by sensors, smart meters, RFIDs and many more every second of every day. In modern power\nplants almost every part, from pumps to valves, constantly sends data about its state, temperature, processed fluids and\nmany other information. All this data is valuable and offers enormous opportunities: Critical states that might not have\nbeen taken into account, could lead to failure and thanks to being able to detect unusual data values reported from the\nsensors, life threatening situations can be detected ",[448,39819,36980],{}," they occur. Correlations of events, deducible from the\nevents of the time series of different parts can help to understand situations leading to failures, and therefore reduce\nrisks in the future. ",[448,39822,39823],{},"Prediction"," of material fatigue and failure behavior could be achieved as well as *\n*classification** and **anomaly detection**. In general, in a wide range of events ranging from natural sciences to\nmonetary businesses to marketing to medical care",[994,39826,39827,39830,39833,39836],{},[997,39828,39829],{},"prognostication",[997,39831,39832],{},"introspection",[997,39834,39835],{},"prediction and",[997,39837,39838],{},"diagnosis",[439,39840,39841],{},"might get possible.",[3938,39843,39845],{"id":39844},"how-to-use-time-series-databases","How to use time series databases",[439,39847,39848],{},"Now that we understand why time series data is valuable, we are interested in how to process them? Friedman and Dunning\nnicely explained the necessity of dedicated time series database technologies. Usually, time series data is very simple:\nA (static) data source emits time/value pairs, and thats basically it. If you have",[994,39850,39851,39854],{},[997,39852,39853],{},"a huge amount of (time series) data, and",[997,39855,39856],{},"queries mostly based on time or time ranges",[439,39858,39859],{},"you might think about using a time series databases (TSDB) that enables you to efficiently analyze the data.",[1065,39861,39863],{"id":39862},"why-not-use-a-relational-database-system","Why not use a relational database system?",[439,39865,39866,39867,39872],{},"Interestingly, data storage is less of a problem than efficient data retrieval. A traditional relational database\nsystem (RDBMS) does not suffice when it comes to efficient time series data retrieval. The overhead generated by unused\ntransaction management and query optimizers, together with the row-by-row retrieval forced by star schemas, does not\nallow for efficient response times. And again, scaling of an RDBMS is hardly possible. The solution are specialized\nTSDBs, based on open source NoSQL technologies, and a smart data model to overcome said deficiencies. As a foundation,\nthe distributed file system of Hadoop is appropriate, backed by the NoSQL wide column store Hbase (or MapR-DB). The\nclever combination of semi-structured wide columns with blob compression techniques can lead to rates of up to 100\nmillion data point per second on a 10 nodes, good equipped, cluster with 4 nodes active. Quite an impressive data rate.\nThe usage of in-memory structures enables fast computation, and write-ahead logs ensure reliable durability of the\ndatabase. A time series database system is implemented with ",[1002,39868,39871],{"href":39869,"rel":39870},"http://opentsdb.net/",[1006],"OpenTSDB",", based on MapR-DB and\nHadoops HDFS. On a 4-node MapR-DB cluster, 30 million data point can be retrieved, aggregated and plotted in less than\n20 seconds.",[1065,39874,39876],{"id":39875},"where-to-go-from-here","Where to go from here?",[439,39878,39879],{},"What can we do with all this data, why should we collect and keep it, and what can we learn from it? There is a variety\nof use cases, including machine learning techniques, that classify data or detect anomalies. There are good algorithms\navailable, and the combination of open source technologies backed by Hadoop make the Hadoop ecosystem applicable: That\nmeans the availability of Apache Mahout for machine learning, Apache Spark for data analysis (at the moment preferable\nover Hive), or Apache Drill for data analytics.",[3938,39881,39883],{"id":39882},"and-this-is-how-nosql-changes-society","And this is how NoSQL changes society",[439,39885,39886,39887,39892],{},"Friedman and Dunning pointed out how modern society’s Internet has reversed the flow of data: Instead of demanding data\nfrom servers, applications now often push (time series) data into databases. NoSQL and open source technologies allow\nfor the analysis of these data, hence enabling society to take advantage of all the data gathered. Maury gave a good\nexample, and his proceeding hopefully becomes a widely accepted and widespread way to gather and share data for data\nanalytics. An example is the ",[1002,39888,39891],{"href":39889,"rel":39890},"http://en.wikipedia.org/wiki/Unique_Identification_Authority_of_India",[1006],"Aadhaar-project","\nthat aims for identification without regard to cast, creed, religion or geography in India to ensure better welfare\nservices, and that runs on the NoSQL database MapR-DB. Let’s use the potential and power of the technologies together\nfor the better good of society.",[3938,39894,39896],{"id":39895},"further-readings","Further readings",[439,39898,39899],{},"As this blog can only give a brief motivation and introduction into the topic of times series databases, the interested\nreader who likes to get deeper insights is referred to the literature and the references therein. 😉",[994,39901,39902,39910,39918,39925],{},[997,39903,39904,39909],{},[1002,39905,39908],{"href":39906,"rel":39907},"https://web.archive.org/web/20150502150101/http://info.mapr.com:80/resources-ebook-Time-Series-Databases.html",[1006],"Time series databases","\nby Ted Dunning and Ellen Friedman",[997,39911,39912,39917],{},[1002,39913,39916],{"href":39914,"rel":39915},"http://shop.oreilly.com/product/0636920034650.do",[1006],"Practical Machine Learning: A New Look at Anomaly Detection"," by Ted\nDunning and Ellen Friedman",[997,39919,39920,39917],{},[1002,39921,39924],{"href":39922,"rel":39923},"https://www.mapr.com/practical-machine-learning",[1006],"Practical Machine Learning: Innovations in Recommendation",[997,39926,39927,39928,39932],{},"The talks by Friedman and Dunning on the topic given at ",[1002,39929,35673],{"href":39930,"rel":39931},"https://2014.nosql-matters.org/bcn/abstracts/",[1006],"\nin Barcelona",[439,39934,39935],{},"Or just download some software and try it out yourself:",[994,39937,39938,39943],{},[997,39939,39940],{},[1002,39941,39871],{"href":39869,"rel":39942},[1006],[997,39944,39945],{},[1002,39946,39949],{"href":39947,"rel":39948},"https://github.com/mapr-demos/opentsdb",[1006],"Open source MapR extensions",{"title":469,"searchDepth":507,"depth":507,"links":39951},[39952,39957,39961,39962],{"id":39747,"depth":507,"text":39748,"children":39953},[39954,39955,39956],{"id":39760,"depth":547,"text":39761},{"id":39791,"depth":547,"text":39792},{"id":39813,"depth":547,"text":39814},{"id":39844,"depth":507,"text":39845,"children":39958},[39959,39960],{"id":39862,"depth":547,"text":39863},{"id":39875,"depth":547,"text":39876},{"id":39882,"depth":507,"text":39883},{"id":39895,"depth":507,"text":39896},[1030],"2014-11-28T10:52:11","On 22 November 2014, the NoSQL matters conference took place in\\nBarcelona at the Casa Convalescència, which is doubtless one of\\nthe most beautiful locations for a conference! The Casa was declared a Historical Artistic Monument in 1978, and a World\\nCultural Heritage Site in 1997, and these great halls are a great place for great\\nspeakers.","https://synyx.de/blog/time-series-data-is-the-the-new-big-data/",{},"/blog/time-series-data-is-the-the-new-big-data",{"title":39696,"description":39970},"On 22 November 2014, the NoSQL matters conference took place in\nBarcelona at the Casa Convalescència, which is doubtless one of\nthe most beautiful locations for a conference! The Casa was declared a Historical Artistic Monument in 1978, and a World\nCultural Heritage Site in 1997, and these great halls are a great place for great\nspeakers.","blog/time-series-data-is-the-the-new-big-data",[39973,39974,39975,39976,39977],"big-data","data-science","nosql14","nosqlmatters","time-series-databases","On 22 November 2014, the NoSQL matters conference took place in Barcelona at the Casa Convalescència, which is doubtless one of the most beautiful locations for a conference! The Casa…","Ffj-9QwXmXithON6uA82S2SJEDj8V8Ca-pBbf2Jm4kc",{"id":39981,"title":39982,"author":39983,"body":39984,"category":40148,"date":40149,"description":469,"extension":1034,"link":40150,"meta":40151,"navigation":916,"path":40152,"seo":40153,"slug":39988,"stem":40154,"tags":40155,"teaser":40156,"__hash__":40157},"blog/blog/lets-add-some-value.md","Let's add some value!",[83],{"type":432,"value":39985,"toc":40144},[39986,39989,39992,40000,40020,40023,40029,40033,40036,40039,40056,40059,40064,40067,40070,40073,40096,40099,40102,40113,40122,40125,40128,40137,40140],[435,39987,39982],{"id":39988},"lets-add-some-value",[3938,39990,33735],{"id":39991},"tldr",[439,39993,39994,39995,18773],{},"Follow principles #1 and #3 from the ",[1002,39996,39999],{"href":39997,"rel":39998},"http://agilemanifesto.org/",[1006],"agile manifesto",[11947,40001,40002,40005,40008,40011,40014,40017],{},[439,40003,40004],{},"“Our highest priority is to satisfy the customer",[439,40006,40007],{},"through early and continuous delivery",[439,40009,40010],{},"of valuable software. ”",[439,40012,40013],{},"“Deliver working software frequently, from a",[439,40015,40016],{},"couple of weeks to a couple of months, with a",[439,40018,40019],{},"preference to the shorter timescale.”",[439,40021,40022],{},"Or in my own words:",[439,40024,40025,40026],{},"In each iteration ask yourself: ",[448,40027,40028],{},"While working towards a big vision can we solve at least one real problem by the end\nof this iteration?",[3938,40030,40032],{"id":40031},"part-1-the-problem-with-technical-stories","Part 1: The problem with technical stories",[439,40034,40035],{},"In Scrum we deliver a potential shippable product increment in each iteration. Following this directive has many\nadvantages for us:",[439,40037,40038],{},"We can…",[994,40040,40041,40044,40047,40050,40053],{},[997,40042,40043],{},"detect errors or misunderstandings very early through feedback from stakeholders",[997,40045,40046],{},"fail fast by discovering critical problems as early as possible",[997,40048,40049],{},"find out what users really want/need by experimenting with early versions of a feature",[997,40051,40052],{},"generate value by solving real problems with each new version of the product",[997,40054,40055],{},"change priorities based on new circumstances",[439,40057,40058],{},"Often customers have visions which result in very large user stories, also known as epics. Since we can not build the\nrequested feature within one iteration we break it down into smaller work packages. The challenge here is to find user\nstories which solve part of the problem while still making progress towards the envisioned goal.",[439,40060,40061],{},[448,40062,40063],{},"A partially finished feature has no value but a smaller feature which solves part of a problem has.",[439,40065,40066],{},"It is often easy to come to the conclusion that only the whole feature provides value because after all we can not go\nlive with a half finished feature.",[439,40068,40069],{},"So we start to create tasks to fulfil the epic:",[439,40071,40072],{},"We…",[994,40074,40075,40078,40081,40084,40087,40090,40093],{},[997,40076,40077],{},"analyse the problem",[997,40079,40080],{},"design a possible solution",[997,40082,40083],{},"evaluate technology",[997,40085,40086],{},"implement the frontend with backend mockups",[997,40088,40089],{},"implement the backend while mocking calls to 3rd party services",[997,40091,40092],{},"integrate the real 3rd party services",[997,40094,40095],{},"… and the list goes on",[439,40097,40098],{},"And there can be no objection to that! As long was we do not start to call these tasks stories, fill our backlog with\nthem and start to plan whole sprints with them. But in my experience that is exactly what happens when we are confronted\nwith complex problems. Of course we all know that a user story describes a real problem from a stakeholder which is\nsolved once the story is fulfilled and since our defined tasks do not meet this criteria we call them technical stories.\nBefore I talk about the problems of technical stories, I want to look at the advantages that we can still have if we use\nthem. Since we still work in iterations and (hopefully) deliver some sort of running software after each iteration we\ncan still inspect and adapt.",[439,40100,40101],{},"We can still…",[994,40103,40104,40107,40110],{},[997,40105,40106],{},"try out early versions of the software in an experimental environment",[997,40108,40109],{},"get feedback from stakeholders by showing them design sketches, frontend mockups or partially implemented features",[997,40111,40112],{},"fail fast if we discover that a chosen technology does not work as expected",[439,40114,40115,40116,40121],{},"That sounds good! So where are the problems? The product increment which we deliver after each iteration can not be used\nin production and therefore does not generate value until the whole (complex) feature is finished. We miss the advantage\nof a lesser ",[1002,40117,40120],{"href":40118,"rel":40119},"http://en.wikipedia.org/wiki/Time_to_market",[1006],"time to market"," (to use a management buzz word) which means we\nloose money. We also do not get feedback from real users which is often the most valuable resource to base further\ndevelopment on.",[439,40123,40124],{},"But here I want to focus on a problem which is less obvious: Generating no value early means that the product owner can\nnot change priorities based on new insights (change in the market situatíon, new requirments from stakeholders, new\nmanagement priorities). Taken to the extreme we could plan each iteration without the product owner being present until\nhis epic is completed. After all he has already told his story and the decision in which order the tasks to fulfil it\nmust be completed, belongs entirely to the team. Of course we would still work closely together and invite him (and\nstakeholders who are interested) to a review meeting after each iteration, to benefit from the advantages stated above.\nBut as long as that one large feature remains the highest priority and the whole team can work on it (i.e. the work is\nparallelisable) the product owner has not much to do.",[439,40126,40127],{},"In reality there are often small change requests or bug fixes getting higher priority which results in a delay of the\nstory. This puts the product owner into a difficult situation. While he always has to prioritize between different\nneeds (= stories) of his stakeholders he now has to decide between fast wins and his most important feature which\neveryone waits for. Real trouble awaits him if the epic that the team currently works on suddenly is not the highest\npriority anymore or for some reason takes much longer to implement than expected. Of course we can just delay new\nstories until the current feature is finished but that might be bad for the project and it is certainly not what we hope\nto achieve with agile development (being able to react to change). The option to stop development on the feature\nentirely (which a PO should always have) however would mean that all his money spent up until then is lost without any\nreturned value. That would not be the case if we had solved partial problems on the way towards the big goal. Maybe the\nfeature can be shrunk down to minimize the work needed to get at least some value before we abandon it. But if that is\npossible why did we not define this smaller goal as a partial solution from the start?",[439,40129,40130,40131,40136],{},"More often than not it is not that easy to get reasonably fast to a smaller solution once we started developing with the\nbig solution in mind. Let us assume that with some additional effort design and technology decisions can be adopted to a\nslightly changed goal, since that will always be expected from an agile development team. The structuring of the work\nmakes the real difference though. For example a big and complex feature naturally requires a longer analysis and design\nphase. So by starting with the epic as our goal we might still be working on the design tasks or an implementation task\nwhich results in a small part of the final feature, when the product owner announces the change of priorities. Had we\nstarted with a smaller goal we might already have implemented something which we can either just finish (to generate\nimmediate value) or adapt to better match the new goal of the PO. When changing priorities we must also be careful not\nto rush the development because we know that we ideally should already be working on a different story. The quality of\nthe software must always be maintained because otherwise we build\nup ",[1002,40132,40135],{"href":40133,"rel":40134},"http://en.wikipedia.org/wiki/Technical_debt",[1006],"technical debt"," which must eventually be repaid.",[439,40138,40139],{},"There are some objections that I have come across while talking with developers and product owners about why we do not\nbreak down epics into real user stories to avoid these problems. But as this post is already a bit to long I decided to\nmake a series about this topic. This way I can discuss the objections as well as possible answers more detailed in the\nupcoming parts. Until then I leave you with my conclusion up until now:",[439,40141,40142],{},[448,40143,37763],{},{"title":469,"searchDepth":507,"depth":507,"links":40145},[40146,40147],{"id":39991,"depth":507,"text":33735},{"id":40031,"depth":507,"text":40032},[1031],"2014-11-12T17:05:06","https://synyx.de/blog/lets-add-some-value/",{},"/blog/lets-add-some-value",{"title":39982,"description":469},"blog/lets-add-some-value",[9546,37776,9557],"tl;dr Follow principles #1 and #3 from the agile manifesto! “Our highest priority is to satisfy the customer through early and continuous delivery of valuable software. ” “Deliver working software…","jCMSyRgM9WyBPQ0pcFsJa4VR8ktEvJORcwRdiEVh4w0",{"id":40159,"title":40160,"author":40161,"body":40162,"category":40366,"date":40367,"description":40368,"extension":1034,"link":40369,"meta":40370,"navigation":916,"path":40371,"seo":40372,"slug":40373,"stem":40374,"tags":40375,"teaser":40378,"__hash__":40379},"blog/blog/individuelle-taschen-fur-synyx.md","Egal ob groß oder klein, unsere neuen synyx-Taschen sind grün und genial!",[172],{"type":432,"value":40163,"toc":40364},[40164,40167,40170,40180,40191,40196,40199,40205,40211,40216,40221,40226,40231,40239,40247,40252,40260,40265,40270,40275,40280,40285,40290,40295,40300,40305,40313,40316,40325,40328,40331,40336,40341,40346,40351,40356,40361],[435,40165,40160],{"id":40166},"egal-ob-groß-oder-klein-unsere-neuen-synyx-taschen-sind-grün-und-genial",[439,40168,40169],{},"Seit ein paar Wochen sind immer wieder tolle synyx-Taschen zu sehen. Ob im Web, über Twitter oder wie zuletzt auf der\nW-Jax in echt. Bis es so weit war, dass wir diese außergewöhnlichen Taschen erhalten haben, ist jedoch einige Zeit\nvergangen. Warum? Ganz einfach: Diese Taschen aus Lkw-Plane sind ganz individuell für uns entwickelt, gestaltet und\nproduziert worden. Angefangen hat es mit einfachen Zeichnungen, über den Prototypen bis hin zur ersten Tasche. Dies\nalles ist in enger Zusammenarbeit zwischen uns und der Herstellerin Lene Arrasz-Zehl, Bag&Needle, entstanden.",[439,40171,40172],{},[1002,40173,40176],{"href":40174,"rel":40175},"https://media.synyx.de/uploads//2014/11/tasche_ganz.jpg",[1006],[2205,40177],{"alt":40178,"src":40179},"tasche_ganz","https://media.synyx.de/uploads//2014/11/seite.jpg",[439,40181,40182,40185],{},[448,40183,40184],{},"Tasche von der Seite",[1002,40186,40188],{"href":40174,"rel":40187},[1006],[2205,40189],{"alt":40178,"src":40190},"https://media.synyx.de/uploads//2014/11/tasche_ganz-300x225.jpg",[439,40192,40193],{},[448,40194,40195],{},"Tasche von vorne",[439,40197,40198],{},"Ich habe mich mit Lene zusammengesetzt und mich mit ihr über die Entstehung der Taschen unterhalten:",[439,40200,40201,40204],{},[448,40202,40203],{},"Katja",": Hi Lene, schön, dass du uns ein bisschen etwas über die Taschen erzählen möchtest.",[439,40206,40207,40210],{},[448,40208,40209],{},"Lene",": Hei Katja, sehr gerne! So kann ich mal ein bisschen erklären, welche Arbeit sich hinter den fertigen\nsynyx-Taschen verbirgt.",[439,40212,40213,40215],{},[448,40214,40203],{},": Wir sind ja vor einiger Zeit auf dich zugekommen, da wir ganz individuelle Taschen für unsere Mitarbeiter\nbenötigten. Kannst du dich noch an die Anforderungen erinnern?",[439,40217,40218,40220],{},[448,40219,40209],{},": Klar, denn es sollte mein erster großer Auftrag werden. Vor allem war es neu, das die Taschen so individuell\ngewünscht wurden. Das war schon aufregend.",[439,40222,40223,40225],{},[448,40224,40203],{},": Warum war es in diesem speziellen Fall nicht möglich auf Standard-Taschen zurückzugreifen?",[439,40227,40228,40230],{},[448,40229,40209],{},": Da manche Mitarbeiter beim Kunden vor Ort arbeiten und auch mal eine Nacht im Hotel verbringen, war es wichtig\ndie Taschen so zu konzipieren, dass geschäftliche als auch private Dinge getrennt darin untergebracht werden können.",[439,40232,40233],{},[1002,40234,40236],{"href":40174,"rel":40235},[1006],[2205,40237],{"alt":40178,"src":40238},"https://media.synyx.de/uploads//2014/11/offen_seite.jpg",[439,40240,40241,40244,40246],{},[448,40242,40243],{},"Tasche offen",[448,40245,40203],{},": Was war die besondere Herausforderung für dich?",[439,40248,40249,40251],{},[448,40250,40209],{},": Genau das, wie eben beschrieben. Ich brauchte die Idee zwei Taschen in einer unterzubringen. So, dass weder\ndas Private dem Geschäftlichen im Wege ist, noch andersherum. Dazu musste ich natürlich auch neue Schablonen erstellen\nund die Suche nach einer passenden Druckerei hat ordentlich Nerven gekostet.",[439,40253,40254],{},[1002,40255,40257],{"href":40174,"rel":40256},[1006],[2205,40258],{"alt":40178,"src":40259},"https://media.synyx.de/uploads//2014/11/erweiterung_2.jpg",[439,40261,40262],{},[448,40263,40264],{},"Tasche mit geöffneter Erweiterung nach vorne",[439,40266,40267,40269],{},[448,40268,40203],{},": Warum war es so schwierig, eine passende Druckerei zu finden?",[439,40271,40272,40274],{},[448,40273,40209],{},": Lkw-Plane braucht einen speziellen Druck mit lösemittelhaltiger Farbe. Ansonsten reibt sich dieser ganz\nschnell wieder ab. Ich habe gefühlte 20 Druckereien angefragt, Proben verschickt und Absagen kassiert.Nach langem Suchen\nhabe ich dann doch noch eine Druckerei in Bremen gefunden, die meine Plane bedrucken konnte.",[439,40276,40277,40279],{},[448,40278,40203],{},": Als du damals noch die Zeichnungen erstellt hast, war dir klar, dass das ein so umfangreiches Projekt wird?",[439,40281,40282,40284],{},[448,40283,40209],{},": Nein, nicht direkt. Eine Planung auf Papier ist immer noch mal eine andere Sache, als diese dann nachher\numzusetzen. Das ‘Ausmaß’ wurde mir erst wirklich bewusst, als ich anfing nach Druckereien zu suchen und dann der\nPrototyp entstand. Nachdem der fertig war, wurden noch einige Änderungen besprochen und dementsprechend umgesetzt. Das\nwar schon ziemlich viel Arbeit.",[439,40286,40287,40289],{},[448,40288,40203],{},": Du hast dich dann mit der Zeit ‘eingearbeitet’. Wie lange hast du an einer Tasche gearbeitet?",[439,40291,40292,40294],{},[448,40293,40209],{},": Sage und schreibe 10 Stunden, ohne Denken. Also nur die Näharbeiten und der Zuschnitt. Schaut sich ein Laie\ndie fertige Tasche an, kann er sich das vielleicht nicht vorstellen und auch ich habe ein wenig geschluckt, als ich\nmeine penibel notierten Arbeitszeiten zusammenrechnete. Aber saubere Arbeit braucht eben auch Zeit. Und ‘schnell,\nschnell’ kann und will ich nicht abliefern.",[439,40296,40297,40299],{},[448,40298,40203],{},": Jeder Mitarbeiter kann sich die Tasche individuell auf seine Bedürfnisse abstimmen lassen……",[439,40301,40302,40304],{},[448,40303,40209],{},": Genau das ist wohl mein Alleinstellungsmerkmal. Eine Tasche von der Stange kann jeder herstellen. Mein Können\nliegt, neben der Näharbeit, eben auch darin, genau die Wünsche und Bedürfnisse des Kunden zu berücksichtigen und\numzusetzen. Was hilft es einem, wenn man eine coole Tasche hat, die man aber nicht voll ausnutzen kann?!",[439,40306,40307],{},[1002,40308,40311],{"href":40309,"rel":40310},"https://media.synyx.de/uploads//2014/11/innen.jpg",[1006],[2205,40312],{"alt":40178,"src":40309},[439,40314,40315],{},"**Oben: Tasche innen gefüllt",[439,40317,40318,40319],{},"Unten: Tasche innen**\n",[1002,40320,40323],{"href":40321,"rel":40322},"https://media.synyx.de/uploads//2014/11/erweiterungen.jpg",[1006],[2205,40324],{"alt":40178,"src":40321},[439,40326,40327],{},"**Oben: Tasche mit Erweiterung nach vorne",[439,40329,40330],{},"Unten: Tasche mit Erweiterung nach oben**",[439,40332,40333,40335],{},[448,40334,40203],{},": Was ist das Besondere an den Taschen?",[439,40337,40338,40340],{},[448,40339,40209],{},": Die Taschen für synyx bieten zuerst einmal sehr viel Stauraum. Man kann, neben flexibel einstellbaren\nUnterteilungen, die Taschen auch vergrößern. Nach oben, um z.B. einen Ordner zu transportieren. Wenn man einen teilbaren\nReißverschluss vorne ringsherum der Tasche, aufmacht, entsteht noch einmal so viel Raum, für z.B. Privates. Braucht man\ndiesen nicht, macht man den Reißverschluss einfach wieder zu und alles ist kompakt wie vorher. So kann man den\nTascheninhalt geordnet voneinander trennen. Besonders ist auch der Taschendeckel, in den man durch eine aufgesetzte\nKlarsichtfolie z.B. Werbezettel o.ä. einschieben kann. Am besten guckt man sich so eine Tasche real mal an. Es gibt zu\nviele Dinge, die cool, entlastend, schlau konzipiert sind. Schwer, alles aufzuzählen.",[439,40342,40343,40345],{},[448,40344,40203],{},": Würdest du so ein Projekt noch einmal in Angriff nehmen?",[439,40347,40348,40350],{},[448,40349,40209],{},": Ganz bestimmt. Sicherlich würde ich beim nächsten Mal viel sicherer und gezielter mit den Arbeitsschritten\numgehen können. Ich habe bei dem Projekt wahnsinnig viel gelernt und bin wirklich froh, diese Erfahrungen gemacht zu\nhaben. Außerdem macht es einfach Spaß dem Kunden exakt das bieten zu können, was er will bzw. braucht.",[439,40352,40353,40355],{},[448,40354,40203],{},": Danke für das Interview.",[439,40357,40358,40360],{},[448,40359,40209],{},": Sehr gern!",[439,40362,40363],{},"Wir sind auf jeden Fall sehr happy mit den neuen synyx-Taschen.",{"title":469,"searchDepth":507,"depth":507,"links":40365},[],[1031],"2014-11-12T15:27:44","Seit ein paar Wochen sind immer wieder tolle synyx-Taschen zu sehen. Ob im Web, über Twitter oder wie zuletzt auf der\\nW-Jax in echt. Bis es so weit war, dass wir diese außergewöhnlichen Taschen erhalten haben, ist jedoch einige Zeit\\nvergangen. Warum? Ganz einfach: Diese Taschen aus Lkw-Plane sind ganz individuell für uns entwickelt, gestaltet und\\nproduziert worden. Angefangen hat es mit einfachen Zeichnungen, über den Prototypen bis hin zur ersten Tasche. Dies\\nalles ist in enger Zusammenarbeit zwischen uns und der Herstellerin Lene Arrasz-Zehl, Bag&Needle, entstanden.","https://synyx.de/blog/individuelle-taschen-fur-synyx/",{},"/blog/individuelle-taschen-fur-synyx",{"title":40160,"description":40169},"individuelle-taschen-fur-synyx","blog/individuelle-taschen-fur-synyx",[40376,18912,389,40377],"lwk-plane","werbemittel","Seit ein paar Wochen sind immer wieder tolle synyx-Taschen zu sehen. Ob im Web, über Twitter oder wie zuletzt auf der W-Jax in echt. Bis es so weit war, dass…","dT9XOBfa4UteWNVh5MouCYVh4dn8zq95AL7NJCp_j10",{"id":40381,"title":40382,"author":40383,"body":40384,"category":41542,"date":41543,"description":41544,"extension":1034,"link":41545,"meta":41546,"navigation":916,"path":41547,"seo":41548,"slug":40388,"stem":41550,"tags":41551,"teaser":41552,"__hash__":41553},"blog/blog/installing-xenserver-and-virtual-hosts-in-a-routed-network.md","Installing XenServer and Virtual Hosts in a Routed Network",[178],{"type":432,"value":40385,"toc":41540},[40386,40389,40398,40403,40411,40416,40419,40424,40433,40438,40441,40444,40473,40476,40484,40487,40492,40504,40530,40533,40654,40659,40666,40723,40728,40731,40895,40900,40903,41295,41300,41303,41306,41448,41453,41456,41473,41476,41493,41496,41500,41503,41508,41514,41520,41526,41532,41538],[435,40387,40382],{"id":40388},"installing-xenserver-and-virtual-hosts-in-a-routed-network",[439,40390,40391,40392,40397],{},"A few days ago, we had to set up a XenServer Host, running on one of ",[1002,40393,40396],{"href":40394,"rel":40395},"http://hetzner.de",[1006],"Hetzners"," dedicated servers.\nThere are plenty of howto’s around, but since we happened to run into a few problems with routed networking and Domain\nName Resolution during installation, that weren’t documented in Hetzners Wiki, i decided to provide a blog post for\nself-reference and as a way to contribute a (hopefully helpful) basic guide on how to install XenServer on a dedicated\nserver – in our Case a Hetzner Root Server.",[439,40399,40400],{},[448,40401,40402],{},"References:",[994,40404,40405,40408],{},[997,40406,40407],{},"When i talk about the host system or simply the host, i am referring to the dedicated server itself.",[997,40409,40410],{},"When i talk about the guest system, the guest or simply the VM, i refer to the virtual machines running on the Host\nsystem.",[439,40412,40413],{},[448,40414,40415],{},"IP-Addresses and Networks",[439,40417,40418],{},"By default, Hetzners servers come with a single IP-address. Make sure to write down your network configuration from\nyour originally installed base system (or boot up your dedicated server into a rescue system). You need that information\nduring the installation of your base system! Also, when you plan on running externally accessible guest systems on your\nXenServer, you need to order additional IPs (you can either order a single IP or order a whole subnet with Hetzner).\nKeep in mind that Hetzners network setup delivers traffic only to the mac-address of the physical interface, so we have\nto do some trickery to set up the network for our VMs later on.",[439,40420,40421],{},[448,40422,40423],{},"Downloading the installation image",[439,40425,40426,40427,40432],{},"First of all, download the current XenServer installation\nimage: ",[1002,40428,40431],{"href":40429,"rel":40430},"https://web.archive.org/web/20181026000632/https://xenserver.org/open-source-virtualization-download.html",[1006],"http://xenserver.org/open-source-virtualization-download.html",".\nWe decided to install our host system, using LARA (Hetzners remote console, providing KVM-over-IP). To mount the ISO\ninstallation image in LARA, click on the “Interfaces” tab in LARAs login page, then go to the “Virtual Media” link. Here\nyou can enter the details of how you want to mount your image:",[439,40434,40435],{},[448,40436,40437],{},"Mounting the ISO file and installing the base system",[439,40439,40440],{},"To access the virtual media interface please click on the “Interfaces” tab and then go to the “Virtual Media” link. Here\nyou can enter the details of the location of the ISO file: You can mount it either via the SAMBA/CIFS networking\nprotocol from your backup space or from your own server (as long as it is reachable via SAMBA/CIFS), or mount it\ndirectly in your LARA (although this isn’t officially supported by Hetzner).",[439,40442,40443],{},"Mount your ISO via SAMBA/CIFS:",[11947,40445,40446,40449,40452,40455,40458,40461,40464,40467,40470],{},[439,40447,40448],{},"Under Interfaces > Virtual Media > Choose “Image on Windows Share”:",[439,40450,40451],{},"Share Host/IP: \u003Cusername>.your-backup.de",[439,40453,40454],{},"Share Name: backup",[439,40456,40457],{},"Image File with Path: \u003Cfile_name>",[439,40459,40460],{},"User Name: \u003Cusername>",[439,40462,40463],{},"Password: \u003Cpassword>",[439,40465,40466],{},"\u003Cuser name> is the username of your backup space, which you can found under “Backup” in Hetzners\nrobot.your-server.de",[439,40468,40469],{},"\u003Cfile_name> is the name of your ISO installation image",[439,40471,40472],{},"\u003Cpassword> is the password that you have setup in your backup space.",[439,40474,40475],{},"Mount your ISO directly in LARA:",[11947,40477,40478,40481],{},[439,40479,40480],{},"In your LARA, click on the floppy symbol on the top right and navigate to your local ISO installation image.",[439,40482,40483],{},"You can verify under Interfaces > Virtual Media > “Virtual Media Active Image” if your ISO is correctly mounted.",[439,40485,40486],{},"After rebooting your server, you should see the installation wizard coming up. It’s pretty easy from here, just follow\nthrough the the installation process, no biggies here…",[439,40488,40489],{},[448,40490,40491],{},"Checking your raid configuration",[439,40493,40494,40495,40498,40499,40503],{},"To monitor your LSI raid controller with ",[448,40496,40497],{},"megactl",", install MegaCLI RPM (available\nfrom ",[1002,40500,40501],{"href":40501,"rel":40502},"http://download.hetzner.de/tools/LSI/",[1006]," and other sources) on your base system:",[11947,40505,40506],{},[464,40507,40509],{"className":16895,"code":40508,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# unzip 8.07.14_MegaCLI.zip\n[root@XenServer-Host ~]# rpm -ivh Linux/MegaCli-8.07.14-1.noarch.rpm\n...\n[root@XenServer-Host ~]#\n\n",[471,40510,40511,40516,40521,40525],{"__ignoreMap":469},[474,40512,40513],{"class":476,"line":477},[474,40514,40515],{},"[root@XenServer-Host ~]# unzip 8.07.14_MegaCLI.zip\n",[474,40517,40518],{"class":476,"line":507},[474,40519,40520],{},"[root@XenServer-Host ~]# rpm -ivh Linux/MegaCli-8.07.14-1.noarch.rpm\n",[474,40522,40523],{"class":476,"line":547},[474,40524,14198],{},[474,40526,40527],{"class":476,"line":584},[474,40528,40529],{},"[root@XenServer-Host ~]#\n",[439,40531,40532],{},"Verify the installation by checking your raid status:",[11947,40534,40535],{},[464,40536,40538],{"className":16895,"code":40537,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# /opt/MegaRAID/MegaCli/MegaCli LDInfo -Lall -Aall\nAdapter 0 -- Virtual Drive Information:\nVirtual Drive: 0 (Target Id: 0)\nName :\nRAID Level : Primary-1, Secondary-0, RAID Level Qualifier-0\nSize : 1.818 TB\nSector Size : 512\nIs VD emulated : No\nMirror Data : 1.818 TB\nState : Optimal\nStrip Size : 256 KB\nNumber Of Drives : 2\nSpan Depth : 1\nDefault Cache Policy: WriteBack, ReadAhead, Cached, Write Cache OK if Bad BBU\nCurrent Cache Policy: WriteBack, ReadAhead, Cached, Write Cache OK if Bad BBU\nDefault Access Policy: Read/Write\nCurrent Access Policy: Read/Write\nDisk Cache Policy : Disk's Default\nEncryption Type : None\nBad Blocks Exist: No\nIs VD Cached: No\nExit Code: 0x00\n[root@XenServer-Host ~]#\n",[471,40539,40540,40545,40550,40555,40560,40565,40570,40575,40580,40585,40590,40595,40600,40605,40610,40615,40620,40625,40630,40635,40640,40645,40650],{"__ignoreMap":469},[474,40541,40542],{"class":476,"line":477},[474,40543,40544],{},"[root@XenServer-Host ~]# /opt/MegaRAID/MegaCli/MegaCli LDInfo -Lall -Aall\n",[474,40546,40547],{"class":476,"line":507},[474,40548,40549],{},"Adapter 0 -- Virtual Drive Information:\n",[474,40551,40552],{"class":476,"line":547},[474,40553,40554],{},"Virtual Drive: 0 (Target Id: 0)\n",[474,40556,40557],{"class":476,"line":584},[474,40558,40559],{},"Name :\n",[474,40561,40562],{"class":476,"line":607},[474,40563,40564],{},"RAID Level : Primary-1, Secondary-0, RAID Level Qualifier-0\n",[474,40566,40567],{"class":476,"line":642},[474,40568,40569],{},"Size : 1.818 TB\n",[474,40571,40572],{"class":476,"line":663},[474,40573,40574],{},"Sector Size : 512\n",[474,40576,40577],{"class":476,"line":694},[474,40578,40579],{},"Is VD emulated : No\n",[474,40581,40582],{"class":476,"line":700},[474,40583,40584],{},"Mirror Data : 1.818 TB\n",[474,40586,40587],{"class":476,"line":913},[474,40588,40589],{},"State : Optimal\n",[474,40591,40592],{"class":476,"line":920},[474,40593,40594],{},"Strip Size : 256 KB\n",[474,40596,40597],{"class":476,"line":926},[474,40598,40599],{},"Number Of Drives : 2\n",[474,40601,40602],{"class":476,"line":932},[474,40603,40604],{},"Span Depth : 1\n",[474,40606,40607],{"class":476,"line":938},[474,40608,40609],{},"Default Cache Policy: WriteBack, ReadAhead, Cached, Write Cache OK if Bad BBU\n",[474,40611,40612],{"class":476,"line":944},[474,40613,40614],{},"Current Cache Policy: WriteBack, ReadAhead, Cached, Write Cache OK if Bad BBU\n",[474,40616,40617],{"class":476,"line":950},[474,40618,40619],{},"Default Access Policy: Read/Write\n",[474,40621,40622],{"class":476,"line":956},[474,40623,40624],{},"Current Access Policy: Read/Write\n",[474,40626,40627],{"class":476,"line":962},[474,40628,40629],{},"Disk Cache Policy : Disk's Default\n",[474,40631,40632],{"class":476,"line":4876},[474,40633,40634],{},"Encryption Type : None\n",[474,40636,40637],{"class":476,"line":4888},[474,40638,40639],{},"Bad Blocks Exist: No\n",[474,40641,40642],{"class":476,"line":4900},[474,40643,40644],{},"Is VD Cached: No\n",[474,40646,40647],{"class":476,"line":4913},[474,40648,40649],{},"Exit Code: 0x00\n",[474,40651,40652],{"class":476,"line":4921},[474,40653,40529],{},[439,40655,40656],{},[448,40657,40658],{},"Updating the base system",[439,40660,40661,40662,40665],{},"Updates are available\nfrom ",[1002,40663,40431],{"href":40429,"rel":40664},[1006],".\nCitrix provides an official YUM repo, but for some reason it’s empty, so that leaves us with installing updates\nmanually. Make sure to install all updates in the right order and reboot your host system when necessary!",[11947,40667,40668],{},[464,40669,40671],{"className":16895,"code":40670,"language":16897,"meta":469,"style":469},"For example:\n[root@XenServer-Host ~]# mkdir XS62ESP1014\n[root@XenServer-Host ~]# cd XS62ESP1014\n[root@XenServer-Host ~]# wget http://downloadns.citrix.com.edgesuite.net/akdlm/9708/XS62ESP1014.zip\n[root@XenServer-Host ~]# unzip XS62ESP1014.zip\n[root@XenServer-Hoste ~]# xe patch-upload file-name=XS62ESP1014.xsupdate\n[root@XenServer-Host ~]# xe patch-apply uuid=4fc82e62-b938-407d-a2c6-68c8922f3ec2 host-uuid=099a87af-0c80-4ea2-a406-f8a1bdf1e2a1\n[root@XenServer-Host ~]# cd ..\n[root@XenServer-Host ~]# rm -Rf XS62ESP1014\n[root@XenServer-Host ~]# reboot\n",[471,40672,40673,40678,40683,40688,40693,40698,40703,40708,40713,40718],{"__ignoreMap":469},[474,40674,40675],{"class":476,"line":477},[474,40676,40677],{},"For example:\n",[474,40679,40680],{"class":476,"line":507},[474,40681,40682],{},"[root@XenServer-Host ~]# mkdir XS62ESP1014\n",[474,40684,40685],{"class":476,"line":547},[474,40686,40687],{},"[root@XenServer-Host ~]# cd XS62ESP1014\n",[474,40689,40690],{"class":476,"line":584},[474,40691,40692],{},"[root@XenServer-Host ~]# wget http://downloadns.citrix.com.edgesuite.net/akdlm/9708/XS62ESP1014.zip\n",[474,40694,40695],{"class":476,"line":607},[474,40696,40697],{},"[root@XenServer-Host ~]# unzip XS62ESP1014.zip\n",[474,40699,40700],{"class":476,"line":642},[474,40701,40702],{},"[root@XenServer-Hoste ~]# xe patch-upload file-name=XS62ESP1014.xsupdate\n",[474,40704,40705],{"class":476,"line":663},[474,40706,40707],{},"[root@XenServer-Host ~]# xe patch-apply uuid=4fc82e62-b938-407d-a2c6-68c8922f3ec2 host-uuid=099a87af-0c80-4ea2-a406-f8a1bdf1e2a1\n",[474,40709,40710],{"class":476,"line":694},[474,40711,40712],{},"[root@XenServer-Host ~]# cd ..\n",[474,40714,40715],{"class":476,"line":700},[474,40716,40717],{},"[root@XenServer-Host ~]# rm -Rf XS62ESP1014\n",[474,40719,40720],{"class":476,"line":913},[474,40721,40722],{},"[root@XenServer-Host ~]# reboot\n",[439,40724,40725],{},[448,40726,40727],{},"Configuring a network interface for your guest systems",[439,40729,40730],{},"We ordered a dedicated Subnet for our guest systems, and added the first IP as an alias device to xenbr0:",[11947,40732,40733],{},[464,40734,40736],{"className":16895,"code":40735,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# cat /etc/sysconfig/network-scripts/ifcfg-xenbr0:1\nDEVICE=xenbr0:1\nONBOOT=yes\nBOOTPROTO=none\nNETMASK=\u003Cnetmask>\nIPADDR=\u003Cfirst_subnet_ip>\n[root@XenServer-Host ~]# ifup xenbr0:1\n[root@XenServer-Host ~]# ifconfig\neth0 Link encap:Ethernet HWaddr \u003Chwaddress>\n UP BROADCAST RUNNING PROMISC MTU:1500 Metric:1\n RX packets:38850 errors:0 dropped:0 overruns:0 frame:0\n TX packets:26088 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:1000\n RX bytes:4238740 (4.0 MiB) TX bytes:3740798 (3.5 MiB)\nlo Link encap:Local Loopback\n inet addr:127.0.0.1 Mask:255.0.0.0\n UP LOOPBACK RUNNING MTU:16436 Metric:1\n RX packets:2083 errors:0 dropped:0 overruns:0 frame:0\n TX packets:2083 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:0\n RX bytes:1065579 (1.0 MiB) TX bytes:1065579 (1.0 MiB)\nxenbr0 Link encap:Ethernet HWaddr \u003Chwaddress>\n inet addr:\u003Chost_ip_address> Bcast:\u003Cbroadcast> Mask:\u003Cnetmask>\n UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1\n RX packets:45603 errors:0 dropped:0 overruns:0 frame:0\n TX packets:31564 errors:0 dropped:0 overruns:0 carrier:0\n collisions:0 txqueuelen:0\n RX bytes:5305346 (5.0 MiB) TX bytes:4642674 (4.4 MiB)\nxenbr0:1 Link encap:Ethernet HWaddr \u003Chwaddress>\n inet addr:\u003Cfirst_subnet_ip> Bcast:\u003Cbroadcast> Mask:\u003Cnetmask>\n UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1\n[root@XenServer-Host ~]#\n",[471,40737,40738,40743,40748,40753,40758,40763,40768,40773,40778,40783,40788,40793,40798,40803,40808,40813,40818,40823,40828,40833,40838,40843,40848,40853,40858,40863,40868,40872,40877,40882,40887,40891],{"__ignoreMap":469},[474,40739,40740],{"class":476,"line":477},[474,40741,40742],{},"[root@XenServer-Host ~]# cat /etc/sysconfig/network-scripts/ifcfg-xenbr0:1\n",[474,40744,40745],{"class":476,"line":507},[474,40746,40747],{},"DEVICE=xenbr0:1\n",[474,40749,40750],{"class":476,"line":547},[474,40751,40752],{},"ONBOOT=yes\n",[474,40754,40755],{"class":476,"line":584},[474,40756,40757],{},"BOOTPROTO=none\n",[474,40759,40760],{"class":476,"line":607},[474,40761,40762],{},"NETMASK=\u003Cnetmask>\n",[474,40764,40765],{"class":476,"line":642},[474,40766,40767],{},"IPADDR=\u003Cfirst_subnet_ip>\n",[474,40769,40770],{"class":476,"line":663},[474,40771,40772],{},"[root@XenServer-Host ~]# ifup xenbr0:1\n",[474,40774,40775],{"class":476,"line":694},[474,40776,40777],{},"[root@XenServer-Host ~]# ifconfig\n",[474,40779,40780],{"class":476,"line":700},[474,40781,40782],{},"eth0 Link encap:Ethernet HWaddr \u003Chwaddress>\n",[474,40784,40785],{"class":476,"line":913},[474,40786,40787],{}," UP BROADCAST RUNNING PROMISC MTU:1500 Metric:1\n",[474,40789,40790],{"class":476,"line":920},[474,40791,40792],{}," RX packets:38850 errors:0 dropped:0 overruns:0 frame:0\n",[474,40794,40795],{"class":476,"line":926},[474,40796,40797],{}," TX packets:26088 errors:0 dropped:0 overruns:0 carrier:0\n",[474,40799,40800],{"class":476,"line":932},[474,40801,40802],{}," collisions:0 txqueuelen:1000\n",[474,40804,40805],{"class":476,"line":938},[474,40806,40807],{}," RX bytes:4238740 (4.0 MiB) TX bytes:3740798 (3.5 MiB)\n",[474,40809,40810],{"class":476,"line":944},[474,40811,40812],{},"lo Link encap:Local Loopback\n",[474,40814,40815],{"class":476,"line":950},[474,40816,40817],{}," inet addr:127.0.0.1 Mask:255.0.0.0\n",[474,40819,40820],{"class":476,"line":956},[474,40821,40822],{}," UP LOOPBACK RUNNING MTU:16436 Metric:1\n",[474,40824,40825],{"class":476,"line":962},[474,40826,40827],{}," RX packets:2083 errors:0 dropped:0 overruns:0 frame:0\n",[474,40829,40830],{"class":476,"line":4876},[474,40831,40832],{}," TX packets:2083 errors:0 dropped:0 overruns:0 carrier:0\n",[474,40834,40835],{"class":476,"line":4888},[474,40836,40837],{}," collisions:0 txqueuelen:0\n",[474,40839,40840],{"class":476,"line":4900},[474,40841,40842],{}," RX bytes:1065579 (1.0 MiB) TX bytes:1065579 (1.0 MiB)\n",[474,40844,40845],{"class":476,"line":4913},[474,40846,40847],{},"xenbr0 Link encap:Ethernet HWaddr \u003Chwaddress>\n",[474,40849,40850],{"class":476,"line":4921},[474,40851,40852],{}," inet addr:\u003Chost_ip_address> Bcast:\u003Cbroadcast> Mask:\u003Cnetmask>\n",[474,40854,40855],{"class":476,"line":4932},[474,40856,40857],{}," UP BROADCAST RUNNING PROMISC MULTICAST MTU:1500 Metric:1\n",[474,40859,40860],{"class":476,"line":4938},[474,40861,40862],{}," RX packets:45603 errors:0 dropped:0 overruns:0 frame:0\n",[474,40864,40865],{"class":476,"line":4946},[474,40866,40867],{}," TX packets:31564 errors:0 dropped:0 overruns:0 carrier:0\n",[474,40869,40870],{"class":476,"line":4952},[474,40871,40837],{},[474,40873,40874],{"class":476,"line":4957},[474,40875,40876],{}," RX bytes:5305346 (5.0 MiB) TX bytes:4642674 (4.4 MiB)\n",[474,40878,40879],{"class":476,"line":4969},[474,40880,40881],{},"xenbr0:1 Link encap:Ethernet HWaddr \u003Chwaddress>\n",[474,40883,40884],{"class":476,"line":4990},[474,40885,40886],{}," inet addr:\u003Cfirst_subnet_ip> Bcast:\u003Cbroadcast> Mask:\u003Cnetmask>\n",[474,40888,40889],{"class":476,"line":5001},[474,40890,40857],{},[474,40892,40893],{"class":476,"line":5013},[474,40894,40529],{},[439,40896,40897],{},[448,40898,40899],{},"Activate package forwarding",[439,40901,40902],{},"With routed setups (like with Hetzner, as mentioned earlier in this post), we need to set up package forwarding!",[11947,40904,40905],{},[464,40906,40908],{"className":16895,"code":40907,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# cat /etc/sysctl.conf\n# Kernel sysctl configuration file for Red Hat Linux\n#\n# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and\n# sysctl.conf(5) for more details.\n# Controls IP packet forwarding\nnet.ipv4.ip_forward = 1\n#net.ipv6.conf.all.forwarding=1\n# Controls proxy arp\nnet.ipv4.conf.default.proxy_arp = 0\n# Turn off redirects\nnet.ipv4.conf.all.send_redirects = 0\nnet.ipv4.conf.default.send_redirects = 0\nnet.ipv4.conf.lo.send_redirects = 0\nnet.ipv4.conf.xenbr0.send_redirects = 0\n# Controls source route verification\nnet.ipv4.conf.default.rp_filter = 0\n# Enable broadcast echo Protection\nnet.ipv4.icmp_echo_ignore_broadcasts = 1\n# Do not accept source routing\nnet.ipv4.conf.default.accept_source_route = 0\n# Disable ICMP Redirect Acceptance\nnet.ipv4.conf.all.accept_redirects = 0\n# Don.t send Redirect Messages\nnet.ipv4.conf.all.send_redirects = 0\n# ARP replies only on single interface with corresponding IP address\nnet.ipv4.conf.all.arp_filter = 1\n# Disable *tables rules for bridge traffic to increase performance\nnet.bridge.bridge-nf-call-iptables = 0\nnet.bridge.bridge-nf-call-ip6tables = 0\nnet.bridge.bridge-nf-call-arptables = 0\n# Controls the System Request debugging functionality of the kernel\nkernel.sysrq = 1\n# Controls whether core dumps will append the PID to the core filename\n# Useful for debugging multi-threaded applications\nkernel.core_uses_pid = 1\n# Controls the use of TCP syncookies\nnet.ipv4.tcp_syncookies = 1\n# Controls the maximum size of a message, in bytes\nkernel.msgmnb = 65536\n# Controls the default maxmimum size of a mesage queue\nkernel.msgmax = 65536\n# Controls the maximum shared segment size, in bytes\nkernel.shmmax = 4294967295\n# Controls the maximum number of shared memory segments, in pages\nkernel.shmall = 268435456\n# Controls the percentage of system memory reserved for dirty pages\nvm.dirty_ratio = 5\n# Mimimum priority of kernel message to log\nkernel.printk = 4 4 1 4\n# Maximum io_events: 2048 tapdisks, 32k headroom\nfs.aio-max-nr = 1048576\n# Panic on soft lockups.\nkernel.softlockup_panic = 1\n[root@XenServer-Host ~]# sysctl -p\nnet.ipv4.ip_forward = 1\nnet.ipv4.conf.default.proxy_arp = 0\nnet.ipv4.conf.all.send_redirects = 0\nnet.ipv4.conf.default.send_redirects = 0\nnet.ipv4.conf.lo.send_redirects = 0\nnet.ipv4.conf.xenbr0.send_redirects = 0\nnet.ipv4.conf.default.rp_filter = 0\nnet.ipv4.icmp_echo_ignore_broadcasts = 1\nnet.ipv4.conf.default.accept_source_route = 0\nnet.ipv4.conf.all.accept_redirects = 0\nnet.ipv4.conf.all.send_redirects = 0\nnet.ipv4.conf.all.arp_filter = 1\nerror: \"net.bridge.bridge-nf-call-iptables\" is an unknown key\nerror: \"net.bridge.bridge-nf-call-ip6tables\" is an unknown key\nerror: \"net.bridge.bridge-nf-call-arptables\" is an unknown key\nkernel.sysrq = 1\nkernel.core_uses_pid = 1\nnet.ipv4.tcp_syncookies = 1\nkernel.msgmnb = 65536\nkernel.msgmax = 65536\nkernel.shmmax = 4294967295\nkernel.shmall = 268435456\nvm.dirty_ratio = 5\nkernel.printk = 4 4 1 4\nfs.aio-max-nr = 1048576\nkernel.softlockup_panic = 1\n[root@XenServer-Host ~]#\n",[471,40909,40910,40915,40920,40925,40930,40935,40940,40945,40950,40955,40960,40965,40970,40975,40980,40985,40990,40995,41000,41005,41010,41015,41020,41025,41030,41034,41039,41044,41049,41054,41059,41064,41069,41074,41079,41084,41089,41094,41099,41104,41109,41114,41119,41124,41129,41134,41139,41144,41149,41154,41159,41164,41169,41174,41179,41184,41188,41192,41196,41200,41204,41208,41212,41216,41220,41224,41228,41232,41237,41242,41247,41251,41255,41259,41263,41267,41271,41275,41279,41283,41287,41291],{"__ignoreMap":469},[474,40911,40912],{"class":476,"line":477},[474,40913,40914],{},"[root@XenServer-Host ~]# cat /etc/sysctl.conf\n",[474,40916,40917],{"class":476,"line":507},[474,40918,40919],{},"# Kernel sysctl configuration file for Red Hat Linux\n",[474,40921,40922],{"class":476,"line":547},[474,40923,40924],{},"#\n",[474,40926,40927],{"class":476,"line":584},[474,40928,40929],{},"# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and\n",[474,40931,40932],{"class":476,"line":607},[474,40933,40934],{},"# sysctl.conf(5) for more details.\n",[474,40936,40937],{"class":476,"line":642},[474,40938,40939],{},"# Controls IP packet forwarding\n",[474,40941,40942],{"class":476,"line":663},[474,40943,40944],{},"net.ipv4.ip_forward = 1\n",[474,40946,40947],{"class":476,"line":694},[474,40948,40949],{},"#net.ipv6.conf.all.forwarding=1\n",[474,40951,40952],{"class":476,"line":700},[474,40953,40954],{},"# Controls proxy arp\n",[474,40956,40957],{"class":476,"line":913},[474,40958,40959],{},"net.ipv4.conf.default.proxy_arp = 0\n",[474,40961,40962],{"class":476,"line":920},[474,40963,40964],{},"# Turn off redirects\n",[474,40966,40967],{"class":476,"line":926},[474,40968,40969],{},"net.ipv4.conf.all.send_redirects = 0\n",[474,40971,40972],{"class":476,"line":932},[474,40973,40974],{},"net.ipv4.conf.default.send_redirects = 0\n",[474,40976,40977],{"class":476,"line":938},[474,40978,40979],{},"net.ipv4.conf.lo.send_redirects = 0\n",[474,40981,40982],{"class":476,"line":944},[474,40983,40984],{},"net.ipv4.conf.xenbr0.send_redirects = 0\n",[474,40986,40987],{"class":476,"line":950},[474,40988,40989],{},"# Controls source route verification\n",[474,40991,40992],{"class":476,"line":956},[474,40993,40994],{},"net.ipv4.conf.default.rp_filter = 0\n",[474,40996,40997],{"class":476,"line":962},[474,40998,40999],{},"# Enable broadcast echo Protection\n",[474,41001,41002],{"class":476,"line":4876},[474,41003,41004],{},"net.ipv4.icmp_echo_ignore_broadcasts = 1\n",[474,41006,41007],{"class":476,"line":4888},[474,41008,41009],{},"# Do not accept source routing\n",[474,41011,41012],{"class":476,"line":4900},[474,41013,41014],{},"net.ipv4.conf.default.accept_source_route = 0\n",[474,41016,41017],{"class":476,"line":4913},[474,41018,41019],{},"# Disable ICMP Redirect Acceptance\n",[474,41021,41022],{"class":476,"line":4921},[474,41023,41024],{},"net.ipv4.conf.all.accept_redirects = 0\n",[474,41026,41027],{"class":476,"line":4932},[474,41028,41029],{},"# Don.t send Redirect Messages\n",[474,41031,41032],{"class":476,"line":4938},[474,41033,40969],{},[474,41035,41036],{"class":476,"line":4946},[474,41037,41038],{},"# ARP replies only on single interface with corresponding IP address\n",[474,41040,41041],{"class":476,"line":4952},[474,41042,41043],{},"net.ipv4.conf.all.arp_filter = 1\n",[474,41045,41046],{"class":476,"line":4957},[474,41047,41048],{},"# Disable *tables rules for bridge traffic to increase performance\n",[474,41050,41051],{"class":476,"line":4969},[474,41052,41053],{},"net.bridge.bridge-nf-call-iptables = 0\n",[474,41055,41056],{"class":476,"line":4990},[474,41057,41058],{},"net.bridge.bridge-nf-call-ip6tables = 0\n",[474,41060,41061],{"class":476,"line":5001},[474,41062,41063],{},"net.bridge.bridge-nf-call-arptables = 0\n",[474,41065,41066],{"class":476,"line":5013},[474,41067,41068],{},"# Controls the System Request debugging functionality of the kernel\n",[474,41070,41071],{"class":476,"line":5024},[474,41072,41073],{},"kernel.sysrq = 1\n",[474,41075,41076],{"class":476,"line":5035},[474,41077,41078],{},"# Controls whether core dumps will append the PID to the core filename\n",[474,41080,41081],{"class":476,"line":5047},[474,41082,41083],{},"# Useful for debugging multi-threaded applications\n",[474,41085,41086],{"class":476,"line":5055},[474,41087,41088],{},"kernel.core_uses_pid = 1\n",[474,41090,41091],{"class":476,"line":5062},[474,41092,41093],{},"# Controls the use of TCP syncookies\n",[474,41095,41096],{"class":476,"line":5067},[474,41097,41098],{},"net.ipv4.tcp_syncookies = 1\n",[474,41100,41101],{"class":476,"line":5072},[474,41102,41103],{},"# Controls the maximum size of a message, in bytes\n",[474,41105,41106],{"class":476,"line":5084},[474,41107,41108],{},"kernel.msgmnb = 65536\n",[474,41110,41111],{"class":476,"line":5100},[474,41112,41113],{},"# Controls the default maxmimum size of a mesage queue\n",[474,41115,41116],{"class":476,"line":5111},[474,41117,41118],{},"kernel.msgmax = 65536\n",[474,41120,41121],{"class":476,"line":5122},[474,41122,41123],{},"# Controls the maximum shared segment size, in bytes\n",[474,41125,41126],{"class":476,"line":5134},[474,41127,41128],{},"kernel.shmmax = 4294967295\n",[474,41130,41131],{"class":476,"line":5145},[474,41132,41133],{},"# Controls the maximum number of shared memory segments, in pages\n",[474,41135,41136],{"class":476,"line":5156},[474,41137,41138],{},"kernel.shmall = 268435456\n",[474,41140,41141],{"class":476,"line":5163},[474,41142,41143],{},"# Controls the percentage of system memory reserved for dirty pages\n",[474,41145,41146],{"class":476,"line":5170},[474,41147,41148],{},"vm.dirty_ratio = 5\n",[474,41150,41151],{"class":476,"line":5175},[474,41152,41153],{},"# Mimimum priority of kernel message to log\n",[474,41155,41156],{"class":476,"line":5180},[474,41157,41158],{},"kernel.printk = 4 4 1 4\n",[474,41160,41161],{"class":476,"line":5192},[474,41162,41163],{},"# Maximum io_events: 2048 tapdisks, 32k headroom\n",[474,41165,41166],{"class":476,"line":5217},[474,41167,41168],{},"fs.aio-max-nr = 1048576\n",[474,41170,41171],{"class":476,"line":5229},[474,41172,41173],{},"# Panic on soft lockups.\n",[474,41175,41176],{"class":476,"line":5240},[474,41177,41178],{},"kernel.softlockup_panic = 1\n",[474,41180,41181],{"class":476,"line":5251},[474,41182,41183],{},"[root@XenServer-Host ~]# sysctl -p\n",[474,41185,41186],{"class":476,"line":5262},[474,41187,40944],{},[474,41189,41190],{"class":476,"line":5274},[474,41191,40959],{},[474,41193,41194],{"class":476,"line":5281},[474,41195,40969],{},[474,41197,41198],{"class":476,"line":5294},[474,41199,40974],{},[474,41201,41202],{"class":476,"line":5307},[474,41203,40979],{},[474,41205,41206],{"class":476,"line":5318},[474,41207,40984],{},[474,41209,41210],{"class":476,"line":5323},[474,41211,40994],{},[474,41213,41214],{"class":476,"line":5330},[474,41215,41004],{},[474,41217,41218],{"class":476,"line":5335},[474,41219,41014],{},[474,41221,41222],{"class":476,"line":5340},[474,41223,41024],{},[474,41225,41226],{"class":476,"line":5352},[474,41227,40969],{},[474,41229,41230],{"class":476,"line":5376},[474,41231,41043],{},[474,41233,41234],{"class":476,"line":5387},[474,41235,41236],{},"error: \"net.bridge.bridge-nf-call-iptables\" is an unknown key\n",[474,41238,41239],{"class":476,"line":5398},[474,41240,41241],{},"error: \"net.bridge.bridge-nf-call-ip6tables\" is an unknown key\n",[474,41243,41244],{"class":476,"line":5409},[474,41245,41246],{},"error: \"net.bridge.bridge-nf-call-arptables\" is an unknown key\n",[474,41248,41249],{"class":476,"line":5420},[474,41250,41073],{},[474,41252,41253],{"class":476,"line":5432},[474,41254,41088],{},[474,41256,41257],{"class":476,"line":5439},[474,41258,41098],{},[474,41260,41261],{"class":476,"line":5450},[474,41262,41108],{},[474,41264,41265],{"class":476,"line":5461},[474,41266,41118],{},[474,41268,41269],{"class":476,"line":5470},[474,41270,41128],{},[474,41272,41273],{"class":476,"line":5475},[474,41274,41138],{},[474,41276,41277],{"class":476,"line":5482},[474,41278,41148],{},[474,41280,41281],{"class":476,"line":5487},[474,41282,41158],{},[474,41284,41285],{"class":476,"line":5492},[474,41286,41168],{},[474,41288,41289],{"class":476,"line":5504},[474,41290,41178],{},[474,41292,41293],{"class":476,"line":5521},[474,41294,40529],{},[439,41296,41297],{},[448,41298,41299],{},"Configuring the firewall",[439,41301,41302],{},"To allow our subnet to communicate outside the host system, we need to update some firewall settings. We learned during\nthe installation of our guest system, that we failed with the netinstall routine: when it came to choosing a debian\nmirror, the installation wizard failed to resolve all available mirrors, which was quite strange since we had network\nconnectivity. Basically, we could connect to and from the base installation with ssh to any external ip – proving that\nour routing setup worked perfectly fine. However, we could not resolve any domain names. All we got when trying to ping\nor resolve a domain name was a not-so-helpfull error message stating “bad domain name”. After some troubleshooting, we\nfound out that XenServer firewalls network communication to and from the guest systems: Per default, only communication\non ports 22, 80 and 443 (ssh, http, https) are allowed. Any other communication, including DNS (Port 53) is blocked,\ntherefore causing Debian netinstall to fail.",[439,41304,41305],{},"To fix this, we added a few iptables rules:",[11947,41307,41308],{},[464,41309,41311],{"className":16895,"code":41310,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# iptables -I RH-Firewall-1-INPUT 3 -s \u003Csubnet>/28 -j ACCEPT\n[root@XenServer-Host ~]#\n[root@XenServer-Host ~]# iptables -nvL --line-numbers\nChain INPUT (policy ACCEPT 0 packets, 0 bytes)\nnum pkts bytes target prot opt in out source destination\n1 19091 2967K RH-Firewall-1-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0\nChain FORWARD (policy ACCEPT 0 packets, 0 bytes)\nnum pkts bytes target prot opt in out source destination\n1 11971 1722K RH-Firewall-1-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0\nChain OUTPUT (policy ACCEPT 21439 packets, 3530K bytes)\nnum pkts bytes target prot opt in out source destination\nChain RH-Firewall-1-INPUT (2 references)\nnum pkts bytes target prot opt in out source destination\n1 2073 1065K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0\n2 163 10568 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 255\n3 6079 898K ACCEPT all -- * * \u003Csubnet>/28 0.0.0.0/0\n4 0 0 ACCEPT esp -- * * 0.0.0.0/0 0.0.0.0/0\n5 0 0 ACCEPT ah -- * * 0.0.0.0/0 0.0.0.0/0\n6 0 0 ACCEPT udp -- * * 0.0.0.0/0 224.0.0.251 udp dpt:5353\n7 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:631\n8 1 40 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:631\n9 18922 2496K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED\n10 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW udp dpt:694\n11 1090 54556 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22\n12 123 6188 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80\n13 57 2796 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:443\n14 2554 156K REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited\n[root@XenServer-Host ~]#\n",[471,41312,41313,41318,41322,41327,41332,41337,41342,41347,41351,41356,41361,41365,41370,41374,41379,41384,41389,41394,41399,41404,41409,41414,41419,41424,41429,41434,41439,41444],{"__ignoreMap":469},[474,41314,41315],{"class":476,"line":477},[474,41316,41317],{},"[root@XenServer-Host ~]# iptables -I RH-Firewall-1-INPUT 3 -s \u003Csubnet>/28 -j ACCEPT\n",[474,41319,41320],{"class":476,"line":507},[474,41321,40529],{},[474,41323,41324],{"class":476,"line":547},[474,41325,41326],{},"[root@XenServer-Host ~]# iptables -nvL --line-numbers\n",[474,41328,41329],{"class":476,"line":584},[474,41330,41331],{},"Chain INPUT (policy ACCEPT 0 packets, 0 bytes)\n",[474,41333,41334],{"class":476,"line":607},[474,41335,41336],{},"num pkts bytes target prot opt in out source destination\n",[474,41338,41339],{"class":476,"line":642},[474,41340,41341],{},"1 19091 2967K RH-Firewall-1-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0\n",[474,41343,41344],{"class":476,"line":663},[474,41345,41346],{},"Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)\n",[474,41348,41349],{"class":476,"line":694},[474,41350,41336],{},[474,41352,41353],{"class":476,"line":700},[474,41354,41355],{},"1 11971 1722K RH-Firewall-1-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0\n",[474,41357,41358],{"class":476,"line":913},[474,41359,41360],{},"Chain OUTPUT (policy ACCEPT 21439 packets, 3530K bytes)\n",[474,41362,41363],{"class":476,"line":920},[474,41364,41336],{},[474,41366,41367],{"class":476,"line":926},[474,41368,41369],{},"Chain RH-Firewall-1-INPUT (2 references)\n",[474,41371,41372],{"class":476,"line":932},[474,41373,41336],{},[474,41375,41376],{"class":476,"line":938},[474,41377,41378],{},"1 2073 1065K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0\n",[474,41380,41381],{"class":476,"line":944},[474,41382,41383],{},"2 163 10568 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmp type 255\n",[474,41385,41386],{"class":476,"line":950},[474,41387,41388],{},"3 6079 898K ACCEPT all -- * * \u003Csubnet>/28 0.0.0.0/0\n",[474,41390,41391],{"class":476,"line":956},[474,41392,41393],{},"4 0 0 ACCEPT esp -- * * 0.0.0.0/0 0.0.0.0/0\n",[474,41395,41396],{"class":476,"line":962},[474,41397,41398],{},"5 0 0 ACCEPT ah -- * * 0.0.0.0/0 0.0.0.0/0\n",[474,41400,41401],{"class":476,"line":4876},[474,41402,41403],{},"6 0 0 ACCEPT udp -- * * 0.0.0.0/0 224.0.0.251 udp dpt:5353\n",[474,41405,41406],{"class":476,"line":4888},[474,41407,41408],{},"7 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:631\n",[474,41410,41411],{"class":476,"line":4900},[474,41412,41413],{},"8 1 40 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:631\n",[474,41415,41416],{"class":476,"line":4913},[474,41417,41418],{},"9 18922 2496K ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED\n",[474,41420,41421],{"class":476,"line":4921},[474,41422,41423],{},"10 0 0 ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW udp dpt:694\n",[474,41425,41426],{"class":476,"line":4932},[474,41427,41428],{},"11 1090 54556 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22\n",[474,41430,41431],{"class":476,"line":4938},[474,41432,41433],{},"12 123 6188 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80\n",[474,41435,41436],{"class":476,"line":4946},[474,41437,41438],{},"13 57 2796 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:443\n",[474,41440,41441],{"class":476,"line":4952},[474,41442,41443],{},"14 2554 156K REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited\n",[474,41445,41446],{"class":476,"line":4957},[474,41447,40529],{},[439,41449,41450],{},[448,41451,41452],{},"Providing ISO image files for guest system installation",[439,41454,41455],{},"Finally, we want to install a guest system on our XenServer host. Because we can’t simply mount an installation image\nwithin XenCenter, we need to create a ISO repository first:",[11947,41457,41458],{},[464,41459,41461],{"className":16895,"code":41460,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# mkdir -p /var/opt/xen/iso_import\n[root@XenServer-Host ~]# xe sr-create name-label=\"MyISORepository\" type=iso device-config:location=/var/opt/xen/iso_import/ device-config:legacy_mode=true content-type=is\n",[471,41462,41463,41468],{"__ignoreMap":469},[474,41464,41465],{"class":476,"line":477},[474,41466,41467],{},"[root@XenServer-Host ~]# mkdir -p /var/opt/xen/iso_import\n",[474,41469,41470],{"class":476,"line":507},[474,41471,41472],{},"[root@XenServer-Host ~]# xe sr-create name-label=\"MyISORepository\" type=iso device-config:location=/var/opt/xen/iso_import/ device-config:legacy_mode=true content-type=is\n",[439,41474,41475],{},"And, for instance, provide a Debian netinstall image here:",[11947,41477,41478],{},[464,41479,41481],{"className":16895,"code":41480,"language":16897,"meta":469,"style":469},"[root@XenServer-Host ~]# cd /var/opt/xen/iso_import\n[root@XenServer-Host ~]# wget http://cdimage.debian.org/debian-cd/7.7.0/amd64/iso-cd/debian-7.7.0-amd64-netinst.iso\n",[471,41482,41483,41488],{"__ignoreMap":469},[474,41484,41485],{"class":476,"line":477},[474,41486,41487],{},"[root@XenServer-Host ~]# cd /var/opt/xen/iso_import\n",[474,41489,41490],{"class":476,"line":507},[474,41491,41492],{},"[root@XenServer-Host ~]# wget http://cdimage.debian.org/debian-cd/7.7.0/amd64/iso-cd/debian-7.7.0-amd64-netinst.iso\n",[439,41494,41495],{},"Keep in mind that your root file system only has 4 GB available, therefore try to provide only one installation image at\na time.",[439,41497,41498],{},[448,41499,33932],{},[439,41501,41502],{},"You should now have a running, properly configured XenServer with the capability of installing and running multiple\nvirtual machines in it. Even though this is how we got it up and running on Hetzner hardware, i don’t claim that this is\nthe only (or best) way to do it. If you have any questions, concerns or better guides on this topic, feel free to share\nit in the comments!",[439,41504,41505],{},[448,41506,41507],{},"Additional Links & Sources",[439,41509,41510],{},[1002,41511,41512],{"href":41512,"rel":41513},"http://support.citrix.com/article/CTX121169",[1006],[439,41515,41516],{},[1002,41517,41518],{"href":41518,"rel":41519},"http://techblog.conglomer.net/adding-different-subnet-ips-to-xenserver/",[1006],[439,41521,41522],{},[1002,41523,41524],{"href":41524,"rel":41525},"http://www.jansipke.nl/using-xenserver-in-a-routed-ip-network/",[1006],[439,41527,41528],{},[1002,41529,41530],{"href":41530,"rel":41531},"http://wiki.hetzner.de/index.php/LSI_RAID_Controller",[1006],[439,41533,41534],{},[1002,41535,41536],{"href":41536,"rel":41537},"http://wiki.hetzner.de/index.php/XenServer_5.6_Installation_per_LARA",[1006],[1024,41539,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":41541},[],[9045],"2014-11-06T15:18:35","A few days ago, we had to set up a XenServer Host, running on one of Hetzners dedicated servers.\\nThere are plenty of howto’s around, but since we happened to run into a few problems with routed networking and Domain\\nName Resolution during installation, that weren’t documented in Hetzners Wiki, i decided to provide a blog post for\\nself-reference and as a way to contribute a (hopefully helpful) basic guide on how to install XenServer on a dedicated\\nserver – in our Case a Hetzner Root Server.","https://synyx.de/blog/installing-xenserver-and-virtual-hosts-in-a-routed-network/",{},"/blog/installing-xenserver-and-virtual-hosts-in-a-routed-network",{"title":40382,"description":41549},"A few days ago, we had to set up a XenServer Host, running on one of Hetzners dedicated servers.\nThere are plenty of howto’s around, but since we happened to run into a few problems with routed networking and Domain\nName Resolution during installation, that weren’t documented in Hetzners Wiki, i decided to provide a blog post for\nself-reference and as a way to contribute a (hopefully helpful) basic guide on how to install XenServer on a dedicated\nserver – in our Case a Hetzner Root Server.","blog/installing-xenserver-and-virtual-hosts-in-a-routed-network",[],"A few days ago, we had to set up a XenServer Host, running on one of Hetzners dedicated servers. There are plenty of howto’s around, but since we happened to…","vC_NgIjQy3jCxvIRsenY2EMxxvbxyCNkuXa6k_KNAdA",{"id":41555,"title":41556,"author":41557,"body":41558,"category":41838,"date":41839,"description":41840,"extension":1034,"link":41841,"meta":41842,"navigation":916,"path":41843,"seo":41844,"slug":41562,"stem":41845,"tags":41846,"teaser":41848,"__hash__":41849},"blog/blog/urlaubsverwaltung-goes-mobile.md","Urlaubsverwaltung goes mobile",[30],{"type":432,"value":41559,"toc":41821},[41560,41563,41566,41569,41572,41576,41580,41590,41593,41597,41615,41618,41621,41624,41627,41637,41640,41644,41647,41657,41660,41663,41666,41669,41672,41690,41693,41696,41700,41703,41713,41716,41719,41722,41740,41743,41746,41750,41767,41770,41773,41776,41779,41783,41791,41794,41797,41801,41807,41814],[435,41561,41556],{"id":41562},"urlaubsverwaltung-goes-mobile",[439,41564,41565],{},"Seit ungefähr zweieinhalb Jahren wird die Urlaubsverwaltung nun bei synyx eingesetzt. Mit dem regelmäßigen Einsatz kamen\nimmer wieder neue Anforderungen hinzu, sodass die Urlaubsverwaltung längst nicht mehr nur zur Beantragung und\nGenehmigung von Urlaubsanträgen genutzt wird. So werden beispielsweise inzwischen auch Krankmeldungen in der\nUrlaubsverwaltung erfasst.",[439,41567,41568],{},"In Zeiten von Smartphones und Tablets ist es wichtig, dass eine Anwendung auch auf mobilen Endgeräten bedienbar ist,\nweshalb die Urlaubsverwaltung ein komplettes Re-Design erfahren hat, um diesen Ansprüchen gerecht zu werden.",[439,41570,41571],{},"Im Folgenden wird das neue Look and Feel der Urlaubsverwaltung vorgestellt und gleichzeitig ein Überblick über einige\nder neuen Features verschafft.",[3938,41573,41575],{"id":41574},"entwicklung-der-übersichtsseite","Entwicklung der Übersichtsseite",[1065,41577,41579],{"id":41578},"_2012","2012",[439,41581,41582],{},[1002,41583,41586],{"href":41584,"rel":41585},"https://media.synyx.de/uploads//2014/10/overview-2012.png",[1006],[2205,41587],{"alt":41588,"src":41589},"Übersicht 2012","https://media.synyx.de/uploads//2014/10/overview-2012-300x240.png",[439,41591,41592],{},"Die Übersichtsseite zeigt Urlaubsanspruch und verbleibende Urlaubstage für das jeweils gewählte Jahr. Es gibt eine\nStatistik darüber, wie viele Urlaubstage für welchen Typ von Urlaub eingeplant sind. Die Urlaubsanträge für das gewählte\nJahr sind mit Status, Zeitraum und Anzahl von verbrauchten Urlaubstagen aufgelistet.",[1065,41594,41596],{"id":41595},"_2014","2014",[439,41598,41599,41607],{},[1002,41600,41603],{"href":41601,"rel":41602},"https://media.synyx.de/uploads//2014/10/overview.png",[1006],[2205,41604],{"alt":41605,"src":41606},"Übersicht","https://media.synyx.de/uploads//2014/10/overview-300x211.png",[1002,41608,41611],{"href":41609,"rel":41610},"https://media.synyx.de/uploads//2014/10/overview_mobile.png",[1006],[2205,41612],{"alt":41613,"src":41614},"Übersicht Mobile","https://media.synyx.de/uploads//2014/10/overview_mobile-173x300.png",[439,41616,41617],{},"Benutzer haben nun dank dem Kalender auf der Übersichtsseite einen schnelleren Überblick über Wochenenden und\nFeiertage (in grün dargestellt) und genehmigten Urlaub (in gelb dargestellt).",[439,41619,41620],{},"Informationen über Urlaubsanspruch und verbleibende Urlaubstage für das gewählte Jahr sind schneller erfassbar durch die\nneue Darstellungsweise, da nur noch die für den Mitarbeiter relevanten Inhalte angezeigt werden.",[439,41622,41623],{},"Die Statistik über geplante Urlaubstage ist aufgeteilt in Erholungsurlaub und anderen Urlaub (Sonderurlaub, unbezahlter\nUrlaub, Überstundenabbau). Hierbei wird angezeigt, wie viele der geplanten Urlaubstage bereits genehmigt wurden.",[439,41625,41626],{},"Die Liste der Urlaubsanträge enthält nun die zusätzliche Information über das Datum der letzten Bearbeitung. Zum\nschnelleren Erfassen des Status der Urlaubsanträge wird dieser durch ein Icon symbolisiert.",[439,41628,41629],{},[1002,41630,41633],{"href":41631,"rel":41632},"https://media.synyx.de/uploads//2014/10/overview2.png",[1006],[2205,41634],{"alt":41635,"src":41636},"Übersichtsseite - Krankmeldungen","https://media.synyx.de/uploads//2014/10/overview2-300x215.png",[439,41638,41639],{},"In der Übersicht befinden sich nun zusätzlich zu den Urlaubsanträgen auch die Krankmeldungen des Mitarbeiters. Hierbei\nwird unterschieden zwischen Krankheitstagen und Kind-Krankheitstagen. Dies ist insofern relevant, da einem Angestellten\ngesetzlich nur eine bestimmte Anzahl von Kind-Krankheitstagen im Jahr zur Verfügung steht.",[3938,41641,41643],{"id":41642},"entwicklung-des-urlaubsantrags","Entwicklung des Urlaubsantrags",[1065,41645,41579],{"id":41646},"_2012-1",[439,41648,41649],{},[1002,41650,41653],{"href":41651,"rel":41652},"https://media.synyx.de/uploads//2014/10/app_form-2012.png",[1006],[2205,41654],{"alt":41655,"src":41656},"Urlaubsantrag 2012","https://media.synyx.de/uploads//2014/10/app_form-2012-300x239.png",[439,41658,41659],{},"Wichtige Informationen bei einem Urlaubsantrag sind Art (Erholungsurlaub, Sonderurlaub, unbezahlter Urlaub,\nÜberstundenabbau) und Zeitraum des Urlaubs.",[439,41661,41662],{},"Start- und Enddatum des Urlaubs werden bequem über einen Datepicker gewählt.",[439,41664,41665],{},"Außerdem stehen optionale Felder zur weiteren Information zur Verfügung wie beispielsweise Vertreter während des\nUrlaubszeitraums, Anschrift und Telefon.",[439,41667,41668],{},"Wird Erholungsurlaub beantragt, muss nicht zwingend ein Grund angegeben werden. Beantragt man jedoch Sonderurlaub,\nunbezahlten Urlaub oder Urlaub zum Überstundenabbau, muss ein Grund angegeben werden.",[1065,41670,41596],{"id":41671},"_2014-1",[439,41673,41674,41682],{},[1002,41675,41678],{"href":41676,"rel":41677},"https://media.synyx.de/uploads//2014/10/app_form1.png",[1006],[2205,41679],{"alt":41680,"src":41681},"Urlaubsantrag","https://media.synyx.de/uploads//2014/10/app_form1-300x210.png",[1002,41683,41686],{"href":41684,"rel":41685},"https://media.synyx.de/uploads//2014/10/app_form_mobile.png",[1006],[2205,41687],{"alt":41688,"src":41689},"Urlaubsantrag Mobile","https://media.synyx.de/uploads//2014/10/app_form_mobile-174x300.png",[439,41691,41692],{},"Die Informationen zum Beantragen von Urlaub haben sich nicht verändert, jedoch wurde der Datepicker erweitert. Um die\nAuswahl des Urlaubszeitraums komfortabler zu gestalten, werden im Datepicker Wochenenden und Feiertage (in grün) und\ngenehmigter Urlaub (in gelb) angezeigt.",[439,41694,41695],{},"Wie hier in der mobilen Ansicht zu sehen, wird nach Angabe des Zeitraums direkt berechnet, wie viele Urlaubstage der\ngewählte Urlaubszeitraum kosten würde.",[3938,41697,41699],{"id":41698},"entwicklung-der-mitarbeiterpflege","Entwicklung der Mitarbeiterpflege",[1065,41701,41579],{"id":41702},"_2012-2",[439,41704,41705],{},[1002,41706,41709],{"href":41707,"rel":41708},"https://media.synyx.de/uploads//2014/10/staff_edit.png",[1006],[2205,41710],{"alt":41711,"src":41712},"Mitarbeiterformular 2012","https://media.synyx.de/uploads//2014/10/staff_edit-300x239.png",[439,41714,41715],{},"Das Formular zum Pflegen von Mitarbeitern ist unterteilt in Personendaten und Urlaubsanspruch.",[439,41717,41718],{},"Beim Urlaubsanspruch gibt man den Jahresurlaubsanspruch und gegebenenfalls Resturlaub aus dem letzten Jahr an. Anhand\ndes gewählten Zeitraums und des Jahresurlaubsanspruchs wird der anteilige Urlaubsanspruch berechnet. Es kann bestimmt\nwerden, ob der Resturlaub aus dem letzten Jahr am 1. April eines Jahres verfällt oder weiterhin genutzt werden kann.",[1065,41720,41596],{"id":41721},"_2014-2",[439,41723,41724,41732],{},[1002,41725,41728],{"href":41726,"rel":41727},"https://media.synyx.de/uploads//2014/10/staff_list.png",[1006],[2205,41729],{"alt":41730,"src":41731},"Mitarbeiterformular","https://media.synyx.de/uploads//2014/10/staff_list-300x202.png",[1002,41733,41736],{"href":41734,"rel":41735},"https://media.synyx.de/uploads//2014/10/staff_edit_mobile.png",[1006],[2205,41737],{"alt":41738,"src":41739},"Mitarbeiter Mobile","https://media.synyx.de/uploads//2014/10/staff_edit_mobile-174x300.png",[439,41741,41742],{},"Das Mitarbeiterformular wurde erweitert um Rollen- und Arbeitszeitkonfiguration. Ein Mitarbeiter kann mehrere Rollen\ngleichzeitig innehaben – außer er ist inaktiv.",[439,41744,41745],{},"Bei der Arbeitszeitkonfiguration werden die wöchentlichen Arbeitstage eines Mitarbeiters festgelegt. Diese Konfiguration\nwird bei der Berechnung von Urlaubstagen für einen Urlaubszeitraum herangezogen. Es wird nicht mehr standardmäßig davon\nausgegangen, dass jeder Mitarbeiter von Montag bis Freitag arbeitet.",[3938,41747,41749],{"id":41748},"krankmeldungen","Krankmeldungen",[439,41751,41752,41759],{},[1002,41753,41756],{"href":41754,"rel":41755},"https://media.synyx.de/uploads//2014/10/sick_notes1.png",[1006],[2205,41757],{"alt":41749,"src":41758},"https://media.synyx.de/uploads//2014/10/sick_notes1-300x209.png",[1002,41760,41763],{"href":41761,"rel":41762},"https://media.synyx.de/uploads//2014/10/sick_notes_mobile.png",[1006],[2205,41764],{"alt":41765,"src":41766},"Krankmeldungen Mobile","https://media.synyx.de/uploads//2014/10/sick_notes_mobile-174x300.png",[439,41768,41769],{},"Neben Urlaubsanträgen können nun auch Krankmeldungen in der Anwendung elektronisch verwaltet werden.",[439,41771,41772],{},"Dabei gibt es zwei Typen von Krankmeldungen: eigene Krankmeldungen und Kind-Krankmeldungen.",[439,41774,41775],{},"Nach dem Anlegen einer Krankmeldung kann diese bei Bedarf bearbeitet, storniert oder in Urlaub umgewandelt werden.",[439,41777,41778],{},"Im Gegensatz zu Urlaubsanträgen können Krankmeldungen übrigens nur vom Office angelegt werden und nicht vom Mitarbeiter\nselbst 😉",[3938,41780,41782],{"id":41781},"demo-system","Demo-System",[439,41784,41785,41786,41790],{},"Wem dieser Überblick über die Urlaubsverwaltung noch nicht gereicht hat, kann sich gerne die aktuelle Version der\nUrlaubsverwaltung auf\nunserem ",[1002,41787,41782],{"href":41788,"rel":41789},"https://urlaubsverwaltung-demo.synyx.de/login?username=office&password=secret",[1006]," anschauen.",[439,41792,41793],{},"Benutzername: office",[439,41795,41796],{},"Passwort: secret",[3938,41798,41800],{"id":41799},"weiterführende-informationen","Weiterführende Informationen",[439,41802,41803],{},[1002,41804,41806],{"href":17440,"rel":41805},[1006],"Projekt-Repository auf Github",[439,41808,41809],{},[1002,41810,41813],{"href":41811,"rel":41812},"http://blog.synyx.de/2012/11/urlaubsverwaltung-was-hat-sich-getan/",[1006],"Blogpost von 2012",[439,41815,41816],{},[1002,41817,41820],{"href":41818,"rel":41819},"http://blog.synyx.de/2011/11/elektronische-urlaubsverwaltung-made-by-youngsters/",[1006],"Blogpost von 2011",{"title":469,"searchDepth":507,"depth":507,"links":41822},[41823,41827,41831,41835,41836,41837],{"id":41574,"depth":507,"text":41575,"children":41824},[41825,41826],{"id":41578,"depth":547,"text":41579},{"id":41595,"depth":547,"text":41596},{"id":41642,"depth":507,"text":41643,"children":41828},[41829,41830],{"id":41646,"depth":547,"text":41579},{"id":41671,"depth":547,"text":41596},{"id":41698,"depth":507,"text":41699,"children":41832},[41833,41834],{"id":41702,"depth":547,"text":41579},{"id":41721,"depth":547,"text":41596},{"id":41748,"depth":507,"text":41749},{"id":41781,"depth":507,"text":41782},{"id":41799,"depth":507,"text":41800},[1031],"2014-10-28T17:56:39","Seit ungefähr zweieinhalb Jahren wird die Urlaubsverwaltung nun bei synyx eingesetzt. Mit dem regelmäßigen Einsatz kamen\\nimmer wieder neue Anforderungen hinzu, sodass die Urlaubsverwaltung längst nicht mehr nur zur Beantragung und\\nGenehmigung von Urlaubsanträgen genutzt wird. So werden beispielsweise inzwischen auch Krankmeldungen in der\\nUrlaubsverwaltung erfasst.","https://synyx.de/blog/urlaubsverwaltung-goes-mobile/",{},"/blog/urlaubsverwaltung-goes-mobile",{"title":41556,"description":41565},"blog/urlaubsverwaltung-goes-mobile",[389,26637,41847,26638],"urlaubsantrag","Seit ungefähr zweieinhalb Jahren wird die Urlaubsverwaltung nun bei synyx eingesetzt. Mit dem regelmäßigen Einsatz kamen immer wieder neue Anforderungen hinzu, sodass die Urlaubsverwaltung längst nicht mehr nur zur Beantragung…","hARHsIALbjcriLCXopdnTfuLaNQU4rs0CDiJIDO0kCM",{"id":41851,"title":41852,"author":41853,"body":41854,"category":42012,"date":42013,"description":42014,"extension":1034,"link":42015,"meta":42016,"navigation":916,"path":42017,"seo":42018,"slug":42020,"stem":42021,"tags":42022,"teaser":42024,"__hash__":42025},"blog/blog/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids.md","Devoxx4Kids in Karlsruhe – Programmieren und Elektronik für Kids",[238],{"type":432,"value":41855,"toc":42007},[41856,41859,41876,41878,41881,41896,41913,41923,41930,41940,41947,41951,41975,41979,42001,42004],[435,41857,41852],{"id":41858},"devoxx4kids-in-karlsruhe-programmieren-und-elektronik-für-kids",[439,41860,13082,41861,41865,41866,41871,41872,41875],{},[1002,41862,12123],{"href":41863,"rel":41864},"http://www.devoxx4kids.org/deutschland/",[1006]," ist eine Tagung für Kinder. Angelehnt ist sie an die Devoxx,\ndie alljährliche Konferenz für Java-Entwickler. Die Devoxx4Kids richtet ihren Fokus auf die Programmierer, Entwickler\nund Informationsdesigner von Morgen. Die Idee zur dieser Kindertagung entstand vor einigen Jahren in der belgischen Java\nUser Group (",[1002,41867,41870],{"href":41868,"rel":41869},"http://www.bejug.org/",[1006],"BeJUG",") und findet seither weltweit großen Anklang. 26 Jungen und sechs Mädchen im\nAlter von acht bis 14 Jahren fanden sich am letzten Samstag, dem 27.09.2014, in\nder ",[1002,41873,18688],{"href":26135,"rel":41874},[1006]," in Karlsruhe ein, um in vier angebotenen Workshops Spiele und\nRoboter zu programmieren, Quadrocopter zu steuern und die Grundlagen der Elektronik zu erlernen. Die Kids haben in\njeweils vier Zweiergruppen pro Workshop Übungen absolviert, wobei der Lernspaß hier im Vordergrund stand.",[3938,41877,12184],{"id":12183},[439,41879,41880],{},"Insgesamt hat jedes Kind vier Workshops besucht. Jeder Workshop wurde von einem Team von drei bis vier Mentoren\ngeleitet, um die Kids optimal betreuen zu können. Neben den Workshops zu Scratch und dem Roboter Nao, die mittlerweile\nauf Veranstaltungen dieser Art in anderen Ländern erprobt sind, wurden auch zwei völlig neue Workshops angeboten: synyx\nhat einen Quadrocopter Workshop erarbeitet und angeboten, Tinkerforge einen zu den Grundlagen der Elektronik und des\n‘Internets der Dinge’.",[439,41882,41883,41891,41895],{},[1002,41884,41887],{"href":41885,"rel":41886},"https://media.synyx.de/uploads//2014/09/IMG_9017.jpg",[1006],[2205,41888],{"alt":41889,"src":41890},"IMG_9017","https://media.synyx.de/uploads//2014/09/IMG_9017-300x200.jpg",[1002,41892,12189],{"href":41893,"rel":41894},"http://scratch.mit.edu/",[1006],"\nist eine grafische Programmiersprache, die es erlaubt eigene Spielideen schnell umzusetzen und die Grundlagen der\nSpieleprogrammierung zu erlernen. Nachdem mit den Kindern zusammen eine gemeinsame Grundlage für ein Spiel erarbeitet\nwurde, durften eigene Ideen ausprobiert werden. Dabei haben die interessierten Fragen der Kids die Mentoren oft ganz\nschön ins Grübeln gebracht, zusammen wurden aber Lösungen zur Umsetzung interessanter und individueller Spielideen\ngefunden.",[439,41897,41898,41906,41907,41912],{},[1002,41899,41902],{"href":41900,"rel":41901},"https://media.synyx.de/uploads//2014/09/IMG_9083.jpg",[1006],[2205,41903],{"alt":41904,"src":41905},"IMG_9083","https://media.synyx.de/uploads//2014/09/IMG_9083-300x200.jpg","\nDer ",[1002,41908,41911],{"href":41909,"rel":41910},"https://web.archive.org/web/20160201111355/https://www.aldebaran.com/en",[1006],"Nao-Robot"," ist ein humanoider Roboter,\nhergestellt von der französischen Firma Aldebaran Robotics. Für diesen Workshop waren Tasha und Pierre Carl mit ihren\nbeiden Nao-Robotern extra aus Belgien angereist. Mittels der Oberfläche Choreographe konnten die Kids das Verhalten des\nRoboters programmieren, seine Sensoren und Aktoren ansprechen und auf Signale reagieren. So war der Nao in der Lage die\nKinder zu suchen und mit ihnen zu interagieren. Sein Talent als Unterhalter hat der Nao aber auch in der Mittagspause\ngezeigt, in der sich als Tänzer und Rock’n’Roller präsentierte (und damit nicht nur die Kinder unterhielt und\nbeeindruckte).",[439,41914,41915],{},[1002,41916,41919],{"href":41917,"rel":41918},"https://media.synyx.de/uploads//2014/09/IMG_8947.jpg",[1006],[2205,41920],{"alt":41921,"src":41922},"IMG_8947","https://media.synyx.de/uploads//2014/09/IMG_8947-300x200.jpg",[439,41924,41925,41926,41929],{},"Als Piloten agierten die Kinder im ",[448,41927,41928],{},"Quadrocopter-Workshop",". Hier steuerten sie fliegende Drohnen mittels einer\nJavaScript-API. In diesem Workshop galt es am Beispiel von Quadrocoptern zu verstehen, wie ein Echtzeitsystem reagiert\nund sich in einer räumlich beschränkten Geometrie bewegt. Die Flugübungen umfassten das Umfliegen von Hindernissen und\ndas Ausführen von Figuren.",[439,41931,41932],{},[1002,41933,41936],{"href":41934,"rel":41935},"https://media.synyx.de/uploads//2014/09/IMG_8994.jpg",[1006],[2205,41937],{"alt":41938,"src":41939},"IMG_8994","https://media.synyx.de/uploads//2014/09/IMG_8994-300x200.jpg",[439,41941,41942,41943,41946],{},"Eine Einführung in das ‘Internet der Dinge’ gab der Tinkerforge-Workshop. ",[1002,41944,36025],{"href":38411,"rel":41945},[1006],"\nbietet elektronische Bausteine, die ähnlich wie Legosteine kombiniert werden können, und einen Basissatz an Sensoren für\nTemperatur, Luftfeuchtigkeit oder Lichteinstrahlung mitbringen. Im Workshop wurde den Kindern erklärt, wie diese\nSensoren Daten liefern, wie deren Auswertung funktioniert und wie auf diese reagiert werden kann. Auch das Anzeigen und\nInterpretieren von Daten hat hier eine Rolle gespielt.",[3938,41948,41950],{"id":41949},"betreuung-und-sponsoren","Betreuung und Sponsoren",[439,41952,41953,41961,41962,41964,41965,41968,41969,41971,41972,41974],{},[1002,41954,41957],{"href":41955,"rel":41956},"https://media.synyx.de/uploads//2014/09/IMG_9163.jpg",[1006],[2205,41958],{"alt":41959,"src":41960},"IMG_9163","https://media.synyx.de/uploads//2014/09/IMG_9163-300x200.jpg","\nViele Personen haben dazu beigetragen, dass die Devoxx4Kids in Karlsruhe ein Erfolg für alle Teilnehmer wurde.\nAngefangen bei den mehr als zehn Kollegen von synyx kamen viele weitere Freiwillige aus verschiedenen Teilen der\nRepublik (von Braunschweig über Schwäbisch Hall und Heidelberg bis nach München) und sogar aus Belgien dazu.\nAusgerichtet wurde die Devoxx4Kids zwar von ",[448,41963,389],{},", aber weitere Sponsoren haben mit zum Erfolg beigetragen. Vielen\nDank hier an ",[448,41966,41967],{},"Citrix"," (Goldsponsor), ",[448,41970,36025],{}," (die den Kindern jeweils ein Elektronikset mit nach Hause gaben)\nund die ",[448,41973,18688],{}," (die ihre tollen Räume zur Verfügung stellte).",[3938,41976,41978],{"id":41977},"nachhaltiger-lernspaß","Nachhaltiger Lernspaß",[439,41980,41981,41989,41990,41994,41995,42000],{},[1002,41982,41985],{"href":41983,"rel":41984},"https://media.synyx.de/uploads//2014/09/IMG_9143.jpg",[1006],[2205,41986],{"alt":41987,"src":41988},"IMG_9143","https://media.synyx.de/uploads//2014/09/IMG_9143-300x200.jpg","\nAm Ende der Veranstaltung durften die Kinder jeweils einen Raspberry-Pi und ein Tinkerforge-Set mit nach Hause nehmen,\num das Gelernte nicht zu vergessen und um zu Hause weiter üben zu können. Die neuen Vorträge werden demnächst, so wie es\nbei der Devoxx4Kids üblich ist, auf der ",[1002,41991,41993],{"href":41863,"rel":41992,"title":12123},[1006],"Homepage"," und\nauf ",[1002,41996,27413],{"href":41997,"rel":41998,"title":41999},"https://github.com/devoxx4kids/",[1006],"D4K Github"," bereit gestellt, damit sie auch anderen zur Verfügung stehen\nund angepasst oder auch weiterentwickelt werden können. Aber nicht nur die Kinder, auch die Betreuer und Tutoren haben\nviel gelernt an diesem Tag. Die Kinder haben riesiges Interesse an den angebotenen Themen gezeigt und mit ihrer\nNeugierde und Fragen die Tutoren immer wieder gefordert. Wir hoffen, dass der Tag den Kindern genauso viel Spaß gemacht\nhat wie den Veranstaltern, Tutoren und Helfern! Es ist geplant, die Devoxx4Kids zu einem regelmäßigen Event in Karlsruhe\nwerden zu lassen. Aber auch andere Städte sind herzlich eingeladen eine eigene Devoxx4Kids auf die Beine zu stellen. Wir\nstehen allen Interessierten hier gerne mit Rat und Tat zur Seite!",[439,42002,42003],{},"Vielen Dank an alle Beteiligten und bis zum nächsten Mal sagt Euer",[439,42005,42006],{},"Devoxx4Kids Team aus Deutschland",{"title":469,"searchDepth":507,"depth":507,"links":42008},[42009,42010,42011],{"id":12183,"depth":507,"text":12184},{"id":41949,"depth":507,"text":41950},{"id":41977,"depth":507,"text":41978},[1031],"2014-09-30T12:00:45","Die Devoxx4Kids ist eine Tagung für Kinder. Angelehnt ist sie an die Devoxx,\\ndie alljährliche Konferenz für Java-Entwickler. Die Devoxx4Kids richtet ihren Fokus auf die Programmierer, Entwickler\\nund Informationsdesigner von Morgen. Die Idee zur dieser Kindertagung entstand vor einigen Jahren in der belgischen Java\\nUser Group (BeJUG) und findet seither weltweit großen Anklang. 26 Jungen und sechs Mädchen im\\nAlter von acht bis 14 Jahren fanden sich am letzten Samstag, dem 27.09.2014, in\\nder Karlshochschule in Karlsruhe ein, um in vier angebotenen Workshops Spiele und\\nRoboter zu programmieren, Quadrocopter zu steuern und die Grundlagen der Elektronik zu erlernen. Die Kids haben in\\njeweils vier Zweiergruppen pro Workshop Übungen absolviert, wobei der Lernspaß hier im Vordergrund stand.","https://synyx.de/blog/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids/",{},"/blog/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids",{"title":41852,"description":42019},"Die Devoxx4Kids ist eine Tagung für Kinder. Angelehnt ist sie an die Devoxx,\ndie alljährliche Konferenz für Java-Entwickler. Die Devoxx4Kids richtet ihren Fokus auf die Programmierer, Entwickler\nund Informationsdesigner von Morgen. Die Idee zur dieser Kindertagung entstand vor einigen Jahren in der belgischen Java\nUser Group (BeJUG) und findet seither weltweit großen Anklang. 26 Jungen und sechs Mädchen im\nAlter von acht bis 14 Jahren fanden sich am letzten Samstag, dem 27.09.2014, in\nder Karlshochschule in Karlsruhe ein, um in vier angebotenen Workshops Spiele und\nRoboter zu programmieren, Quadrocopter zu steuern und die Grundlagen der Elektronik zu erlernen. Die Kids haben in\njeweils vier Zweiergruppen pro Workshop Übungen absolviert, wobei der Lernspaß hier im Vordergrund stand.","devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids","blog/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids",[4216,38371,42023,38373,24653,389,38374,9459],"nao-robot","Die Devoxx4Kids ist eine Tagung für Kinder. Angelehnt ist sie an die Devoxx, die alljährliche Konferenz für Java-Entwickler. Die Devoxx4Kids richtet ihren Fokus auf die Programmierer, Entwickler und Informationsdesigner von…","hD2fD8AyJTu9uCZLO2KiTyWupgwBe-NlAU-ZynsoBhE",{"id":42027,"title":42028,"author":42029,"body":42030,"category":42043,"date":42044,"description":42045,"extension":1034,"link":42046,"meta":42047,"navigation":916,"path":42048,"seo":42049,"slug":42034,"stem":42050,"tags":42051,"teaser":42052,"__hash__":42053},"blog/blog/wir-ziehen-um.md","Wir ziehen um!",[178],{"type":432,"value":42031,"toc":42041},[42032,42035,42038],[435,42033,42028],{"id":42034},"wir-ziehen-um",[439,42036,42037],{},"Ab Oktober finden Sie uns in unseren neuen Büroräumen in der Gartenstraße 67 in 76135 Karlsruhe. Unsere bisherigen\nTelefonnummern und Email-Adressen bleiben erhalten.",[439,42039,42040],{},"Da ein Umzug natürlich auch mit logistischem und organisatorischen Aufwand verbunden ist, bitten wir Sie um Verständnis,\ndass wir am 1. und 2. Oktober nur eingeschränkt erreichbar sind. Während dieser Zeit kann es auch vereinzelt zu\nAusfällen unserer Services kommen. Ab Montag, dem 6. Oktober stehen wir wieder wie gewohnt für Sie zur Verfügung.",{"title":469,"searchDepth":507,"depth":507,"links":42042},[],[9045],"2014-09-24T13:31:18","Ab Oktober finden Sie uns in unseren neuen Büroräumen in der Gartenstraße 67 in 76135 Karlsruhe. Unsere bisherigen\\nTelefonnummern und Email-Adressen bleiben erhalten.","https://synyx.de/blog/wir-ziehen-um/",{},"/blog/wir-ziehen-um",{"title":42028,"description":42037},"blog/wir-ziehen-um",[],"Ab Oktober finden Sie uns in unseren neuen Büroräumen in der Gartenstraße 67 in 76135 Karlsruhe. Unsere bisherigen Telefonnummern und Email-Adressen bleiben erhalten. Da ein Umzug natürlich auch mit logistischem…","5pn6vmFmiQNbFGavHOOn5pAcgATjoeh3Oo61us1ZSsA",{"id":42055,"title":42056,"author":42057,"body":42058,"category":42390,"date":42391,"description":42392,"extension":1034,"link":42393,"meta":42394,"navigation":916,"path":42395,"seo":42396,"slug":42062,"stem":42397,"tags":42398,"teaser":42403,"__hash__":42404},"blog/blog/the-qt-framework-solid-fun-in-many-languages.md","The Qt framework: solid fun in many languages",[292],{"type":432,"value":42059,"toc":42388},[42060,42063,42066,42083,42092,42095,42098,42100,42103,42111,42120,42123,42130,42133,42137,42140,42148,42157,42160,42167,42170,42173,42265,42273,42277,42285,42288,42298,42308,42318,42328,42338,42347,42356,42366,42376,42386],[435,42061,42056],{"id":42062},"the-qt-framework-solid-fun-in-many-languages",[439,42064,42065],{},"Particularly to people using C++ and Python the Qt framework is probably quite well-known, as in these communities\nit’s one of the most-used frameworks for application development. For those who don’t know what Qt is or what it does:\nit’s a comprehensive LGPL-licensed framework providing cross-platform support for GUI, network, multimedia, database,\nsensors, graphics (OpenGL) and many other features. In this article I would like to give a quick overview of these.",[439,42067,42068,42069,8351,42072,8351,42075,42078,42079,42082],{},"While written in C++, Qt has many language bindings",[1002,42070,5008],{"href":42071},"#sdfootnote1sym",[1002,42073,4871],{"href":42074},"#sdfootnote2sym",[1002,42076,15946],{"href":42077},"#sdfootnote3sym",",\nincluding for Python, Perl, Ada, Ruby, Java, BASIC, Go, C#, PHP, Lua and Haskell. Any application written in any of\nthese languages and using the Qt framework can be deployed unmodified on any of the supported\nplatforms",[1002,42080,5224],{"href":42081},"#sdfootnote4sym"," – including all major desktop and mobile platforms – which makes it a popular framework\nfor many big organizations and companies. Some well-known applications written using Qt include Autodesk Maya, Altera\nQuartus, KDE, Google Earth, Skype, Spotify (Linux), Virtualbox and VLC.",[439,42084,42085],{},[1002,42086,42089],{"href":42087,"rel":42088},"https://media.synyx.de/uploads//2014/09/qt_imagecomposer_qt-creator.jpg",[1006],[2205,42090],{"alt":42091,"src":42087},"qt_imagecomposer_qt-creator",[439,42093,42094],{},"Screenshot 1: Image Composition sample application running on top of Qt Creator IDE.",[439,42096,42097],{},"In addition to the straight Qt framework there is also the Qt Modeling Language (QML) component which can be used to\nrapidly create user interface-centric applications in a JavaScript-based, declarative language. It’s commonly used for\nmobile and embedded applications. A basic QML application can be enhanced using JavaScript code and feature anything\nfrom UI controls to a complete web browser widget (using the WebKit-based module).",[435,42099,10009],{"id":10008},[439,42101,42102],{},"When I started using Qt in 2010 Qt 4.7 was the standard. Since then Qt has grown into its current form at version 5.3,\nwith a strong focus on JavaScript and mobile development (using the Qt Quick module, which defines QML), while the\noriginal C++ API also got a makeover. This didn’t change any fundamentals, however, mostly improving library\norganization and features such as accessibility in GUIs.",[439,42104,42105,42106,42110],{},"To quickly build a GUI application, one can use the provided Qt Creator IDE, which includes all of the tools to make any\ntype of application, including non-Qt-based ones. If one wanted to for example create a browser using the Webkit\nbrowser engine, a single class implementation would suffice, as in Qt’s Fancy Browser example",[1002,42107,42109],{"href":42108},"#sdfootnote5sym","5",", which\ngoes one step further and even loads a JQuery instance into the JavaScript runtime to perform HTML manipulation.",[439,42112,42113],{},[1002,42114,42117],{"href":42115,"rel":42116},"https://media.synyx.de/uploads//2014/09/qt_fancybrowser.jpg",[1006],[2205,42118],{"alt":42119,"src":42115},"qt_fancybrowser",[439,42121,42122],{},"Screenshot 2: Fancy Browser example application.",[439,42124,42125,42126,42129],{},"For a hobby project I took this basic concept and made a more full-featured browser",[1002,42127,4859],{"href":42128},"#sdfootnote6sym",", writing a\ncustom cookie handler among other extensions to the basic Qt classes. With the foundation Qt provides it’s very easy to\nrapidly get started on a project, or to quickly prototype a concept without wasting hours on implementation details.",[439,42131,42132],{},"Whether one uses C++, Python, Ada or another language for which a complete wrapper exists, the basic principle doesn’t\nchange in implementing a Qt-based application. One always uses the same API and same concepts, just molded to fit the\nimplementing language.",[435,42134,42136],{"id":42135},"enter-qml","Enter QML",[439,42138,42139],{},"Even to long-time users of C++/Qt QML can seem quite confusing at first, mostly because of the confusion over what\nQML is and isn’t. In essence QML (Qt Modeling Language) is the name of the modeling language: a descriptive language\nusing which one can define user interface elements and their behavior. QML is part of Qt Quick, the UI creation kit\nwhich itself is part of the Qt framework. Finally, the runtime for QML is called Qt Declarative.",[439,42141,42142,42143,42147],{},"Places where QML is used include (outside of mobile/embedded) KDE and the Unity UI (as of version 8",[1002,42144,42146],{"href":42145},"#sdfootnote7sym","7",")\nwhich is used by Ubuntu. The main motivations behind the use of a QML-based UI seem to revolve around the language and\nplatform agnostic nature of it. All one needs is the QML runtime whereby one can add JavaScript and C++ code for\nfurther functionality. Unity 8 uses QML to ease the cross-platform deployment across desktop and mobile devices (\nrunning Ubuntu Touch).",[439,42149,42150],{},[1002,42151,42154],{"href":42152,"rel":42153},"https://media.synyx.de/uploads//2014/09/qt_photo_viewer.jpg",[1006],[2205,42155],{"alt":42156,"src":42152},"Qt PhotoViewer sample",[439,42158,42159],{},"Screenshot 3: Photo Viewer example. QML with minimal JavaScript.",[439,42161,42162,42163,42166],{},"The Photo Viewer QML example application",[1002,42164,5129],{"href":42165},"#sdfootnote8sym"," on the Qt site is a good example of how much one can do\nwith just QML: this application allows one to define all views of the application with transitions, widgets and the\nXML-based model which retrieves image URLs from the Flickr public API. The JavaScript file is just used for some minor\nutility functions.",[439,42168,42169],{},"In theory one could extend the JavaScript side to include more or additional logic, and use a C++ extension for\nexample image processing or similar functionality. Where one puts the logic and which features are included would be\ndetermined by the available resources and intended languages. One can also use QML with just C++, or pure QML with no\nadditional languages. Many QML applications can be readily deployed on a mobile device as well.",[439,42171,42172],{},"QML isn’t just about static content either. Using Qt’s multimedia features one can for example quickly set up a video\nplayer:",[464,42174,42176],{"className":16895,"code":42175,"language":16897,"meta":469,"style":469},"import QtQuick 2.0\nimport QtMultimedia 5.0\nVideo {\n id: video\n width : 800\n height : 600\n source: \"video.avi\"\n \u003Ca class=\"broken_link\" href=\"http://qt-project.org/doc/qt-5/qml-qtquick-mousearea.html\">MouseArea\u003C/a> {\n anchors.fill: parent\n onClicked: {\n video.play()\n }\n }\n focus: true\n Keys.onSpacePressed: video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play()\n Keys.onLeftPressed: video.seek(video.position - 5000)\n Keys.onRightPressed: video.seek(video.position + 5000)\n}\n",[471,42177,42178,42183,42188,42193,42198,42203,42208,42213,42218,42223,42228,42233,42237,42241,42246,42251,42256,42261],{"__ignoreMap":469},[474,42179,42180],{"class":476,"line":477},[474,42181,42182],{},"import QtQuick 2.0\n",[474,42184,42185],{"class":476,"line":507},[474,42186,42187],{},"import QtMultimedia 5.0\n",[474,42189,42190],{"class":476,"line":547},[474,42191,42192],{},"Video {\n",[474,42194,42195],{"class":476,"line":584},[474,42196,42197],{}," id: video\n",[474,42199,42200],{"class":476,"line":607},[474,42201,42202],{}," width : 800\n",[474,42204,42205],{"class":476,"line":642},[474,42206,42207],{}," height : 600\n",[474,42209,42210],{"class":476,"line":663},[474,42211,42212],{}," source: \"video.avi\"\n",[474,42214,42215],{"class":476,"line":694},[474,42216,42217],{}," \u003Ca class=\"broken_link\" href=\"http://qt-project.org/doc/qt-5/qml-qtquick-mousearea.html\">MouseArea\u003C/a> {\n",[474,42219,42220],{"class":476,"line":700},[474,42221,42222],{}," anchors.fill: parent\n",[474,42224,42225],{"class":476,"line":913},[474,42226,42227],{}," onClicked: {\n",[474,42229,42230],{"class":476,"line":920},[474,42231,42232],{}," video.play()\n",[474,42234,42235],{"class":476,"line":926},[474,42236,5704],{},[474,42238,42239],{"class":476,"line":932},[474,42240,1276],{},[474,42242,42243],{"class":476,"line":938},[474,42244,42245],{}," focus: true\n",[474,42247,42248],{"class":476,"line":944},[474,42249,42250],{}," Keys.onSpacePressed: video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play()\n",[474,42252,42253],{"class":476,"line":950},[474,42254,42255],{}," Keys.onLeftPressed: video.seek(video.position - 5000)\n",[474,42257,42258],{"class":476,"line":956},[474,42259,42260],{}," Keys.onRightPressed: video.seek(video.position + 5000)\n",[474,42262,42263],{"class":476,"line":962},[474,42264,703],{},[439,42266,42267,42268,42272],{},"This sample, taken from the Qt Video QML type documentation",[1002,42269,42271],{"href":42270},"#sdfootnote9sym","9"," shows just how easy it is to set up a\nresponsive user interface with QML and to add elements which not only respond to user inputs, but can use video and\naudio as well.",[435,42274,42276],{"id":42275},"wrapping-up","Wrapping up",[439,42278,42279,42280,42284],{},"This article has barely scratched the surface of what Qt is capable of. The multi-threading, networking, multimedia,\ngraphics acceleration, storage-related and many other features are at least as interesting. Using the many sample\napplications on the Qt site",[1002,42281,42283],{"href":42282},"#sdfootnote10sym","10"," it’s easy to get an idea of the possibilities, however. Simply\ndownload the current version of the libraries together with Qt Creator and browse through the examples in the Welcome\ntab of the IDE, or check them out online.",[439,42286,42287],{},"Finally, if anyone reading has experience with any of the language wrappers for Qt, please leave a comment. I’d be very\ninterested in hearing how well they work.",[439,42289,42290,42293],{},[1002,42291,5008],{"href":42292},"#sdfootnote1anc",[1002,42294,42297],{"href":42295,"rel":42296},"http://qt%5C-project.org/wiki/Category:LanguageBindings",[1006],"http://qt\\-project.org/wiki/Category:LanguageBindings",[439,42299,42300,42303],{},[1002,42301,4871],{"href":42302},"#sdfootnote2anc",[1002,42304,42307],{"href":42305,"rel":42306},"http://en.wikipedia.org/wiki/List%5C_of%5C_language%5C_bindings%5C_for%5C_Qt%5C_4",[1006],"http://en.wikipedia.org/wiki/List\\_of\\_language\\_bindings\\_for\\_Qt\\_4",[439,42309,42310,42313],{},[1002,42311,15946],{"href":42312},"#sdfootnote3anc",[1002,42314,42317],{"href":42315,"rel":42316},"http://en.wikipedia.org/wiki/List%5C_of%5C_language%5C_bindings%5C_for%5C_Qt%5C_5",[1006],"http://en.wikipedia.org/wiki/List\\_of\\_language\\_bindings\\_for\\_Qt\\_5",[439,42319,42320,42323],{},[1002,42321,5224],{"href":42322},"#sdfootnote4anc",[1002,42324,42327],{"href":42325,"rel":42326},"http://qt%5C-project.org/doc/qt%5C-5/supported%5C-platforms.html",[1006],"http://qt\\-project.org/doc/qt\\-5/supported\\-platforms.html",[439,42329,42330,42333],{},[1002,42331,42109],{"href":42332},"#sdfootnote5anc",[1002,42334,42337],{"href":42335,"rel":42336},"http://qt%5C-project.org/doc/qt%5C-5/qtwebkitexamples%5C-webkitwidgets%5C-fancybrowser%5C-example.html",[1006],"http://qt\\-project.org/doc/qt\\-5/qtwebkitexamples\\-webkitwidgets\\-fancybrowser\\-example.html",[439,42339,42340,42343],{},[1002,42341,4859],{"href":42342},"#sdfootnote6anc",[1002,42344,42345],{"href":42345,"rel":42346},"http://mayaposch.com/wildfox.php",[1006],[439,42348,42349,42352],{},[1002,42350,42146],{"href":42351},"#sdfootnote7anc",[1002,42353,42354],{"href":42354,"rel":42355},"https://unity.ubuntu.com/getinvolved/development/unity8/",[1006],[439,42357,42358,42361],{},[1002,42359,5129],{"href":42360},"#sdfootnote8anc",[1002,42362,42365],{"href":42363,"rel":42364},"http://qt%5C-project.org/doc/qt%5C-5/qtquick%5C-demos%5C-photoviewer%5C-example.html",[1006],"http://qt\\-project.org/doc/qt\\-5/qtquick\\-demos\\-photoviewer\\-example.html",[439,42367,42368,42371],{},[1002,42369,42271],{"href":42370},"#sdfootnote9anc",[1002,42372,42375],{"href":42373,"rel":42374},"http://qt%5C-project.org/doc/qt%5C-5/qml%5C-qtmultimedia%5C-video.html",[1006],"http://qt\\-project.org/doc/qt\\-5/qml\\-qtmultimedia\\-video.html",[439,42377,42378,42381],{},[1002,42379,42283],{"href":42380},"#sdfootnote10anc",[1002,42382,42385],{"href":42383,"rel":42384},"http://qt%5C-project.org/",[1006],"http://qt\\-project.org/",[1024,42387,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":42389},[],[1030],"2014-09-18T17:15:03","Particularly to people using C++ and Python the Qt framework is probably quite well-known, as in these communities\\nit’s one of the most-used frameworks for application development. For those who don’t know what Qt is or what it does:\\nit’s a comprehensive LGPL-licensed framework providing cross-platform support for GUI, network, multimedia, database,\\nsensors, graphics (OpenGL) and many other features. In this article I would like to give a quick overview of these.","https://synyx.de/blog/the-qt-framework-solid-fun-in-many-languages/",{},"/blog/the-qt-framework-solid-fun-in-many-languages",{"title":42056,"description":42065},"blog/the-qt-framework-solid-fun-in-many-languages",[27189,42399,15201,18496,42400,42401,42402],"cross-platform","qml","qt","qt-quick","Particularly to people using C++ and Python the Qt framework is probably quite well-known, as in these communities it’s one of the most-used frameworks for application development. For those who…","e-Q0beFM2VQtAqQeFrZwi6eVYX9O4YrHWce-mWOtHA4",{"id":42406,"title":42407,"author":42408,"body":42409,"category":43354,"date":43355,"description":42416,"extension":1034,"link":43356,"meta":43357,"navigation":916,"path":43358,"seo":43359,"slug":43360,"stem":43361,"tags":43362,"teaser":43364,"__hash__":43365},"blog/blog/spock-test-well-and-prosper-from-the-unit-up-to-the-integration-level.md","Spock: Testing from the Unit up to the Integration Level",[247],{"type":432,"value":42410,"toc":43352},[42411,42414,42417,42431,42444,42447,42450,42453,42456,42461,42484,42489,42582,42587,42653,42656,42661,42708,42711,42716,42848,42851,42866,42869,42874,43000,43005,43079,43100,43106,43113,43117,43131,43137,43141,43216,43221,43311,43322,43325,43328,43335,43342,43350],[435,42412,42407],{"id":42413},"spock-testing-from-the-unit-up-to-the-integration-level",[439,42415,42416],{},"There are a number of reasons to use the Spock testing framework:",[439,42418,42419,42420,42423,42424,18175,42427,42430],{},"First, tests – ",[990,42421,42422],{},"specifications"," in Spock speak – written in Spock are well structured, expressive and therefore provide\ngood readability. In addition, Spock has built-in features like ",[990,42425,42426],{},"data driven testing",[990,42428,42429],{},"interaction based testing"," (\nmocking). Data driven testing allows your test code to be reused, i.e. to be applied multiple times with different\nparameters.",[439,42432,42433,42434,42437,42438,42443],{},"Second, because Spock is a Groovy based DSL, specification code can become concise where the equivalent Java code would\nbe overly verbose. For example, Groovy provides native syntax for maps, lists and regular expressions. ",[990,42435,42436],{},"Closure\ncoercion"," helps providing stub implementations for one or more interface methods without having to write a stub class.\nAs Groovy and Java can freely be mixed together you can use any Java based library you like, or use Groovy based\nlibraries. For example\nthe ",[1002,42439,42442],{"href":42440,"rel":42441},"https://web.archive.org/web/20150313003201/http://groovy.codehaus.org:80/modules/http-builder/home.html",[1006],"HTTPBuilder","\nenhances the HttpComponents HttpClient by providing features like various builders & parsers and a streamlined REST\nclient.",[439,42445,42446],{},"Also the Spring framework supports Groovy and – not surprisingly – Spring TestContext framework works well with Spock:\napplication contexts can easily be made available to specifications via annotation, thus enabling integration testing at\nall levels.",[439,42448,42449],{},"Spock specifications can be run from an IDE just like normal JUnit tests and, last but not least, implementing them is a\ngreat opportunity to learn the Groovy language.",[439,42451,42452],{},"For demonstration purposes we’ll create a very simple Spring Boot web application that responds with string “prime” or\n“not prime” dependant on a number given by a request parameter. In case of errors the string “error” should be sent back\nto the client. Then we’ll create Spock specifications, both for unit and integration testing.",[439,42454,42455],{},"We start by defining a service interface, its implementation and a controller class:",[439,42457,42458],{},[471,42459,42460],{},"src/main/java/prime/service/PrimeService.java",[464,42462,42464],{"className":709,"code":42463,"language":711,"meta":469,"style":469},"\npublic interface PrimeService {\n boolean isPrime(int number);\n}\n\n",[471,42465,42466,42470,42475,42480],{"__ignoreMap":469},[474,42467,42468],{"class":476,"line":477},[474,42469,917],{"emptyLinePlaceholder":916},[474,42471,42472],{"class":476,"line":507},[474,42473,42474],{},"public interface PrimeService {\n",[474,42476,42477],{"class":476,"line":547},[474,42478,42479],{}," boolean isPrime(int number);\n",[474,42481,42482],{"class":476,"line":584},[474,42483,703],{},[439,42485,42486],{},[471,42487,42488],{},"src/main/java/prime/service/PrimeServiceImpl.java",[464,42490,42492],{"className":709,"code":42491,"language":711,"meta":469,"style":469},"\n@Service\npublic class PrimeServiceImpl implements PrimeService {\n @Override\n public boolean isPrime(int number) {\n if (number \u003C 0) {\n throw new IllegalArgumentException(\"argument must not be negative\");\n }\n if (number \u003C= 2) {\n return number == 2 ? true : false;\n }\n for (int i = 2; i \u003C Math.sqrt(number) + 1; i++) {\n if (number % i == 0) {\n return false;\n }\n }\n return true;\n }\n}\n\n",[471,42493,42494,42498,42503,42508,42512,42517,42522,42527,42531,42536,42541,42545,42550,42555,42560,42565,42569,42574,42578],{"__ignoreMap":469},[474,42495,42496],{"class":476,"line":477},[474,42497,917],{"emptyLinePlaceholder":916},[474,42499,42500],{"class":476,"line":507},[474,42501,42502],{},"@Service\n",[474,42504,42505],{"class":476,"line":547},[474,42506,42507],{},"public class PrimeServiceImpl implements PrimeService {\n",[474,42509,42510],{"class":476,"line":584},[474,42511,21043],{},[474,42513,42514],{"class":476,"line":607},[474,42515,42516],{}," public boolean isPrime(int number) {\n",[474,42518,42519],{"class":476,"line":642},[474,42520,42521],{}," if (number \u003C 0) {\n",[474,42523,42524],{"class":476,"line":663},[474,42525,42526],{}," throw new IllegalArgumentException(\"argument must not be negative\");\n",[474,42528,42529],{"class":476,"line":694},[474,42530,5704],{},[474,42532,42533],{"class":476,"line":700},[474,42534,42535],{}," if (number \u003C= 2) {\n",[474,42537,42538],{"class":476,"line":913},[474,42539,42540],{}," return number == 2 ? true : false;\n",[474,42542,42543],{"class":476,"line":920},[474,42544,5704],{},[474,42546,42547],{"class":476,"line":926},[474,42548,42549],{}," for (int i = 2; i \u003C Math.sqrt(number) + 1; i++) {\n",[474,42551,42552],{"class":476,"line":932},[474,42553,42554],{}," if (number % i == 0) {\n",[474,42556,42557],{"class":476,"line":938},[474,42558,42559],{}," return false;\n",[474,42561,42562],{"class":476,"line":944},[474,42563,42564],{}," }\n",[474,42566,42567],{"class":476,"line":950},[474,42568,5704],{},[474,42570,42571],{"class":476,"line":956},[474,42572,42573],{}," return true;\n",[474,42575,42576],{"class":476,"line":962},[474,42577,1276],{},[474,42579,42580],{"class":476,"line":4876},[474,42581,703],{},[439,42583,42584],{},[471,42585,42586],{},"src/main/groovy/prime/web/PrimeController.groovy",[464,42588,42590],{"className":709,"code":42589,"language":711,"meta":469,"style":469},"\n@RestController\nclass PrimeController {\n @Autowired PrimeService primeService;\n @ExceptionHandler(Exception)\n String handleError() {\n 'error';\n }\n @RequestMapping('/prime')\n String isPrime(@RequestParam int n) {\n primeService.isPrime(n) ? 'prime' : 'not prime';\n }\n}\n\n",[471,42591,42592,42596,42601,42606,42611,42616,42621,42626,42630,42635,42640,42645,42649],{"__ignoreMap":469},[474,42593,42594],{"class":476,"line":477},[474,42595,917],{"emptyLinePlaceholder":916},[474,42597,42598],{"class":476,"line":507},[474,42599,42600],{},"@RestController\n",[474,42602,42603],{"class":476,"line":547},[474,42604,42605],{},"class PrimeController {\n",[474,42607,42608],{"class":476,"line":584},[474,42609,42610],{}," @Autowired PrimeService primeService;\n",[474,42612,42613],{"class":476,"line":607},[474,42614,42615],{}," @ExceptionHandler(Exception)\n",[474,42617,42618],{"class":476,"line":642},[474,42619,42620],{}," String handleError() {\n",[474,42622,42623],{"class":476,"line":663},[474,42624,42625],{}," 'error';\n",[474,42627,42628],{"class":476,"line":694},[474,42629,1276],{},[474,42631,42632],{"class":476,"line":700},[474,42633,42634],{}," @RequestMapping('/prime')\n",[474,42636,42637],{"class":476,"line":913},[474,42638,42639],{}," String isPrime(@RequestParam int n) {\n",[474,42641,42642],{"class":476,"line":920},[474,42643,42644],{}," primeService.isPrime(n) ? 'prime' : 'not prime';\n",[474,42646,42647],{"class":476,"line":926},[474,42648,1276],{},[474,42650,42651],{"class":476,"line":932},[474,42652,703],{},[439,42654,42655],{},"Since the application is based on Spring Boot we also add this class...",[439,42657,42658],{},[471,42659,42660],{},"src/main/java/prime/Application.java",[464,42662,42664],{"className":709,"code":42663,"language":711,"meta":469,"style":469},"\n@Configuration\n@EnableAutoConfiguration\n@ComponentScan\npublic class Application {\n public static void main(String[] args) {\n SpringApplication.run(Application.class, args);\n }\n}\n\n",[471,42665,42666,42670,42675,42680,42685,42690,42695,42700,42704],{"__ignoreMap":469},[474,42667,42668],{"class":476,"line":477},[474,42669,917],{"emptyLinePlaceholder":916},[474,42671,42672],{"class":476,"line":507},[474,42673,42674],{},"@Configuration\n",[474,42676,42677],{"class":476,"line":547},[474,42678,42679],{},"@EnableAutoConfiguration\n",[474,42681,42682],{"class":476,"line":584},[474,42683,42684],{},"@ComponentScan\n",[474,42686,42687],{"class":476,"line":607},[474,42688,42689],{},"public class Application {\n",[474,42691,42692],{"class":476,"line":642},[474,42693,42694],{}," public static void main(String[] args) {\n",[474,42696,42697],{"class":476,"line":663},[474,42698,42699],{}," SpringApplication.run(Application.class, args);\n",[474,42701,42702],{"class":476,"line":694},[474,42703,15319],{},[474,42705,42706],{"class":476,"line":700},[474,42707,703],{},[439,42709,42710],{},"... and a build script:",[439,42712,42713],{},[471,42714,42715],{},"src/build.gradle",[464,42717,42719],{"className":709,"code":42718,"language":711,"meta":469,"style":469},"\nbuildscript {\n repositories {\n mavenCentral()\n }\n dependencies {\n classpath(\"org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE\")\n }\n}\napply plugin: 'groovy'\napply plugin: 'spring-boot'\njar {\n baseName = 'prime'\n version = '0.10.0'\n}\nrepositories {\n mavenCentral()\n}\ndependencies {\n compile(\"org.codehaus.groovy:groovy-all:2.3.6\")\n compile(\"org.springframework.boot:spring-boot-starter-jetty\")\n compile(\"org.springframework.boot:spring-boot-starter-web\") {\n exclude module: \"spring-boot-starter-tomcat\"\n }\n testCompile(\"org.springframework.boot:spring-boot-starter-test\")\n testCompile(\"org.spockframework:spock-core:0.7-groovy-2.0\")\n}\n\n",[471,42720,42721,42725,42730,42735,42740,42744,42749,42754,42758,42762,42767,42772,42777,42782,42787,42791,42796,42801,42805,42810,42815,42820,42825,42830,42834,42839,42844],{"__ignoreMap":469},[474,42722,42723],{"class":476,"line":477},[474,42724,917],{"emptyLinePlaceholder":916},[474,42726,42727],{"class":476,"line":507},[474,42728,42729],{},"buildscript {\n",[474,42731,42732],{"class":476,"line":547},[474,42733,42734],{}," repositories {\n",[474,42736,42737],{"class":476,"line":584},[474,42738,42739],{}," mavenCentral()\n",[474,42741,42742],{"class":476,"line":607},[474,42743,15319],{},[474,42745,42746],{"class":476,"line":642},[474,42747,42748],{}," dependencies {\n",[474,42750,42751],{"class":476,"line":663},[474,42752,42753],{}," classpath(\"org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE\")\n",[474,42755,42756],{"class":476,"line":694},[474,42757,15319],{},[474,42759,42760],{"class":476,"line":700},[474,42761,703],{},[474,42763,42764],{"class":476,"line":913},[474,42765,42766],{},"apply plugin: 'groovy'\n",[474,42768,42769],{"class":476,"line":920},[474,42770,42771],{},"apply plugin: 'spring-boot'\n",[474,42773,42774],{"class":476,"line":926},[474,42775,42776],{},"jar {\n",[474,42778,42779],{"class":476,"line":932},[474,42780,42781],{}," baseName = 'prime'\n",[474,42783,42784],{"class":476,"line":938},[474,42785,42786],{}," version = '0.10.0'\n",[474,42788,42789],{"class":476,"line":944},[474,42790,703],{},[474,42792,42793],{"class":476,"line":950},[474,42794,42795],{},"repositories {\n",[474,42797,42798],{"class":476,"line":956},[474,42799,42800],{}," mavenCentral()\n",[474,42802,42803],{"class":476,"line":962},[474,42804,703],{},[474,42806,42807],{"class":476,"line":4876},[474,42808,42809],{},"dependencies {\n",[474,42811,42812],{"class":476,"line":4888},[474,42813,42814],{}," compile(\"org.codehaus.groovy:groovy-all:2.3.6\")\n",[474,42816,42817],{"class":476,"line":4900},[474,42818,42819],{}," compile(\"org.springframework.boot:spring-boot-starter-jetty\")\n",[474,42821,42822],{"class":476,"line":4913},[474,42823,42824],{}," compile(\"org.springframework.boot:spring-boot-starter-web\") {\n",[474,42826,42827],{"class":476,"line":4921},[474,42828,42829],{}," exclude module: \"spring-boot-starter-tomcat\"\n",[474,42831,42832],{"class":476,"line":4932},[474,42833,15319],{},[474,42835,42836],{"class":476,"line":4938},[474,42837,42838],{}," testCompile(\"org.springframework.boot:spring-boot-starter-test\")\n",[474,42840,42841],{"class":476,"line":4946},[474,42842,42843],{}," testCompile(\"org.spockframework:spock-core:0.7-groovy-2.0\")\n",[474,42845,42846],{"class":476,"line":4952},[474,42847,703],{},[439,42849,42850],{},"The Groovy plugin handles mixed Groovy and Java code in the project. Not only is our controller class written in Groovy;\nthe specifications for unit and integration testing will be too.",[439,42852,42853,42854,42857,42858,42861,42862,42865],{},"If Groovy is used in production code we have to include the ",[448,42855,42856],{},"groovy-all"," dependency to the ",[471,42859,42860],{},"compile"," configuration,\notherwise this dependency should be added to the ",[471,42863,42864],{},"testCompile"," configuration.",[439,42867,42868],{},"Now we write unit specifications which verify the correctness of service implementation and controller:",[439,42870,42871],{},[471,42872,42873],{},"src/test/groovy/prime/service/PrimeServiceImplSpec.groovy",[464,42875,42877],{"className":709,"code":42876,"language":711,"meta":469,"style":469},"\nclass PrimeServiceImplSpec extends Specification {\n PrimeServiceImpl sut = new PrimeServiceImpl();\n def \"test if the given number is prime\"() {\n expect:\n sut.isPrime(n) == prime\n where:\n n | prime\n 0 | false\n 1 | false\n 2 | true\n 3 | true\n 4 | false\n 5 | true\n 6 | false\n 7 | true\n }\n def \"check method argument constraints\"() {\n when:\n sut.isPrime(-1)\n then:\n def e = thrown(IllegalArgumentException)\n e.message == 'argument must not be negative'\n }\n}\n\n",[471,42878,42879,42883,42888,42893,42898,42903,42908,42913,42918,42923,42928,42933,42938,42943,42948,42953,42958,42962,42967,42972,42977,42982,42987,42992,42996],{"__ignoreMap":469},[474,42880,42881],{"class":476,"line":477},[474,42882,917],{"emptyLinePlaceholder":916},[474,42884,42885],{"class":476,"line":507},[474,42886,42887],{},"class PrimeServiceImplSpec extends Specification {\n",[474,42889,42890],{"class":476,"line":547},[474,42891,42892],{}," PrimeServiceImpl sut = new PrimeServiceImpl();\n",[474,42894,42895],{"class":476,"line":584},[474,42896,42897],{}," def \"test if the given number is prime\"() {\n",[474,42899,42900],{"class":476,"line":607},[474,42901,42902],{}," expect:\n",[474,42904,42905],{"class":476,"line":642},[474,42906,42907],{}," sut.isPrime(n) == prime\n",[474,42909,42910],{"class":476,"line":663},[474,42911,42912],{}," where:\n",[474,42914,42915],{"class":476,"line":694},[474,42916,42917],{}," n | prime\n",[474,42919,42920],{"class":476,"line":700},[474,42921,42922],{}," 0 | false\n",[474,42924,42925],{"class":476,"line":913},[474,42926,42927],{}," 1 | false\n",[474,42929,42930],{"class":476,"line":920},[474,42931,42932],{}," 2 | true\n",[474,42934,42935],{"class":476,"line":926},[474,42936,42937],{}," 3 | true\n",[474,42939,42940],{"class":476,"line":932},[474,42941,42942],{}," 4 | false\n",[474,42944,42945],{"class":476,"line":938},[474,42946,42947],{}," 5 | true\n",[474,42949,42950],{"class":476,"line":944},[474,42951,42952],{}," 6 | false\n",[474,42954,42955],{"class":476,"line":950},[474,42956,42957],{}," 7 | true\n",[474,42959,42960],{"class":476,"line":956},[474,42961,1276],{},[474,42963,42964],{"class":476,"line":962},[474,42965,42966],{}," def \"check method argument constraints\"() {\n",[474,42968,42969],{"class":476,"line":4876},[474,42970,42971],{}," when:\n",[474,42973,42974],{"class":476,"line":4888},[474,42975,42976],{}," sut.isPrime(-1)\n",[474,42978,42979],{"class":476,"line":4900},[474,42980,42981],{}," then:\n",[474,42983,42984],{"class":476,"line":4913},[474,42985,42986],{}," def e = thrown(IllegalArgumentException)\n",[474,42988,42989],{"class":476,"line":4921},[474,42990,42991],{}," e.message == 'argument must not be negative'\n",[474,42993,42994],{"class":476,"line":4932},[474,42995,1276],{},[474,42997,42998],{"class":476,"line":4938},[474,42999,703],{},[439,43001,43002],{},[471,43003,43004],{},"src/test/groovy/prime/web/PrimeControllerSpec.groovy",[464,43006,43008],{"className":709,"code":43007,"language":711,"meta":469,"style":469},"\nclass PrimeControllerSpec extends Specification {\n def \"returns string 'prime' when service detects prime number\"() {\n int p = 3\n def stub = { it == p ? true : false }\n expect:\n new PrimeController(primeService: stub).isPrime(p) == 'prime'\n }\n def \"returns 'not prime' when service detects non-prime number\"() {\n int n = 4\n def stub = { it == n ? false : true }\n expect:\n new PrimeController(primeService: stub).isPrime(n) == 'not prime'\n }\n}\n\n",[471,43009,43010,43014,43019,43024,43029,43034,43038,43043,43047,43052,43057,43062,43066,43071,43075],{"__ignoreMap":469},[474,43011,43012],{"class":476,"line":477},[474,43013,917],{"emptyLinePlaceholder":916},[474,43015,43016],{"class":476,"line":507},[474,43017,43018],{},"class PrimeControllerSpec extends Specification {\n",[474,43020,43021],{"class":476,"line":547},[474,43022,43023],{}," def \"returns string 'prime' when service detects prime number\"() {\n",[474,43025,43026],{"class":476,"line":584},[474,43027,43028],{}," int p = 3\n",[474,43030,43031],{"class":476,"line":607},[474,43032,43033],{}," def stub = { it == p ? true : false }\n",[474,43035,43036],{"class":476,"line":642},[474,43037,42902],{},[474,43039,43040],{"class":476,"line":663},[474,43041,43042],{}," new PrimeController(primeService: stub).isPrime(p) == 'prime'\n",[474,43044,43045],{"class":476,"line":694},[474,43046,1276],{},[474,43048,43049],{"class":476,"line":700},[474,43050,43051],{}," def \"returns 'not prime' when service detects non-prime number\"() {\n",[474,43053,43054],{"class":476,"line":913},[474,43055,43056],{}," int n = 4\n",[474,43058,43059],{"class":476,"line":920},[474,43060,43061],{}," def stub = { it == n ? false : true }\n",[474,43063,43064],{"class":476,"line":926},[474,43065,42902],{},[474,43067,43068],{"class":476,"line":932},[474,43069,43070],{}," new PrimeController(primeService: stub).isPrime(n) == 'not prime'\n",[474,43072,43073],{"class":476,"line":938},[474,43074,1276],{},[474,43076,43077],{"class":476,"line":944},[474,43078,703],{},[439,43080,43081,43082,43085,43086,43089,43090,43092,43093,43096,43097,1402],{},"The first ",[990,43083,43084],{},"feature"," method in ",[471,43087,43088],{},"PrimeServiceImplSpec"," shows how Spocks ",[990,43091,42426],{}," concept works and in\n",[471,43094,43095],{},"PrimeControllerSpec"," we create service stubs by ",[990,43098,43099],{},"closure coercion",[439,43101,43102,43103,43105],{},"Spock does also provide a means for ",[990,43104,42429],{},", i.e. mocking and stubbing.",[439,43107,43108,43109,43112],{},"Before we implement an integration specification to verify the applications behaviour, we have to add another dependency\nin the build script. The ",[448,43110,43111],{},"spock-spring"," dependency enables to use the Spring TestContext framework together with\nSpock which is required for our integration specification.",[439,43114,43115],{},[471,43116,42715],{},[464,43118,43120],{"className":709,"code":43119,"language":711,"meta":469,"style":469},"\ntestCompile(\"org.spockframework:spock-spring:0.7-groovy-2.0\")\n\n",[471,43121,43122,43126],{"__ignoreMap":469},[474,43123,43124],{"class":476,"line":477},[474,43125,917],{"emptyLinePlaceholder":916},[474,43127,43128],{"class":476,"line":507},[474,43129,43130],{},"testCompile(\"org.spockframework:spock-spring:0.7-groovy-2.0\")\n",[439,43132,43133,43134],{},"In order to separate the long running integration specifications from the unit specifications, we modify the build\nscript by defining a corresponding sourceSet, associated configurations and task. Integration testing can now be\ntriggered with ",[471,43135,43136],{},"gradle integTest",[439,43138,43139],{},[471,43140,42715],{},[464,43142,43144],{"className":709,"code":43143,"language":711,"meta":469,"style":469},"\nsourceSets {\n integTest {\n compileClasspath += main.output + test.output\n runtimeClasspath += main.output + test.output\n }\n}\nconfigurations {\n integTestCompile.extendsFrom testCompile\n integTestRuntime.extendsFrom testRuntime\n}\ntask integTest(type: Test) {\n testClassesDir = sourceSets.integTest.output.classesDir\n classpath = sourceSets.integTest.runtimeClasspath\n}\n\n",[471,43145,43146,43150,43155,43160,43165,43170,43174,43178,43183,43188,43193,43197,43202,43207,43212],{"__ignoreMap":469},[474,43147,43148],{"class":476,"line":477},[474,43149,917],{"emptyLinePlaceholder":916},[474,43151,43152],{"class":476,"line":507},[474,43153,43154],{},"sourceSets {\n",[474,43156,43157],{"class":476,"line":547},[474,43158,43159],{}," integTest {\n",[474,43161,43162],{"class":476,"line":584},[474,43163,43164],{}," compileClasspath += main.output + test.output\n",[474,43166,43167],{"class":476,"line":607},[474,43168,43169],{}," runtimeClasspath += main.output + test.output\n",[474,43171,43172],{"class":476,"line":642},[474,43173,1276],{},[474,43175,43176],{"class":476,"line":663},[474,43177,703],{},[474,43179,43180],{"class":476,"line":694},[474,43181,43182],{},"configurations {\n",[474,43184,43185],{"class":476,"line":700},[474,43186,43187],{}," integTestCompile.extendsFrom testCompile\n",[474,43189,43190],{"class":476,"line":913},[474,43191,43192],{}," integTestRuntime.extendsFrom testRuntime\n",[474,43194,43195],{"class":476,"line":920},[474,43196,703],{},[474,43198,43199],{"class":476,"line":926},[474,43200,43201],{},"task integTest(type: Test) {\n",[474,43203,43204],{"class":476,"line":932},[474,43205,43206],{}," testClassesDir = sourceSets.integTest.output.classesDir\n",[474,43208,43209],{"class":476,"line":938},[474,43210,43211],{}," classpath = sourceSets.integTest.runtimeClasspath\n",[474,43213,43214],{"class":476,"line":944},[474,43215,703],{},[439,43217,43218],{},[471,43219,43220],{},"src/integTest/groovy/prime/PrimeSpec.groovy",[464,43222,43224],{"className":709,"code":43223,"language":711,"meta":469,"style":469},"\n@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)\n@WebAppConfiguration\n@IntegrationTest\nclass PrimeSpec extends Specification {\n @Value('${local.server.port}')\n int port;\n def \"server answers with 'prime' or 'not prime' or 'error'\"() {\n expect:\n \"http://localhost:$port/prime?n=$n\"\n .toURL().text == response\n where:\n n | response\n 23 | 'prime'\n 42 | 'not prime'\n -1 | 'error'\n }\n}\n\n",[471,43225,43226,43230,43235,43240,43245,43250,43255,43260,43265,43269,43274,43279,43283,43288,43293,43298,43303,43307],{"__ignoreMap":469},[474,43227,43228],{"class":476,"line":477},[474,43229,917],{"emptyLinePlaceholder":916},[474,43231,43232],{"class":476,"line":507},[474,43233,43234],{},"@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)\n",[474,43236,43237],{"class":476,"line":547},[474,43238,43239],{},"@WebAppConfiguration\n",[474,43241,43242],{"class":476,"line":584},[474,43243,43244],{},"@IntegrationTest\n",[474,43246,43247],{"class":476,"line":607},[474,43248,43249],{},"class PrimeSpec extends Specification {\n",[474,43251,43252],{"class":476,"line":642},[474,43253,43254],{}," @Value('${local.server.port}')\n",[474,43256,43257],{"class":476,"line":663},[474,43258,43259],{}," int port;\n",[474,43261,43262],{"class":476,"line":694},[474,43263,43264],{}," def \"server answers with 'prime' or 'not prime' or 'error'\"() {\n",[474,43266,43267],{"class":476,"line":700},[474,43268,42902],{},[474,43270,43271],{"class":476,"line":913},[474,43272,43273],{}," \"http://localhost:$port/prime?n=$n\"\n",[474,43275,43276],{"class":476,"line":920},[474,43277,43278],{}," .toURL().text == response\n",[474,43280,43281],{"class":476,"line":926},[474,43282,42912],{},[474,43284,43285],{"class":476,"line":932},[474,43286,43287],{}," n | response\n",[474,43289,43290],{"class":476,"line":938},[474,43291,43292],{}," 23 | 'prime'\n",[474,43294,43295],{"class":476,"line":944},[474,43296,43297],{}," 42 | 'not prime'\n",[474,43299,43300],{"class":476,"line":950},[474,43301,43302],{}," -1 | 'error'\n",[474,43304,43305],{"class":476,"line":956},[474,43306,1276],{},[474,43308,43309],{"class":476,"line":962},[474,43310,703],{},[439,43312,43313,43314,43317,43318,43321],{},"In ",[471,43315,43316],{},"PrimeSpec"," the Spring Boot annotation ",[471,43319,43320],{},"@IntegrationTest"," causes the embedded application server to start. As an\nalternative we could use MockMvc to verify application response. Integration testing with MockMvc doesn't require a\nrunning application server.",[439,43323,43324],{},"To summarize, the Spock testing famework is a good example how Groovy can help Java developers. By writing Spock\nspecifications, your test code - whether on the unit oder the integration level - can become concise and expressive.\nIntegration into the build process is easy and your favorite IDE will handle specifications just like regular JUnit\ntests.",[439,43326,43327],{},"Links:",[439,43329,43330],{},[1002,43331,43334],{"href":43332,"rel":43333},"https://code.google.com/p/spock/wiki/SpockBasics/",[1006],"SpockBasics - Anatomy of a Spock specification",[439,43336,43337],{},[1002,43338,43341],{"href":43339,"rel":43340},"http://spock-framework.readthedocs.org/en/latest/",[1006],"Spock Framework Reference Documentation",[439,43343,43344,43349],{},[1002,43345,43348],{"href":43346,"rel":43347},"http://de.slideshare.net/kousen/spock-friendly-testing",[1006],"Spock: Test Well and Prosper"," by Ken Kousen",[1024,43351,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":43353},[],[1030],"2014-09-15T20:19:31","https://synyx.de/blog/spock-test-well-and-prosper-from-the-unit-up-to-the-integration-level/",{},"/blog/spock-test-well-and-prosper-from-the-unit-up-to-the-integration-level",{"title":42407,"description":42416},"spock-test-well-and-prosper-from-the-unit-up-to-the-integration-level","blog/spock-test-well-and-prosper-from-the-unit-up-to-the-integration-level",[28458,43363,1426,18497],"spock","There are a number of reasons to use the Spock testing framework: First, tests – specifications in Spock speak – written in Spock are well structured, expressive and therefore provide…","PUbBjgfCyqC5t0kqqAl71ChX_YmSeT1Y0stRAuBBZHo",{"id":43367,"title":43368,"author":43369,"body":43370,"category":43510,"date":43511,"description":43512,"extension":1034,"link":43513,"meta":43514,"navigation":916,"path":43515,"seo":43516,"slug":43374,"stem":43518,"tags":43519,"teaser":43520,"__hash__":43521},"blog/blog/coming-up-soon-the-new-synyx-headquarters.md","Coming up soon – the new synyx headquarters!",[178],{"type":432,"value":43371,"toc":43508},[43372,43375,43384,43394,43397,43407,43410,43420,43422,43432,43434,43444,43447,43457,43459,43469,43471,43480,43482,43492,43495,43505],[435,43373,43368],{"id":43374},"coming-up-soon-the-new-synyx-headquarters",[439,43376,43377,43378,43383],{},"As ",[1002,43379,43382],{"href":43380,"rel":43381},"http://blog.synyx.de/2014/02/planning-and-designing-your-it-infrastructure-part-i-networking-2/",[1006],"i told you in an earlier blogpost",",\nwe are getting ready to move into our new offices in a few weeks. So, the last few months were pretty busy for us –\nstarting from scratch with an emply floor, we had to come up with a floor plan that would fit our office culture, design\nour infrastructure layout, comparing different hardware and vendors, obtain offers from several contractors, keep track\non schedules and deadlines and frequently visit the construction site to check if everything will be build as we planned\nit.",[439,43385,43386,43393],{},[1002,43387,43390],{"href":43388,"rel":43389},"https://media.synyx.de/uploads//2014/09/Synyx_HQ_new.jpg",[1006],[2205,43391],{"alt":469,"src":43392},"https://media.synyx.de/uploads//2014/09/Synyx_HQ_new-1024x329.jpg","\nNow, as we hit the home stretch, it feels good to finally be able to see how all things come together and how the\nconstruction site finally starts to look like an office! There is still quite a bit of fine tuning and detailing left\nbut we’ve come a long way from what basically started as a concrete shell when we decided to move in. I find it very\nsatisfying to finally see how the last 16 months of planning, descision making and stress turns into what will soon be\nour new synyx Headquarters!",[439,43395,43396],{},"To give you an idea of our new work environment, we present you a sneak peak into our new office!",[439,43398,43399],{},[1002,43400,43403],{"href":43401,"rel":43402},"https://media.synyx.de/uploads//2014/09/Office_0.jpg",[1006],[2205,43404],{"alt":43405,"src":43406},"This will be the main office - this is where the synyx magic happens!","https://media.synyx.de/uploads//2014/09/Office_0-1024x768.jpg",[439,43408,43409],{},"This will be the main office – this is where the synyx magic happens!",[439,43411,43412],{},[1002,43413,43416],{"href":43414,"rel":43415},"https://media.synyx.de/uploads//2014/09/Office_1.jpg",[1006],[2205,43417],{"alt":43418,"src":43419},"Starting to install a/c, wiring, supporting frames and all the other nice little things that make an office liveable!","https://media.synyx.de/uploads//2014/09/Office_1-1024x384.jpg",[439,43421,43418],{},[439,43423,43424],{},[1002,43425,43428],{"href":43426,"rel":43427},"https://media.synyx.de/uploads//2014/09/Office_2.jpg",[1006],[2205,43429],{"alt":43430,"src":43431},"Lots of network cabling! And i mean LOTS of it!","https://media.synyx.de/uploads//2014/09/Office_2-1024x429.jpg",[439,43433,43430],{},[439,43435,43436],{},[1002,43437,43440],{"href":43438,"rel":43439},"https://media.synyx.de/uploads//2014/09/Office_4.jpg",[1006],[2205,43441],{"alt":43442,"src":43443},"As of today - it is almost finished! ","https://media.synyx.de/uploads//2014/09/Office_4-1024x337.jpg",[439,43445,43446],{},"As of today – it is almost finished!",[439,43448,43449],{},[1002,43450,43453],{"href":43451,"rel":43452},"https://media.synyx.de/uploads//2014/09/Rooms_1.jpg",[1006],[2205,43454],{"alt":43455,"src":43456},"So much room for activities!","https://media.synyx.de/uploads//2014/09/Rooms_1-1024x768.jpg",[439,43458,43455],{},[439,43460,43461],{},[1002,43462,43465],{"href":43463,"rel":43464},"https://media.synyx.de/uploads//2014/09/Rooms_21.jpg",[1006],[2205,43466],{"alt":43467,"src":43468},"Drywalls, a/c, electrical installation completed.","https://media.synyx.de/uploads//2014/09/Rooms_21-1024x807.jpg",[439,43470,43467],{},[439,43472,43473],{},[1002,43474,43477],{"href":43475,"rel":43476},"https://media.synyx.de/uploads//2014/09/Rooms_3.jpg",[1006],[2205,43478],{"alt":43479,"src":43475},"Throwing in some more network!",[439,43481,43479],{},[439,43483,43484],{},[1002,43485,43488],{"href":43486,"rel":43487},"https://media.synyx.de/uploads//2014/09/Rooms_4.jpg",[1006],[2205,43489],{"alt":43490,"src":43491},"The smaller office rooms are also almost finished. Looking good, doesn't it?","https://media.synyx.de/uploads//2014/09/Rooms_4-1024x483.jpg",[439,43493,43494],{},"The smaller office rooms are also almost finished. Looking good, doesn’t it?",[439,43496,43497],{},[1002,43498,43501],{"href":43499,"rel":43500},"https://media.synyx.de/uploads//2014/09/20140826_162336.jpg",[1006],[2205,43502],{"alt":43503,"src":43504},"We are ready to move in - can't wait!","https://media.synyx.de/uploads//2014/09/20140826_162336-1024x576.jpg",[439,43506,43507],{},"We are ready to move in – can’t wait!",{"title":469,"searchDepth":507,"depth":507,"links":43509},[],[9045],"2014-09-09T15:00:34","As i told you in an earlier blogpost,\\nwe are getting ready to move into our new offices in a few weeks. So, the last few months were pretty busy for us –\\nstarting from scratch with an emply floor, we had to come up with a floor plan that would fit our office culture, design\\nour infrastructure layout, comparing different hardware and vendors, obtain offers from several contractors, keep track\\non schedules and deadlines and frequently visit the construction site to check if everything will be build as we planned\\nit.","https://synyx.de/blog/coming-up-soon-the-new-synyx-headquarters/",{},"/blog/coming-up-soon-the-new-synyx-headquarters",{"title":43368,"description":43517},"As i told you in an earlier blogpost,\nwe are getting ready to move into our new offices in a few weeks. So, the last few months were pretty busy for us –\nstarting from scratch with an emply floor, we had to come up with a floor plan that would fit our office culture, design\nour infrastructure layout, comparing different hardware and vendors, obtain offers from several contractors, keep track\non schedules and deadlines and frequently visit the construction site to check if everything will be build as we planned\nit.","blog/coming-up-soon-the-new-synyx-headquarters",[],"As i told you in an earlier blogpost, we are getting ready to move into our new offices in a few weeks. So, the last few months were pretty busy…","2aYDfZe8U-Nh0lSeFvZHlGSksQxoVDI9vrhE7SySEBw",{"id":43523,"title":43524,"author":43525,"body":43526,"category":43629,"date":43630,"description":469,"extension":1034,"link":43631,"meta":43632,"navigation":916,"path":43633,"seo":43634,"slug":43530,"stem":43635,"tags":43636,"teaser":43642,"__hash__":43643},"blog/blog/code-reviews.md","Code-Reviews",[166],{"type":432,"value":43527,"toc":43624},[43528,43531,43537,43546,43549,43553,43556,43567,43577,43586,43589,43592,43596,43599,43602,43611,43621],[435,43529,43524],{"id":43530},"code-reviews",[1065,43532,43534],{"id":43533},"zeig-mir-deinen-code-und-ich-sage-dir-wer-du-bist",[990,43535,43536],{},"Zeig’ mir Deinen Code und ich sage Dir wer Du bist.",[439,43538,43539,43540,1402],{},"Oftmals kommen Unternehmen mit der Bitte um einen Code-Review auf uns zu. Gründe dafür gibt es viele, jedoch dreht es\nsich meistens um schlechte Erweiter- und Wartbarkeit der Software und in der Konsequenz um eine langsame\nEntwicklungsgeschwindigkeit (höhere Kosten). Häufig sind diese Anwendungen dann neue Patienten für\ndie ",[1002,43541,43545],{"href":43542,"rel":43543,"title":43544},"http://www.synyx.de/leistungen/code_clinic/",[1006],"synyx' code clinic","Code-Clinic",[439,43547,43548],{},"Aus unserer Sicht kann man diese Probleme oft von unten heraus aus dem Code angehen. Ein erster Schritt hierfür ist ein\nexterner Code-Review. Eine (nicht unbedingt gegensätzliche) Alternative wäre ein verwandter und umfänglicher\nArchitektur-Review, aber das ist eine Geschichte für einen anderen Tag.",[1065,43550,43552],{"id":43551},"vorgehensweise","Vorgehensweise",[439,43554,43555],{},"Wenn wir mit einem Kunden einen Code-Review angehen so folgt zunächst – wie bei fast allen unseren\nBeratungsleistungen – ein intensives Gespräch mit dem Auftraggeber. Hierbei werden zentrale Fragestellungen,\nAnforderungen und Ziele geklärt:",[994,43557,43558,43561,43564],{},[997,43559,43560],{},"“Wo drückt der Schuh besonders?”",[997,43562,43563],{},"“Was sind Ihre Erwartungen an den Review?”",[997,43565,43566],{},"“Welche wichtigen Stakeholder sind beteiligt und was sind die Rahmenbedingungen?”",[439,43568,43569,43570,43576],{},"In der Regel sind anschließend noch weitere Interviews und Gespräche nötig. Auch wenn Code oftmals für sich steht,\nkönnen Mitglieder des Entwicklungsteams und deren direktes Umfeld ebenfalls wichtige Hinweise und Einstiegspunkte für\nden Review geben. Außerdem bevorzugen wir im Zuge\nunserer ",[1002,43571,43575],{"href":43572,"rel":43573,"title":43574},"http://www.synyx.de/unternehmen/vision_mission/",[1006],"Vision und Mission","Firmenphilosophie"," ein offenes und\ntransparentes Vorgehen im Gegensatz zu reinen “undercover Aktionen”.",[439,43578,43579,43580,43585],{},"Sobald alle notwendigen Gespräche geführt sind, ziehen wir uns mit dem Quellcode des Projekts zurück und analysieren\nihn. Geeignete Einstiegspunkte kristallisieren sich oft bereits bei den Vorgesprächen heraus. Zusätzlich liefern\nTool-gestützte Analysen (beispielsweise durch ",[1002,43581,21316],{"href":43582,"rel":43583,"title":43584},"http://www.sonarqube.org/",[1006],"SonarQube zur Codeanalyse",")\nsinnvolle Ansatzpunkte. Nicht zuletzt lernt man mit der Zeit, Probleme und Schwachstellen im Quellcode schnell zu\nerkennen.",[439,43587,43588],{},"Je nach Projektumfang werden die gewonnenen Erkenntnisse parallel durch Rückfragen beim Team vertieft und gestärkt und\nerste identifizierte Probleme werden bestätigt oder entkräftet.",[439,43590,43591],{},"Am Ende des Code-Reviews steht die Zusammenfassung der Ergebnisse und deren Präsentation. Je nach Wunsch kann dies\npersönlich (z.B. in Workshops), schriftlich durch ein ausgearbeitetes Review-Dokument oder auch auf andere Arten\ngeschehen. In der Regel werden die identifizierten Themen dargestellt, bewertet und mit Verbesserungsvorschlägen und\nLösungsansätzen versehen. Es versteht sich von selbst, dass wir unsere Kunden auf Wunsch auch bei der Umsetzung dieser\nLösungen unterstützen.",[1065,43593,43595],{"id":43594},"ergebnisse","Ergebnisse",[439,43597,43598],{},"Auch wenn jedes Projekt, jedes Team und jeder Code anders ist, existieren oft sehr ähnliche Probleme.",[439,43600,43601],{},"So treffen wir beispielsweise regelmäßig auf einen inkonsistenten Mix verschiedenster Entwicklungsstile, Formatierungen\nund Bezeichnungen. Dies führt zu schlechter Lesbarkeit und einer unübersichtlichen Codebasis. Neue Entwickler finden\nsich dort nur schwer zurecht und auch auf die Geschwindigkeit von bestehenden Entwicklern hat dies erheblichen Einfluss.",[439,43603,43604,43605,43610],{},"Ein anderes, häufiges Problem ist die schlechte Struktur und Testbarkeit des bestehenden Codes. Diese resultiert oft aus\nder Verletzung verschiedener, grundlegender Prinzipien der Softwareentwicklung (siehe\nauch ",[1002,43606,43609],{"href":43607,"rel":43608},"http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod",[1006],"SOLID",") und führt letztlich zu monolithischen und\naufwändig wartbaren Anwendungen.",[439,43612,43613,43614,43620],{},"Auch die erarbeiteten Lösungsstrategien haben oftmals Gemeinsamkeiten. So können häufig Tools helfen: Eine\nautomatisierte Toolchain von der Formatierung, über alle Arten\nvon ",[1002,43615,43619],{"href":43616,"rel":43617,"title":43618},"http://martinfowler.com/bliki/TestPyramid.html",[1006],"Test Pyramide","Testing"," und Analysen bis hin zum Deployment in\nProduktion kann hier große Wirkung zeigen. Andere Maßnahmen können beispielsweise intensive Workshops oder Schulungen\ndes Teams mit Fokus auf die beim Review identifizierten Aspekte sein.",[439,43622,43623],{},"Code-Reviews sind nicht nur im Nachgang bei Problemen nützlich sondern können bereits vorher zur Überprüfung der\nCode-Qualität als regelmäßiges Quality-Gate zum Einsatz kommen. Wir verwenden dieses Qualitätssicherungsinstrument\nselbst in unseren Projekten und bei unseren Teams. Meist übernimmt ein erfahrener Kollege aus einem anderen Team den\nReview. Dies ist eine Methode, wie wir die Code-Qualität in unseren eigenen Projekten optimieren und stellt eine\nwirkungsvolle Ergänzung von Team-internen Reviews dar.",{"title":469,"searchDepth":507,"depth":507,"links":43625},[43626,43627,43628],{"id":43533,"depth":547,"text":43536},{"id":43551,"depth":547,"text":43552},{"id":43594,"depth":547,"text":43595},[1030],"2014-08-22T10:30:06","https://synyx.de/blog/code-reviews/",{},"/blog/code-reviews",{"title":43524,"description":469},"blog/code-reviews",[471,43637,43638,43639,43640,43641,18497],"code-clinic","code-qualitat","review","solid","sonarqube","Zeig’ mir Deinen Code und ich sage Dir wer Du bist. Oftmals kommen Unternehmen mit der Bitte um einen Code-Review auf uns zu. Gründe dafür gibt es viele, jedoch dreht…","Qsyk1Z8Y7U5TchROIcmFGONn1yQ2F5yLeUoyxJr7GHc",{"id":43645,"title":43646,"author":43647,"body":43648,"category":43806,"date":43807,"description":43657,"extension":1034,"link":43808,"meta":43809,"navigation":916,"path":43810,"seo":43811,"slug":43652,"stem":43812,"tags":43813,"teaser":43815,"__hash__":43816},"blog/blog/javaforum-stuttgart-2014.md","JavaForum Stuttgart 2014",[133],{"type":432,"value":43649,"toc":43804},[43650,43653,43658,43661,43664,43669,43672,43677,43686,43691,43694,43699,43702,43707,43722,43727,43736,43741,43744,43749,43757,43762,43765,43770,43779,43784,43793,43798,43801],[435,43651,43646],{"id":43652},"javaforum-stuttgart-2014",[439,43654,43655],{},[448,43656,43657],{},"Warum in die Ferne schweifen, wenn das Gute doch so nah ist?",[439,43659,43660],{},"Das dachten sich dieses Jahr auch vier Entwickler von synyx, und machten sich daher am 17.07. auf nach Stuttgart zum\nJavaForum der Java User Group Stuttgart (JUGS).",[439,43662,43663],{},"Die Vorteile liegen auf der Hand: kurze An-/ Abreise, große Auswahl an guten Vorträgen, super Verpflegung, und das\nalles zu einem relativ günstigen Preis.",[439,43665,43666],{},[448,43667,43668],{},"08:05 Uhr, Hauptbahnhof Stuttgart",[439,43670,43671],{},"Alle da? Wo müssen wir hin? Raus aus dem Bahnhof, der Masse nach zum Kongresszentrum Liederhalle. Zehn Minuten laufen\nsind bestimmt nicht verkehrt – sitzen werden wir heute noch genug. Der Checkin zum JavaForum funktioniert dank vorher\nzugeschickter Barcode-Scheckkarte schnell und problemlos. Dann erst mal orientieren: Wo gibts was? Kaffee? Ah, da.\nPraktischerweise gibts auch Brezeln – also Zeit für ein zweites Frühstück.",[439,43673,43674],{},[448,43675,43676],{},"8:45 Uhr, Hegel-Saal",[439,43678,43679,43680,43685],{},"Zum Warm-Up: Thorsten Maier\nmit “",[1002,43681,43684],{"href":43682,"rel":43683},"http://www.java-forum-stuttgart.de/_data/D1_2014.pdf",[1006],"Effektiver Einsatz von Code-Reviews","“. Keine Tool-Show (\npuh, Glück gehabt), dafür umso mehr praktische Infos rund um die Reviews. Warum wollen wir überhaupt Reviews machen? Und\nwie führt man diese praktisch durch? Insgesamt sehr praxisnah und einfach umzusetzen. Das werden wir nächste Woche\ngleich mal ausprobieren.",[439,43687,43688],{},[448,43689,43690],{},"9:50 Uhr, Raum Sylt",[439,43692,43693],{},"Nach etwas suchen den Tagungsraum im obersten Stockwerk gefunden. Mit “Leittechnik für Bahnsysteme mit Eclipse” von\nChristian Scholz geht es hier mal um etwas womit man im Entwickleralltag eher nie zu tun hat, es aber fast jeden Tag (\npassiv) nutzt. Da die Domaine sehr spannend klang dachte ich wäre hier eine Horizonterweiterung bestimmt auch nicht\nschlecht. Leider ist der Vortrag nicht ganz so gut wie erwartet – insgesamt habe ich den Eindruck dass es etwas\ndurcheinander ist, sicherlich auch bedingt durch viele Fachbegriffe sowie den englischsprachigen Foliensatz (weniger\nwäre hier sicher mehr gewesen). Mag sein dass der gute Mann einfach auch nicht mehr (Details) erzählen durfte…",[439,43695,43696],{},[448,43697,43698],{},"10:40 Uhr, Foyer",[439,43700,43701],{},"Wo gibt es die Croissants mit denen hier alle rum laufen?",[439,43703,43704],{},[448,43705,43706],{},"11:10 Uhr, Schiller-Saal",[439,43708,43709,43710,43715,43716,43721],{},"Im dritten Block geht es\num “",[1002,43711,43714],{"href":43712,"rel":43713},"http://www.java-forum-stuttgart.de/_data/A3_2014.pdf",[1006],"Enterprise integration Patterns Revisited","“. Kai Wähner trägt\ndas Thema sehr kurzweilig vor – die 45 Minuten fliegen recht dahin. Die wichtigsten Kernpunkte: letztlich hat wohl schon\njeder EAIs verwendet – wenn auch unbewusst. Daher:\ndas ",[1002,43717,43720],{"href":43718,"rel":43719},"http://www.amazon.de/Enterprise-Integration-Patterns-Designing-Deploying/dp/0321200683/ref=sr_1_1?ie=UTF8",[1006],"EAI Buch von Hohpe und Woolf","\nlesen, und dann die Patterns nicht selbst implementieren, sondern fertige Frameworks bzw. Tools verwenden.",[439,43723,43724],{},[448,43725,43726],{},"12:15 Uhr, Beethoven-Saal",[439,43728,43729,43730,43735],{},"Vor der Mittagspause noch was neues zu Java 8\nlernen: “",[1002,43731,43734],{"href":43732,"rel":43733},"http://www.java-forum-stuttgart.de/_data/E4_2014.pdf",[1006],"Lambdas, Collections und Streams","“. Michael Wiedeking\nträgt sehr unterhaltsam vor wie man mit Lambda Ausdrücken und Streams in Java 8 sehr elegant tolle Sachen machen kann.\nDas ganze in einem wahnsinns schnellen Tempo. Ich weiß zwar jetzt was geht – damit das wirklich anwendbar wird, muss ich\nmich wohl aber erst mal selbst hin setzen und etwas damit herumspielen. Trotz der Geschwindigkeit des Vortrages war es\nbisher wohl der beste (und auch unterhaltsamste) Vortrag des Tages.",[439,43737,43738],{},[448,43739,43740],{},"13:00 Uhr, Foyer",[439,43742,43743],{},"Mittagessen – also schön in die Schlange am Buffet einreihen. Die Auswahl ist groß, man weiß garnicht was man angesichts\ndes Angebots auf den (dafür) viel zu kleinen Teller laden soll. Aber egal wofür man sich entscheidet: alles sehr lecker!\nVorspeise, Hauptgang, Nachspeise – Zeit während der Mittagspause noch etwas den Sonnenschein draußen zu genießen und\nsich zu bewegen.",[439,43745,43746],{},[448,43747,43748],{},"14:30 Uhr, Raum Usedom",[439,43750,1454,43751,43756],{},[1002,43752,43755],{"href":43753,"rel":43754},"http://www.java-forum-stuttgart.de/_data/E5_2014.pdf",[1006],"Slim Fast","“. Heiko Rupp scheint angesicht des vielen Essens auch\nschon bedenken im Bezug auf seine Figur zu haben. Allerdings soll es hier nicht darum gehen wie wir abnehmen können,\nsondern wie der benötigte Heap unserer Anwendung schlanker werden kann. Gezeigt werden einige Tools zur Analyse des\nHeaps (VisualVM, Eclipse MAT), sowie typische Beispiele wo man auf einfache Art viel Speicher sparen kann. Wissen dass\nwir in unseren Projekten sicherlich noch gewinnbringend einsetzen können.",[439,43758,43759],{},[448,43760,43761],{},"15:20 Uhr, Foyer",[439,43763,43764],{},"Bevor dass jemand verhungert: Zeit für lecker Kuchen!",[439,43766,43767],{},[448,43768,43769],{},"15:35 Uhr, Beethoven-Saal",[439,43771,43772,43773,43778],{},"In Zeiten sich täglich überschlagender Meldungen von geklauten Daten und Passwörter so aktuell wie\nnie: “",[1002,43774,43777],{"href":43775,"rel":43776},"http://www.java-forum-stuttgart.de/_data/F6_2014.pdf",[1006],"Krypto für Java-Entwickler","“, vorgetragen von Dominik\nSchadow. Es geht darum dass man auf jeden Fall auf bewährte Krypto-Verfahren setzen sollte, Java bringt hier\nout-of-the-box schon sehr viel mit. Da dies in der Umsetzung dann allerdings oft eher etwas umständlich ist, gibt es\nauch hier Frameworks welche es dem Entwickler leicht machen Daten ordentlich zu verschlüsseln. Wichtigste Message:\nverschlüsseln ja, dabei auf fertige Bibliotheken und Algorithmen vertrauen und nichts selbst implementieren.",[439,43780,43781],{},[448,43782,43783],{},"16:40 Uhr, Raum Usedom",[439,43785,43786,43787,43792],{},"Zum Endspurt noch einmal hinauf ins oberste Stockwerk\nzu “",[1002,43788,43791],{"href":43789,"rel":43790},"http://www.java-forum-stuttgart.de/_data/A7_2014.pdf",[1006],"Entwicklung von BigData-Systemen mit Apache Cassandra","“.\nPhilipp Stussak stellt die grundsätzliche Funktionsweise dieser NoSQL Datenbank vor, und zeigt sehr deutlich welche\nVorteile diese gegenüber einer herkömmlichen (relationalen) Datenbank hat. Auf jeden Fall sehr interessant, vor allem\nwenn man sehr viele Daten zu speichern hat.",[439,43794,43795],{},[448,43796,43797],{},"17:40 Uhr, Stuttgart Innenstadt",[439,43799,43800],{},"Auf dem Weg zurück zum Bahnhof. Rückblickend hat sich der Tag in Stuttgart auf jeden Fall gelohnt. Die Vorträge waren\ninsgesamt alle sehr informativ. Vieles wurde sicherlich nur oberflächlich angerissen (mehr ist in der kurzen Zeit aber\nauch kaum möglich), die Vorträge bieten aber jeweils einen guten Startpunkt um selbst tiefer in die einzelnen Themen\neinzusteigen.",[439,43802,43803],{},"JavaForum Stuttgart – nächstes Jahr gerne wieder. Und nicht nur wegen dem Essen 😉",{"title":469,"searchDepth":507,"depth":507,"links":43805},[],[1031],"2014-07-22T11:50:17","https://synyx.de/blog/javaforum-stuttgart-2014/",{},"/blog/javaforum-stuttgart-2014",{"title":43646,"description":43657},"blog/javaforum-stuttgart-2014",[8276,711,43814,11540,9556],"javaforum","Warum in die Ferne schweifen, wenn das Gute doch so nah ist? Das dachten sich dieses Jahr auch vier Entwickler von synyx, und machten sich daher am 17.07. auf nach…","LoqlLEnXvn-aE3Wc2m1OLELW-ezLTZNNiILSLwq7qW0",{"id":43818,"title":43819,"author":43820,"body":43821,"category":43870,"date":43871,"description":43872,"extension":1034,"link":43873,"meta":43874,"navigation":916,"path":43875,"seo":43876,"slug":43878,"stem":43879,"tags":43880,"teaser":43881,"__hash__":43882},"blog/blog/kleiner-ruckblick-zur-open-street-map-konferenz-in-karlsruhe.md","Kleiner Rückblick zur Open Street Map Konferenz in Karlsruhe",[196,312],{"type":432,"value":43822,"toc":43868},[43823,43826,43835,43844,43853,43862,43865],[435,43824,43819],{"id":43825},"kleiner-rückblick-zur-open-street-map-konferenz-in-karlsruhe",[439,43827,43828,43829,43834],{},"Vom 13.-15. Juni war die ",[1002,43830,43833],{"href":43831,"rel":43832},"http://www.sotm-eu.org/",[1006],"State of the Map EU"," in Karlsruhe. Open Street Map wird bei\neinigen unserer Kundenprojekte verwendet. Nicht nur deswegen sondern auch, weil Open Street Map ein tolles Community\nProjekt ist, haben wir uns entschlossen diese Veranstaltung durch ein Sponsoring zu unterstützen.",[439,43836,43837,43838,43843],{},"Christine Karch und Frederik Ramm von der ",[1002,43839,43842],{"href":43840,"rel":43841},"http://www.geofabrik.de",[1006],"Geofabrik"," haben die Konferenz organisiert und einen\nwirklich tollen Job gemacht! Ich war positiv überrascht wie viele Teilnehmer die Konferenz besucht haben und dass doch\neinige, wie der Titel der Konferenz durch den Zusatz „EU“ suggeriert, auch aus dem Ausland angereist waren.",[439,43845,43846,43847,43852],{},"Von Freitagvormittag bis Samstagnachmittag gab es eine Vielzahl von spannenden Talks zum Thema. Ich persönlich fand den\nTalk ",[1002,43848,43851],{"href":43849,"rel":43850},"http://www.sotm-eu.org/slides/57.pdf",[1006],"„State of Kort“"," von Stefan Keller, Professor an der Hochschule für Technik\nin Rapperswil Schweiz, sehr interessant. Stefan Keller hat das Spiel Kort entwickelt und versucht über Gamification Open\nStreet Map Daten zu validieren. Bei Kort handelt es sich um eine Web-App, die für mobile Geräte optimiert ist und mit\neinem entsprechenden Backend und Open Street Map kommuniziert. Das Spielprinzip ist einfach gehalten. Spieler können\neine Mission anlegen, beispielsweise „Um welchen Weg-Typ handelt es sich hier?“ und Credits bzw. in dem Fall „Koins“\nfür die richtige Antwort vergeben. Wenn nun andere Spieler die Mission annehmen und die richtige Antwort wählen,\nerhalten sie die „Koins“ und bestätigen zum anderen, dass es sich bei dem Weg-Typ beispielsweise um einen Schotterweg\nhandelt. Damit ist das eigentliche Ziel erreicht, nämlich dass es zum einen ein gesteigertes Interesse am „mappen“ von\nOpen Street Map Daten gibt und zum anderen diese über die Community validiert werden.",[439,43854,43855,43856,43861],{},"Ein ebenfalls sehr interessanter Vortrag war der zu ",[1002,43857,43860],{"href":43858,"rel":43859},"http://www.sotm-eu.org/en/slots/6",[1006],"pgmapcss",", einem neuen Standard\nfür die Darstellung von Kartographie-Daten. Die Syntax ist stark angelehnt an CSS für HTML, wobei sich hier die\nSelektoren aber auf geographische Objekte beziehen. Dabei lässt sich bereits auf eine Vielzahl vorhandener Stile\nzurückgreifen, aber auch das Hinzufügen von eigenen Stilen ist möglich. Dies eröffnet viele Möglichkeiten einerseits die\nDarstellung, als auch Funktionen bis hin zum Routing vom selben Datenmodell in unterschiedlichen Formen auszugeben.",[439,43863,43864],{},"Am letzten Tag der Konferenz, dem Hackday, ging es dann mehr praktisch zu. Die Konferenzteilnehmer konnten selbst in die\nTasten greifen, an Workshops teilnehmen oder einen Ausflug in die „Wildnis“ von Karlsruhe unternehmen und mit Open\nStreet Map verbinden.",[439,43866,43867],{},"Insgesamt fand ich die Konferenz absolut empfehlenswert und bin gespannt, wie sie sich in Zukunft entwickelt.",{"title":469,"searchDepth":507,"depth":507,"links":43869},[],[1031],"2014-07-04T15:35:15","Vom 13.-15. Juni war die State of the Map EU in Karlsruhe. Open Street Map wird bei\\neinigen unserer Kundenprojekte verwendet. Nicht nur deswegen sondern auch, weil Open Street Map ein tolles Community\\nProjekt ist, haben wir uns entschlossen diese Veranstaltung durch ein Sponsoring zu unterstützen.","https://synyx.de/blog/kleiner-ruckblick-zur-open-street-map-konferenz-in-karlsruhe/",{},"/blog/kleiner-ruckblick-zur-open-street-map-konferenz-in-karlsruhe",{"title":43819,"description":43877},"Vom 13.-15. Juni war die State of the Map EU in Karlsruhe. Open Street Map wird bei\neinigen unserer Kundenprojekte verwendet. Nicht nur deswegen sondern auch, weil Open Street Map ein tolles Community\nProjekt ist, haben wir uns entschlossen diese Veranstaltung durch ein Sponsoring zu unterstützen.","kleiner-ruckblick-zur-open-street-map-konferenz-in-karlsruhe","blog/kleiner-ruckblick-zur-open-street-map-konferenz-in-karlsruhe",[],"Vom 13.-15. Juni war die State of the Map EU in Karlsruhe. Open Street Map wird bei einigen unserer Kundenprojekte verwendet. Nicht nur deswegen sondern auch, weil Open Street Map…","kEOmVCqxjel3aMKHHvETBu60U9c7pgr6qThAFEbWdVY",{"id":43884,"title":43885,"author":43886,"body":43887,"category":44001,"date":44002,"description":43896,"extension":1034,"link":44003,"meta":44004,"navigation":916,"path":44005,"seo":44006,"slug":43891,"stem":44007,"tags":44008,"teaser":44014,"__hash__":44015},"blog/blog/when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right.md","When your tooling is fooling you. Code review and continuous integration with Gerrit & Jenkins done right.",[418],{"type":432,"value":43888,"toc":43999},[43889,43892,43897,43900,43903,43906,43909,43912,43915,43918,43923,43926,43929,43932,43935,43938,43943,43946,43953,43956,43965,43968,43975,43978,43982,43985,43996],[435,43890,43885],{"id":43891},"when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right",[439,43893,43894],{},[990,43895,43896],{},"tl;dr: When you are using Gerrit and Jenkins on the same machine, know what you’re doing!",[439,43898,43899],{},"In a recent project we decided to increase code quality by introducing Gerrit as Code Review Tool.",[439,43901,43902],{},"The configuration looks as follows:",[439,43904,43905],{},"Next to a colleague who reviews the patchset, we created a dedicated Jenkins job which verfies the patchset by building\nthe project with the usual maven build configuration “mvn clean install” on the same machine. Only when both the\nreviewer and the ci server accept the patchset, it will be merged into our git repository.",[439,43907,43908],{},"After a successful merge of the patchset another jenkins job is triggered for deployment purpose.",[439,43910,43911],{},"That job is not surprisingly configured with “ mvn clean install -U”.",[439,43913,43914],{},"Meaning Jenkins is cleaning up the working directory and building the project by using the newest snapshots and/or\nreleases.",[439,43916,43917],{},"Last days we encounterd a problem with our setup. Surprisingly projects faild to build with the unexpected reason of\nincorrect usage of code in an artifact which in the meanwhile wasn’t changed. There have been changesets in Gerrit but\nsince they haven’t been reviewed and merged yet, they should not be in the artifact used by other projects.",[439,43919,43920],{},[448,43921,43922],{},"So whats going on right here?",[439,43924,43925],{},"Analysing the setup we came across the usage of the “-U” Parameter of Maven. The Manual says:",[439,43927,43928],{},"`-U,--update-snapshots",[439,43930,43931],{},"Forces a check for updated releases and snapshots on remote repositories`",[439,43933,43934],{},"At the first glance it seems to be what we want our Jenkins job to do. Checking for the newest dependencies before\nbuilding a project and deploying it into our repository. But in combination with the Gerrit Jenkins job runnning on the\nsame server, which verifies every patchset pushed to Gerrit we introduced an epic flaw.",[439,43936,43937],{},"The install plugin of maven puts every built artifact into the local repository which by definition is of course the\nnewest artifact you can get. So every project using this dependency will take that artifact, even when configured with\nthe “-U” parameter, which only checks if the artifact in the remote repository is newer. The attentive reader knows why\nit is not.",[439,43939,43940],{},[448,43941,43942],{},"So whats the solution?",[439,43944,43945],{},"There are three possibilities to overcome the flaw:",[439,43947,43948,43949,43952],{},"Of course you may use ",[448,43950,43951],{},"dedicated server"," for both Jenkins and Gerrit. Not sharing the local repository avoids getting\nin trouble with artifacts, which are temporary and not ready for public usage.",[439,43954,43955],{},"Not only because of the costs, also the higher administrative effort might be a reason to look for other solutions.",[439,43957,43958,43959,43964],{},"Maven ships the goal dependency:* *",[1002,43960,43963],{"href":43961,"rel":43962,"title":43963},"http://maven.apache.org/plugins/maven-dependency-plugin/examples/purging-local-repository.html",[1006],"purge-local-repository","\n** within the maven-dependency-plugin, allowing you to remove all dependencies from the local maven repository.\nConfigured in the process-sources phase it would solve the problem in our case. That solution kind of protects your\nproject from using dirty artifacts.",[439,43966,43967],{},"Howerver this unfortunately removes the symptoms, but not the cause.",[439,43969,43970,43971,43974],{},"There is an other solution which is easier than you might thought. Just configure the Gerrit Jenkins job with “mvn clean\n",[448,43972,43973],{},"package","”. This is what we actually want that job to do. It verifies the patchset by building the project without\nputting that temporary and half-baked version of the artifact into the local repository.",[439,43976,43977],{},"Don’t forget to initially clean up the local repository if you switch from ‘install’ to ‘package’, as there still might\nbe an unwanted version of the artifact.",[439,43979,43980],{},[448,43981,9392],{},[439,43983,43984],{},"Let me point out the conclusion in three simple bullet points:",[994,43986,43987,43990,43993],{},[997,43988,43989],{},"Know your artifact lifecycle and its relevance as dependency",[997,43991,43992],{},"Be careful with different tools running on the same machine sharing resources",[997,43994,43995],{},"Use Gerrit! Beside of our fail in the configuration it for sure increased our code quality and distributed knowledge\nof the codebase in our team",[439,43997,43998],{},"Did you have similar problems with that setup? Or other solutions? Don’t hesitate to comment your experience.",{"title":469,"searchDepth":507,"depth":507,"links":44000},[],[1030],"2014-06-30T13:44:14","https://synyx.de/blog/when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right/",{},"/blog/when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right",{"title":43885,"description":43896},"blog/when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right",[44009,44010,44011,44012,44013,22988],"code-quality","code-review","continuous-integration","gerrit","jenkins","tl;dr: When you are using Gerrit and Jenkins on the same machine, know what you’re doing! In a recent project we decided to increase code quality by introducing Gerrit as…","OncgbnZKLS7aSdKb4XlvE-OAdVbi8I11p7Habn0al-o",{"id":44017,"title":44018,"author":44019,"body":44020,"category":44665,"date":44666,"description":44667,"extension":1034,"link":44668,"meta":44669,"navigation":916,"path":44670,"seo":44671,"slug":44024,"stem":44673,"tags":44674,"teaser":44680,"__hash__":44681},"blog/blog/a-very-brief-history-of-the-nosql-development.md","A very brief history of the NoSQL development",[238],{"type":432,"value":44021,"toc":44663},[44022,44025,44034,44068,44079,44099,44169,44195,44198,44213,44285,44309,44319,44345,44378,44381,44407,44427,44450,44483,44493,44499,44578,44580,44659,44661],[435,44023,44018],{"id":44024},"a-very-brief-history-of-the-nosql-development",[439,44026,44027,44028,44030,44031,44033],{},"A very brief history of the NoSQL development – From Codd to Brewer and beyond\nI am still new to the movement that is now called ",[448,44029,35757],{},", and therefore curiously following all the discussions\naround the CAP theorem, consistency levels like BASE, the immolation of several letters in the ACID paradigm and the\n‘demonization’ of the relational join operation. I wondered why long established techniques and paradigms might no\nlonger be valid and attended some of the ",[448,44032,35673],{}," conferences. These conferences are still small and very\ncommunicative, and I enjoyed them a lot! Last year in Barcelona a great inspiring talk on the development of NoSQL was\ngiven by Doug Turnbull (@softwaredoug), who is a historian as well as a computer scientist. He discussed a lot of\ninteresting points, and to some of these I will refer here, too. What is better suited to understand a new topic than\nwriting a review on its history? As this would be too time consuming a task, I will write down a very brief history of\nthe events (as far as I know about them) related to the NoSQL development, as well as some of my very own thoughts and\nimpressions on this topic. There are still a lot of questions troubling my mind…",[439,44035,44036,44037,44039,44040,44043,44045,44046,44049,44050,44052,44053,44056,44057,44060,44061,44063,44064,44067],{},"A short story of three famous papers\nThere is a lot of research going on in the field of ",[448,44038,35757],{},". Three papers are frequently cited in this context: Codds\nworks on ",[448,44041,44042],{},"large shared databanks",[474,44044,18088],{},"](#References), the fundamental work that describes the foundation of relational\ndatabase systems and the relational algebra as its query language. The ",[448,44047,44048],{},"proof of Brewer’s conjecture"," by Gilbert and\nLynch",[474,44051,18182],{},"](#References), who show that, within a reasonable network model and given some premises, CAP is a ",[990,44054,44055],{},"pick two\nout of three"," choice. And the criticism on a ",[448,44058,44059],{},"one size fits all"," philosophy by Stonebraker and Çetintemel",[474,44062,18197],{},"](\n#References), a paper stating, more or less, the end of relational databases as ",[990,44065,44066],{},"the jack of all trades devices"," for\nstorage related problems.",[439,44069,44070,44071,44074,44075,44078],{},"Codds criticism\nTo understand Codds criticism on database systems we have to get back to the 1960s, the time when data in database\nsystems like IBM’s IMS or CODASYL systems was ",[448,44072,44073],{},"hierarchically ordered"," (i.e. tree structured) or ",[448,44076,44077],{},"arranged in\nnetworks"," (i.e. graph structured). Three major issues directly affect the applications that depended on the persisted\ndata in these systems:",[8310,44080,44081,44087,44093],{},[997,44082,44083,44086],{},[448,44084,44085],{},"Ordering dependence:"," The order of the records on the storage system is identical to the presentation of the\nrecords to the application. Hence, a change in this order can break the application.",[997,44088,44089,44092],{},[448,44090,44091],{},"Indexing dependence:"," If index structures are used, queries have to make explicit use of these indexes. Hence,\ndropping an index can invalidate a query and break the application.",[997,44094,44095,44098],{},[448,44096,44097],{},"Access path dependence:"," Data and relations between data are modelled as a tree or a network. If an applications\nrelies on these structures to access and retrieve data it can break if the structure is unknown or changed.",[439,44100,44101,44102,44105,44106,44109,44110,44113,44114,44117,44118,44121,44122,44125,44126,44129,44130,44133,44134,44137,44138,44141,44142,44145,44146,44149,44150,18175,44153,44156,44157,44160,44161,44164,44165,44168],{},"The relational data model: Separation of concerns and data consistency\nCodds basic idea to address these issues was the ",[448,44103,44104],{},"separation"," of the data model from the data representation on the\nstorage system. This can be achieved by modeling data and the relationships between data in terms of ",[448,44107,44108],{},"mathematical\nrelations",", structures that are, basically, sets of (ordered) ",[448,44111,44112],{},"tuples",". A ",[448,44115,44116],{},"database state",", in the simplest sense,\nis a time-varying collection of relations. To avoid anomalies, the ",[448,44119,44120],{},"database schema"," is ",[448,44123,44124],{},"normalized",", which leads\nto a distribution of the elements that constitute a single object of the domain onto several relations. The ",[448,44127,44128],{},"relational\nalgebra",", a set of operations defined on relations, is a suitable ",[448,44131,44132],{},"query language",", and as powerful (or expressive)\nas a ",[448,44135,44136],{},"first order calculus",". It produces new relations from given relations (and the algebra is in that sense **closed\n**). This query language solely depends on the relational data model and is completely independent from the underlying\nstorage of records on disks and from any defined indexing structures: All information the model bears can be retrieved\nand deduced by the query language. Codd and Stonebraker were among the first who implemented such query languages.\nToday, ",[448,44139,44140],{},"SQL",", a language based on the relational algebra, is the standard query language for relational database\nsystems.\nRelational database systems offer the concept of ",[448,44143,44144],{},"referential integrity",", mechanisms to add ",[448,44147,44148],{},"semantics"," to the model\nby using ",[448,44151,44152],{},"keys",[448,44154,44155],{},"foreign key relationships"," to ensure ",[448,44158,44159],{},"data consistency",". Concurrent access to a database is\nadministered by using ",[448,44162,44163],{},"transactions",". The famous ",[448,44166,44167],{},"ACID paradigm"," is part of (almost) all relational database systems\nand guarantees transaction to be",[994,44170,44171,44177,44183,44189],{},[997,44172,44173,44176],{},[448,44174,44175],{},"A","tomic (all operations of a transaction are executed, or none is)",[997,44178,44179,44182],{},[448,44180,44181],{},"C","onsistent (a transaction begins with a consistent state, and leaves the database in a consistent state)",[997,44184,44185,44188],{},[448,44186,44187],{},"I","solated (a transaction is executed as if it were the only transaction in the system)",[997,44190,44191,44194],{},[448,44192,44193],{},"D","urable (all changes of a transaction will be persisted to the storage system)",[439,44196,44197],{},"These features are tunable to some extend, leading to different isolation and consistency concepts. This is an important\npoint, not only in distributed systems.",[439,44199,44200,44201,44204,44205,44208,44209,44212],{},"Performance issues\nThere are some issues to be discussed when it comes to ",[448,44202,44203],{},"performance tuning"," of relational database systems: Not all\nmathematical properties of the relational algebra (or equivalent calculi) hold in the implementation of the query\nlanguages, and despite being declarative the ",[448,44206,44207],{},"order of the operations"," in a SQL statement can significantly affect the\nperformance. Equivalent reformulations of complex queries can immensely decrease response times. The ",[448,44210,44211],{},"relational join","\ncan be quite expensive, because, due to the normalization process that distributes components of one business object to\nseveral rows or even tables, intensive I/O traffic can be produced to gather the object’s components back from the\nstorage. And even the use of indexes does not always lead to acceptable response times for ad-hoc queries, so *\n*denormalization** is applied. This leads to data redundancy and brings back the carefully avoided anomalies. Often\nthese issues are tried to be addressed by **vertical scaling**, i.e. by increasing memory, power or storage of the\ndatabase server.",[439,44214,44215,44216,44219,44220,44223,44224,44226,44227,44230,44231,44234,44235,44238,44239,44241,44242,44244,44245,44248,44249,44252,44253,44256,44257,44260,44261,21201,44264,44267,44268,15250,44271,44274,44275,44277,44278,44281,44282,44284],{},"There is no such thing as a free lunch\nStarting with increasing popularity of the web and web businesses, the world became, naturally, more and more *\n*distributed**. As discussed above, there can be performance issues with relational databases, and these become more\nsignificant when scaling is ",[448,44217,44218],{},"horizontal",". Guaranteeing promises like ACID compliance becomes hard on scaling out,\nwhich is very popular nowadays: Use more computers with less performance rather than fewer computers with higher\nperformance. Commodity hardware is cheap and widely achievable. This distribution of systems comes unavoidably with the\ncharacteristics of the ",[448,44221,44222],{},"CAP theorem"," as stated as a conjecture by Brewer at PODC 2000",[474,44225,18305],{},"](#References): A\ndistributed systems cannot be (at the same time!!) ",[448,44228,44229],{},"consistent"," with all its data, ",[448,44232,44233],{},"available"," to serve all requests\nand ",[448,44236,44237],{},"tolerant to network partitioning",". The conjecture has been formally proven by Gilbert and Lynch in 2002 within a\nreasonable network model",[474,44240,18197],{},"](#References). Hence: There is no free lunch with distributed data.",[474,44243,18409],{},"](#References) In\nCAP, the term ",[448,44246,44247],{},"consistency"," of a distributed system refers to a consistent view of all nodes in the system on the data\nthat is stored. ",[448,44250,44251],{},"Availability"," means that any request will get a response as long as there is no total network\nfailure. And ",[448,44254,44255],{},"partition tolerance"," is about dealing with failures or the unavailability of parts of the system. As in\nhighly distributed systems network failures are not avoidable, the ",[448,44258,44259],{},"P"," is often a premise, and the system can be\nchosen to be ",[448,44262,44263],{},"AP",[448,44265,44266],{},"CP",". Data consistency becomes an issue, and often ",[448,44269,44270],{},"BASE",[448,44272,44273],{},"B","asically ",[448,44276,44175],{},"vailable, soft\n",[448,44279,44280],{},"S","tate, **E**ventually Consistent) is the chosen consistency paradigm in distributed environments: The system is\nguaranteed to become consistent at some time in the future, but is not guaranteed to provide a consistent view on the\ndata at all times. That a distributed datastore can be tuned within CAP bounds (and one somehow has a continuous choice)\nwas pointed out by Brewer",[474,44283,18420],{},"](#References) as well as by others.",[439,44286,44287,44288,44291,44292,44295,44296,44298,44299,44302,44303,44305,44306,44308],{},"One size does not fit all\nOver time, relational database systems with SQL as query language became the ",[448,44289,44290],{},"de facto standard"," wherever a storage\nsystem was needed, ranging from a simple data storage backing up a small web application, to giant companies data\nmanagement systems, integration databases managing data exchange between lots of applications and data warehouses: ",[448,44293,44294],{},"One\ntechnology"," integrating all these totally different purposes. The task of choosing a database is often treated as a *\n*non-functional requirement**, answered by taking the relational database system that was always used. As pointed out\nby Stonebraker and Çetintemel",[474,44297,18197],{},"](#References), there are limits to what applications a relational database system can\nbe used for. The consequence: **One size does not fit all.** A giant hammer is not a universal tool, and choosing the\ncorrect tool for a task becomes an option again in these days. Stonebraker and Çetintemel take stream processing as an\nexample to show how a simple system fitting the application’s needs can outperform a giant ",[990,44300,44301],{},"universal purpose","\nrelational database system by orders of magnitude, and state that ",[990,44304,44059],{}," can only be maintained as a\nmarketing illusion",[474,44307,18197],{},"](#References). The paper and its sequel give several examples.",[439,44310,44311,44312,44315,44316,44318],{},"The rise of NoSQL\nAt this point NoSQL comes into play, by offering datastores optimized for ",[990,44313,44314],{},"special purposes",". The usual categorization\nclassifies the NoSQL stores into one of four categories (document store, key/value store, wide column store or graph\ndatabase), and places it at a specific side of the CAP triangle",[474,44317,18197],{},"](#References), according to the properties they\noffer.",[439,44320,44321,44322,44325,44326,44328,44329,44332,44333,44336,44337,44340,44341,44344],{},"But: What exactly is NoSQL?\nIt is agreed on that NoSQL stands for ",[448,44323,44324],{},"not only SQL",". But there is still no common understanding about the concepts\nincluded into the term ",[448,44327,35757],{},". NoSQL stores are ",[448,44330,44331],{},"often"," schema-less, open source, non-relational, horizontally\nscalable, and use BASE as consistency mode. The term ",[448,44334,44335],{},"elasticity"," is used for stores that are scalable, schema-free,\nand allow for rapid replication and rapid changes. But how can these features be achieved? If there is no schema, the\napplication has to take care of the integrity of the data, as the datastore often cannot support decisions without\nknowledge about the structure. The design of many NoSQL datastores is bottom-up, optimized for horizontal scalability.\nThey often provide only simple low-level APIs (like simple set, get and put operations, ",[448,44338,44339],{},"sometimes"," realized\natomically). Modelling with NoSQL datastores ",[448,44342,44343],{},"feels"," totally different than modelling in the the relational world, and\nfollows a different philosophy.",[439,44346,44347,44348,44351,44352,44354,44355,44357,44358,44361,44362,44365,44366,44369,44370,44373,44374,44377],{},"Common misunderstandings\nThe NoSQL world is still lacking a commonly accepted terminology, and there are frequent misunderstandings: Relational\ndatabases gained their name from ",[448,44349,44350],{},"mathematical relations",", not from relationships between data tables implemented by\nforeign keys and referential integrity. The meaning of ",[448,44353,44181],{},", i.e. the idea of ",[448,44356,44159],{},", differs in ACID and\nCAP by refering to referential integrity or data being the same on different nodes. A ",[448,44359,44360],{},"BASE system"," will be consistent\nat some time in the future, this is a guaranty when the system has enough time and no more updates are altering the\nsystems state. ",[448,44363,44364],{},"ACID"," and NoSQL are not inherently mutual exclusive (see e.g. the graph database Neo4j",[474,44367,44368],{},"[8","](\n#References), or the approach FoundationDB",[474,44371,44372],{},"[9","](#References) has taken). And the terms ",[448,44375,44376],{},"sharding and replication"," are\nsometimes used synonymously, confusing data distribution with data redundancy. Not all of these misunderstandings\nmentioned here are exclusive problems in NoSQL, but produce confusion in all discussions about distributed systems.",[439,44379,44380],{},"A repetition of history?\nDoes the situation we are facing today resemble the one Codd faced when he wrote about the relational data model? There\nare clearly some similarities. But is it fair to compare the situation nowadays to the one of the 1960s? Nonetheless, I\nwill put two (arbitrarily chosen) examples for discussion.",[439,44382,44383,44384,44387,44388,44390,44391,44394,44395,44398,44399,44402,44403,44406],{},"The graph database Neo4j\n",[448,44385,44386],{},"Neo4j",", developed and maintained by Neotechnology",[474,44389,44368],{},"](#References), is the most famous ",[448,44392,44393],{},"graph database"," to date.\nNeo4j is on the ",[448,44396,44397],{},"CA"," side of the CAP triangle",[474,44400,44401],{},"[7","](#References). The data model is well-suited for a wide range of\napplications, ranging from recommendation systems to underground train time tables, and is used by many big companies.\nNeo4j is, undoubtedly, a great NoSQL database: Easy to set up and to use, lots of impressive application examples, and\ncompletely open source with a helpful community. I myself like working with that database a lot. Neo4j brings a query\nlanguage called ",[448,44404,44405],{},"Cypher"," that can be intuitively used and seems to be quite powerful. Let’s have a look at a typical\nCypher statement before Neo4j version 2.0:",[464,44408,44410],{"className":16895,"code":44409,"language":16897,"meta":469,"style":469},"START movie = node:\u003Cstrong>Movies\u003C/strong>(“title:Matrix”)\nMATCH movie\u003C-[:ACTS_IN]-actor\nRETURN actor.name;\n\n",[471,44411,44412,44417,44422],{"__ignoreMap":469},[474,44413,44414],{"class":476,"line":477},[474,44415,44416],{},"START movie = node:\u003Cstrong>Movies\u003C/strong>(“title:Matrix”)\n",[474,44418,44419],{"class":476,"line":507},[474,44420,44421],{},"MATCH movie\u003C-[:ACTS_IN]-actor\n",[474,44423,44424],{"class":476,"line":547},[474,44425,44426],{},"RETURN actor.name;\n",[439,44428,44429,44430,44433,44434,44438,44439,44441,44442,44445,44446,44449],{},"The first line of the statement reveals a direct dependency on an index called ",[990,44431,44432],{},"Movies"," (\ncompare ",[1002,44435,44437],{"href":44436},"#CoddCritics","Codds issue"," no. 2.). Since version 2.0 of Neo4j this ",[990,44440,25610],{}," has been resolved. But how *\n_powerful** and ",[448,44443,44444],{},"expressive"," is Cypher, say, compared to a query language like the relational algebra (or some version\nof datalog, or whatever language you always used and know to its bones)? Can you express everything you always wanted to\nknow about your graph model using Cypher? As far as I know there is no obvious algebra underlying Cypher (some concept\ncomparable to a **path algebra** as proposed by Neubauer and Rodriguez",[474,44447,44448],{},"[10","](#References)) that would make Cypher\neasily accessible for a formal analysis. And the last resort to querying a graph in Neo4j, the core API, is inherently _\n*imperative**, so it depends on access paths and graph traversal strategies.",[439,44451,44452,44453,44456,44459,44460,44463,44464,44466,44467,44470,44471,44474,44475,44478,44479,44482],{},"The wide column store Cassandra\n",[448,44454,44455],{},"Apache Cassandra",[474,44457,44458],{},"[11","](#References) is a famous ",[448,44461,44462],{},"wide column store",", suited to hold tons of data and resides on\nthe AP side of the CAP triangle",[474,44465,44401],{},"](#References). Cassandra can be easily distributed, is highly available and\nprovides no single point of failure with tunable consistency. Cassandra is widely used in big companies for data\nanalysis. Data modelling in Cassandra follows different objectives than data modelling against a relational database:\nData does not need to be flat, which is actually a very nice property. The data that is required to answer a query\nagainst a Cassandra data model must reside in a ",[448,44468,44469],{},"single column family",", and hence, referential integrity is considered\na non-issue here. And the data modelling methodology is equally ",[448,44472,44473],{},"driven by queries and data",", data duplication is\nwanted here, whereas data duplication in relational database systems leads to unwanted anomalies. In opposition to\nrelational database systems, transactions are not supported by Cassandra. So, we deal with completely different\napproaches, and in Cassandra some issues that are carefully treated and avoided for the purpose of data consistency in\nrelational systems are purposely ignored to achieve a ",[448,44476,44477],{},"different goal",". In addition, Cassandra is shipped with the\nCassandra Query Language ",[448,44480,44481],{},"CQL",", that is in many ways similar to SQL:",[464,44484,44486],{"className":16895,"code":44485,"language":16897,"meta":469,"style":469},"SELECT name FROM employees WHERE department = 'Marketing';\n\n",[471,44487,44488],{"__ignoreMap":469},[474,44489,44490],{"class":476,"line":477},[474,44491,44492],{},"SELECT name FROM employees WHERE department = 'Marketing';\n",[439,44494,44495,44496,44498],{},"You can have SELECT, FROM and WHERE clauses in a query, and you can think in tables, rows and columns again. But to\nmatch for an attribute in a WHERE clause, you need an index on that column (compare ",[1002,44497,44437],{"href":44436}," no.\n2.). And, unlike in SQL, you can not have subqueries. Again the question about the expressiveness of CQL remains\nunanswered, at least for me.",[439,44500,44501,44502,44506,44507,44510,44511,44514,44515,44518,44519,44522,44523,44526,44527,44530,44531,44534,44535,44538,44539,44542,44543,44546,44547,44550,44551,44554,44555,44558,44559,44562,44563,21201,44566,44569,44570,44573,44574,44577],{},"Attempting a conclusion\nI do not want, at any point and in any way, to offend any of the NoSQL datastore vendors or communities. The datastores\nall suit their purpose. As one can equivalently model a domain within each of the four pillars of NoSQL (at different\ngains and costs, obviously), one should carefully choose the database that suits the desired purpose. Data is the new\noil",[2205,44503],{"alt":44504,"src":44505},"[12]","#References"," And care should be taken when it comes to the further evolution of a datastore. It would be a\npity if a datastore sacrificed good properties to achieve a ",[990,44508,44509],{},"general purposefulness",", running the risk of repeating the\nhistory of relational databases in several ways. Fast and highly optimized, highly specialized data stores open the\ndoors to highly sophisticated ",[448,44512,44513],{},"polyglot"," solutions, and here the relational databases are included as part of the\ndatabase landscape. But there are lessons learned from the pre-relational days, and some ideas that led to relational\ndatabase systems were not so bad at all, but ",[448,44516,44517],{},"revolutionary"," in the circumstances they were invented in. Looking back\nand learning seems to be the key to ",[448,44520,44521],{},"self-healing data"," and CRDTs as described by Shapiro et al",[474,44524,44525],{},"[13","](#References),\nusing long known ideas from Lamports works",[474,44528,44529],{},"[14","](#References) and mathematical lattice theory (to formally describe\nautomatic convergence towards a common supremum for differing data states). Maybe evolution of datastores in the NoSQL\nworld can be sped-up by increasing a datastores ",[990,44532,44533],{},"fitness"," by looking back and learning, by understanding why certain\ndecisions were made in earlier times, by understanding their consequences and hence avoiding mistakes. Temporary ",[990,44536,44537],{},"faulty\nstates"," (like direct index dependencies or access path dependencies) are not always avoidable, but to know of them is\nimportant and necessary. And often differing concepts are used on purpose. Pointing out specializations and weaknesses\nleads to more ",[448,44540,44541],{},"honest solutions"," (as nicely pointed out in a FoundationDB blogpost",[474,44544,44545],{},"[15","](#References)), and that\nwould drastically simplify the choice of the correct tool from the more than 150 existing NoSQL datastores",[474,44548,44549],{},"[16","](\n#References).\nIn conclusion: I am really not sure if any uttered criticism is even fair. Going back to access, indexing and order\ndependencies can be a good choice in recent developments, and maybe NoSQL does not have to ",[990,44552,44553],{},"evolve out of this"," again,\nfor the sake of query performance. But is the separation of data and its representation on the storage not a good idea\nin the distributed world NoSQL stores reside in today? What makes the situation different from the ",[990,44556,44557],{},"pre-relational\ntime","? Supposedly, we will experience, at least to some extent, a ",[990,44560,44561],{},"reinvention of the wheel"," at certain points, and\ndoing this ",[448,44564,44565],{},"knowingly",[448,44567,44568],{},"unknowingly"," could be the question to ask! Honest database solutions are needed. If you\nknow your ",[448,44571,44572],{},"drawbacks",", do not hide them. And point out ",[448,44575,44576],{},"specializations"," and motivate them. I really cannot predict\nfuture developments in NoSQL. But maybe someone wants to share their experience here? Every comment and discussion is\nvery welcome!",[439,44579,18435],{},[994,44581,44582,44585,44588,44591,44594,44597,44600,44608,44616,44624,44627,44634,44637,44640,44643,44651],{},[997,44583,44584],{},"[1] E.F. Codd: A Relational Model of Data for Large Shared Data Banks, Communications of the ACM, Vol. 13:6, 1970.",[997,44586,44587],{},"[2] N. Lynch and S. Gilbert: “Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant\nweb services”, ACM SIGACT News, Volume 33 Issue 2 (2002), pg. 51-59.",[997,44589,44590],{},"[3] M. Stonebraker and U. Çetintemel: “One Size Fits All”: An Idea Whose Time Has Come and Gone, Proceedings of the\n21st International Conference on Data Engineering, 2005.",[997,44592,44593],{},"[4] E. Brewer: Towards Robust Distributed Systems, Keynote at PODC, 2000.",[997,44595,44596],{},"[5] HP white paper: There is no free lunch with distributed data white paper Consistency, availability, and\npartition-tolerance trade-offs on distributed data access systems.",[997,44598,44599],{},"[6] E. Brewer: CAP Twelve Years Later: How the “Rules” Have Changed, Computer, IEEE Computer Society, 2012.",[997,44601,44602,44603],{},"[7] ",[1002,44604,44607],{"href":44605,"rel":44606},"http://blog.nahurst.com/visual-guide-to-nosql-systems",[1006],"Nathan Hurst’s blog",[997,44609,44610,44611],{},"[8] ",[1002,44612,44615],{"href":44613,"rel":44614},"http://www.neotechnology.com/",[1006],"Neotechnology",[997,44617,44618,44619],{},"[9] ",[1002,44620,44623],{"href":44621,"rel":44622},"https://www.foundationdb.org/",[1006],"FoundationDB",[997,44625,44626],{},"[10] M. A. Rodriguez and P. Neubauer: A Path Algebra for Multi-Relational Graphs. CoRR, 2010.",[997,44628,44629,44630],{},"[11] ",[1002,44631,44455],{"href":44632,"rel":44633},"http://cassandra.apache.org/",[1006],[997,44635,44636],{},"[12] E. Redmond and J.R. Wilson: Seven databases in seven weeks: A Guide to Modern Databases and the NoSQL Movement,\nPragmatic Bookshelf, 2012.",[997,44638,44639],{},"[13] Shapiro et al: A comprehensive study of Convergent and Commutative Replicated Data Types. INRIA, RR-7506,\n2011.",[997,44641,44642],{},"[14] L. Lamport: Time, clocks, and the ordering of events in a distributed system. Comm ACM 21, 7, pp 558-565,\n1978.",[997,44644,44645,44646],{},"[15] ",[1002,44647,44650],{"href":44648,"rel":44649},"https://web.archive.org/web/20150526180306/http://blog.foundationdb.com:80/on-lowered-expectations-transactions-scaling-and-honesty",[1006],"FoundationDB blog",[997,44652,44653,44654],{},"[16] ",[1002,44655,44658],{"href":44656,"rel":44657},"http://nosql-database.org/",[1006],"nosql-database.org",[17894,44660],{},[1024,44662,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":44664},[],[1030],"2014-06-26T09:47:48","A very brief history of the NoSQL development – From Codd to Brewer and beyond\\nI am still new to the movement that is now called NoSQL, and therefore curiously following all the discussions\\naround the CAP theorem, consistency levels like BASE, the immolation of several letters in the ACID paradigm and the\\n‘demonization’ of the relational join operation. I wondered why long established techniques and paradigms might no\\nlonger be valid and attended some of the NoSQL matters conferences. These conferences are still small and very\\ncommunicative, and I enjoyed them a lot! Last year in Barcelona a great inspiring talk on the development of NoSQL was\\ngiven by Doug Turnbull (@softwaredoug), who is a historian as well as a computer scientist. He discussed a lot of\\ninteresting points, and to some of these I will refer here, too. What is better suited to understand a new topic than\\nwriting a review on its history? As this would be too time consuming a task, I will write down a very brief history of\\nthe events (as far as I know about them) related to the NoSQL development, as well as some of my very own thoughts and\\nimpressions on this topic. There are still a lot of questions troubling my mind…","https://synyx.de/blog/a-very-brief-history-of-the-nosql-development/",{},"/blog/a-very-brief-history-of-the-nosql-development",{"title":44018,"description":44672},"A very brief history of the NoSQL development – From Codd to Brewer and beyond\nI am still new to the movement that is now called NoSQL, and therefore curiously following all the discussions\naround the CAP theorem, consistency levels like BASE, the immolation of several letters in the ACID paradigm and the\n‘demonization’ of the relational join operation. I wondered why long established techniques and paradigms might no\nlonger be valid and attended some of the NoSQL matters conferences. These conferences are still small and very\ncommunicative, and I enjoyed them a lot! Last year in Barcelona a great inspiring talk on the development of NoSQL was\ngiven by Doug Turnbull (@softwaredoug), who is a historian as well as a computer scientist. He discussed a lot of\ninteresting points, and to some of these I will refer here, too. What is better suited to understand a new topic than\nwriting a review on its history? As this would be too time consuming a task, I will write down a very brief history of\nthe events (as far as I know about them) related to the NoSQL development, as well as some of my very own thoughts and\nimpressions on this topic. There are still a lot of questions troubling my mind…","blog/a-very-brief-history-of-the-nosql-development",[44675,44676,44677,44678,35895,35897,39976,44679],"acid","base","cap","data-consistency","relational-databases","A very brief history of the NoSQL development – From Codd to Brewer and beyond I am still new to the movement that is now called NoSQL, and therefore curiously…","qCyCGuFSyNU474f3A-k1p9p4YSl-gO7whk1M5u5S2AA",{"id":44683,"title":44684,"author":44685,"body":44686,"category":45208,"date":45209,"description":45210,"extension":1034,"link":45211,"meta":45212,"navigation":916,"path":45213,"seo":45214,"slug":44690,"stem":45216,"tags":45217,"teaser":45221,"__hash__":45222},"blog/blog/sass-support-for-web-applications-with-jetty-and-wro4j.md","Sass support for web applications with jetty and wro4j",[247],{"type":432,"value":44687,"toc":45206},[44688,44691,44700,44703,44715,44741,44835,44853,44857,44891,44895,44924,44935,44942,44946,45004,45017,45021,45130,45133,45137,45187,45190,45195,45204],[435,44689,44684],{"id":44690},"sass-support-for-web-applications-with-jetty-and-wro4j",[439,44692,44693,44694,44699],{},"Suppose we voted for ",[1002,44695,44698],{"href":44696,"rel":44697},"http://sass-lang.com/",[1006],"Sass"," as the css preprocessor of our choice for a web application. Knowing\nthat css must be generated from our Sass code everytime a scss file is modified, we want to set up the project in a way\nthat enables fast turnaround cycles during development.",[439,44701,44702],{},"The Requirements are:",[994,44704,44705,44712],{},[997,44706,44707,44708,44711],{},"generated css should be bundled within the WAR when building the webapp for ",[990,44709,44710],{},"production"," on the continuous integration\nserver",[997,44713,44714],{},"changes made to Sass resources during development time should be available to the browser without requiring a rebuild\nof the webapp",[439,44716,44717,44718,22274,44723,44728,44729,44734,44735,44740],{},"One way to fit these requirements is to use ",[1002,44719,44722],{"href":44720,"rel":44721},"http://www.eclipse.org/jetty/",[1006],"Jetty",[1002,44724,44727],{"href":44725,"rel":44726},"https://code.google.com/p/wro4j/",[1006],"Web Resource Optimizer for Java",". wro4j provides Sass support with the JRuby\nbased ",[1002,44730,44733],{"href":44731,"rel":44732},"https://code.google.com/p/wro4j/wiki/RubySassCss",[1006],"RubySassCssProcessor"," which can be used both as a pre or post\nprocessor. In order to compile our scss into css when building the webapp we have to include\nthe ",[1002,44736,44739],{"href":44737,"rel":44738},"https://code.google.com/p/wro4j/wiki/MavenPlugin",[1006],"wro4j-maven-plugin"," into the projects POM:",[464,44742,44744],{"className":6253,"code":44743,"language":6255,"meta":469,"style":469},"\n\u003Cplugin>\n \u003Cgroupid>ro.isdc.wro4j\u003C/groupid>\n \u003Cartifactid>wro4j-maven-plugin\u003C/artifactid>\n \u003Cversion>${wro4jversion}\u003C/version>\n \u003Cconfiguration>\n \u003Cwromanagerfactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory\u003C/wromanagerfactory>\n \u003Ccssdestinationfolder>${project.build.directory}/${project.build.finalName}/css/\u003C/cssdestinationfolder>\n \u003C/configuration>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>prepare-package\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>run\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n\u003C/plugin>\n\n",[471,44745,44746,44750,44755,44760,44765,44770,44775,44780,44785,44790,44795,44800,44805,44810,44815,44820,44825,44830],{"__ignoreMap":469},[474,44747,44748],{"class":476,"line":477},[474,44749,917],{"emptyLinePlaceholder":916},[474,44751,44752],{"class":476,"line":507},[474,44753,44754],{},"\u003Cplugin>\n",[474,44756,44757],{"class":476,"line":547},[474,44758,44759],{}," \u003Cgroupid>ro.isdc.wro4j\u003C/groupid>\n",[474,44761,44762],{"class":476,"line":584},[474,44763,44764],{}," \u003Cartifactid>wro4j-maven-plugin\u003C/artifactid>\n",[474,44766,44767],{"class":476,"line":607},[474,44768,44769],{}," \u003Cversion>${wro4jversion}\u003C/version>\n",[474,44771,44772],{"class":476,"line":642},[474,44773,44774],{}," \u003Cconfiguration>\n",[474,44776,44777],{"class":476,"line":663},[474,44778,44779],{}," \u003Cwromanagerfactory>ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory\u003C/wromanagerfactory>\n",[474,44781,44782],{"class":476,"line":694},[474,44783,44784],{}," \u003Ccssdestinationfolder>${project.build.directory}/${project.build.finalName}/css/\u003C/cssdestinationfolder>\n",[474,44786,44787],{"class":476,"line":700},[474,44788,44789],{}," \u003C/configuration>\n",[474,44791,44792],{"class":476,"line":913},[474,44793,44794],{}," \u003Cexecutions>\n",[474,44796,44797],{"class":476,"line":920},[474,44798,44799],{}," \u003Cexecution>\n",[474,44801,44802],{"class":476,"line":926},[474,44803,44804],{}," \u003Cphase>prepare-package\u003C/phase>\n",[474,44806,44807],{"class":476,"line":932},[474,44808,44809],{}," \u003Cgoals>\n",[474,44811,44812],{"class":476,"line":938},[474,44813,44814],{}," \u003Cgoal>run\u003C/goal>\n",[474,44816,44817],{"class":476,"line":944},[474,44818,44819],{}," \u003C/goals>\n",[474,44821,44822],{"class":476,"line":950},[474,44823,44824],{}," \u003C/execution>\n",[474,44826,44827],{"class":476,"line":956},[474,44828,44829],{}," \u003C/executions>\n",[474,44831,44832],{"class":476,"line":962},[474,44833,44834],{},"\u003C/plugin>\n",[439,44836,44837,44838,44841,44842,44845,44846,18175,44849,44852],{},"The plugins ",[990,44839,44840],{},"run"," goal is bound to the ",[990,44843,44844],{},"prepare-package"," phase, so the WAR will contain the css resource in /css/ after\npackaging the web application. In addition to the plugin configuration we also have to provide some merging and pre/post\nprocessor related configuration settings in ",[448,44847,44848],{},"wro.xml",[448,44850,44851],{},"wro.properties"," respectively.",[439,44854,44855],{},[448,44856,44848],{},[464,44858,44860],{"className":6253,"code":44859,"language":6255,"meta":469,"style":469},"\n\u003Cgroups xmlns=\"http://www.isdc.ro/wro\">\n \u003Cgroup name=\"base\">\n \u003Ccss>/sass/base/*.scss\u003C/css>\n \u003C/group>\n\u003C/groups>\n\n",[471,44861,44862,44866,44871,44876,44881,44886],{"__ignoreMap":469},[474,44863,44864],{"class":476,"line":477},[474,44865,917],{"emptyLinePlaceholder":916},[474,44867,44868],{"class":476,"line":507},[474,44869,44870],{},"\u003Cgroups xmlns=\"http://www.isdc.ro/wro\">\n",[474,44872,44873],{"class":476,"line":547},[474,44874,44875],{}," \u003Cgroup name=\"base\">\n",[474,44877,44878],{"class":476,"line":584},[474,44879,44880],{}," \u003Ccss>/sass/base/*.scss\u003C/css>\n",[474,44882,44883],{"class":476,"line":607},[474,44884,44885],{}," \u003C/group>\n",[474,44887,44888],{"class":476,"line":642},[474,44889,44890],{},"\u003C/groups>\n",[439,44892,44893],{},[448,44894,44851],{},[464,44896,44898],{"className":709,"code":44897,"language":711,"meta":469,"style":469},"\nmanagerFactoryClassName=ro.isdc.wro.manager.factory.ConfigurableWroManagerFactory\npreProcessors=cssUrlRewriting\npostProcessors=rubySassCss,cssMinJawr\ndisableCache=true\n\n",[471,44899,44900,44904,44909,44914,44919],{"__ignoreMap":469},[474,44901,44902],{"class":476,"line":477},[474,44903,917],{"emptyLinePlaceholder":916},[474,44905,44906],{"class":476,"line":507},[474,44907,44908],{},"managerFactoryClassName=ro.isdc.wro.manager.factory.ConfigurableWroManagerFactory\n",[474,44910,44911],{"class":476,"line":547},[474,44912,44913],{},"preProcessors=cssUrlRewriting\n",[474,44915,44916],{"class":476,"line":584},[474,44917,44918],{},"postProcessors=rubySassCss,cssMinJawr\n",[474,44920,44921],{"class":476,"line":607},[474,44922,44923],{},"disableCache=true\n",[439,44925,44926,44927,44930,44931,44934],{},"The configuration settings for ",[990,44928,44929],{},"caching"," and the ",[990,44932,44933],{},"manager factory"," are necessary for development environment: at\ndevelopment time we want a servlet filter to trigger the css compilation process every time when the browser requests\nthe css.",[439,44936,44937,44938,44941],{},"To enable this mechanism we place the file ",[448,44939,44940],{},"override-web.xml"," in directory src/test/resources/",[439,44943,44944],{},[448,44945,44940],{},[464,44947,44949],{"className":6253,"code":44948,"language":6255,"meta":469,"style":469},"\n\u003Cweb-app version=\"3.0\" xmlns=\"http://java.sun.com/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\">\n \u003Cfilter>\n \u003Cfilter-name>wro\u003C/filter-name>\n \u003Cfilter-class>ro.isdc.wro.http.WroFilter\u003C/filter-class>\n \u003C/filter>\n \u003Cfilter-mapping>\n \u003Cfilter-name>wro\u003C/filter-name>\n \u003Curl-pattern>*.css\u003C/url-pattern>\n \u003C/filter-mapping>\n\u003C/web-app>\n\n",[471,44950,44951,44955,44960,44965,44970,44975,44980,44985,44989,44994,44999],{"__ignoreMap":469},[474,44952,44953],{"class":476,"line":477},[474,44954,917],{"emptyLinePlaceholder":916},[474,44956,44957],{"class":476,"line":507},[474,44958,44959],{},"\u003Cweb-app version=\"3.0\" xmlns=\"http://java.sun.com/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\">\n",[474,44961,44962],{"class":476,"line":547},[474,44963,44964],{}," \u003Cfilter>\n",[474,44966,44967],{"class":476,"line":584},[474,44968,44969],{}," \u003Cfilter-name>wro\u003C/filter-name>\n",[474,44971,44972],{"class":476,"line":607},[474,44973,44974],{}," \u003Cfilter-class>ro.isdc.wro.http.WroFilter\u003C/filter-class>\n",[474,44976,44977],{"class":476,"line":642},[474,44978,44979],{}," \u003C/filter>\n",[474,44981,44982],{"class":476,"line":663},[474,44983,44984],{}," \u003Cfilter-mapping>\n",[474,44986,44987],{"class":476,"line":694},[474,44988,44969],{},[474,44990,44991],{"class":476,"line":700},[474,44992,44993],{}," \u003Curl-pattern>*.css\u003C/url-pattern>\n",[474,44995,44996],{"class":476,"line":913},[474,44997,44998],{}," \u003C/filter-mapping>\n",[474,45000,45001],{"class":476,"line":920},[474,45002,45003],{},"\u003C/web-app>\n",[439,45005,45006,45007,45012,45013,45016],{},"override-web.xml is a web.xml that Jetty applies to a web application after the application’s own WEB-INF/web.xml\nwhich means that it can override values or add new elements.\nThe ",[1002,45008,45011],{"href":45009,"rel":45010},"http://www.eclipse.org/jetty/documentation/current/jetty-maven-plugin.html",[1006],"jetty-maven-plugin"," allows to\nconfigure the location of that file with the ",[448,45014,45015],{},"overridedescriptor"," tag:",[439,45018,45019],{},[448,45020,22193],{},[464,45022,45024],{"className":6253,"code":45023,"language":6255,"meta":469,"style":469},"\n\u003Cplugin>\n \u003Cgroupid>org.eclipse.jetty\u003C/groupid>\n \u003Cartifactid>jetty-maven-plugin\u003C/artifactid>\n \u003Cversion>9.2.0.RC0\u003C/version>\n \u003Cconfiguration>\n \u003Cwebapp>\n \u003Coverridedescriptor>${project.basedir}/src/test/resources/override-web.xml\u003C/overridedescriptor>\n \u003C/webapp>\n \u003C/configuration>\n \u003Cdependencies>\n \u003Cdependency>\n \u003Cgroupid>ro.isdc.wro4j\u003C/groupid>\n \u003Cartifactid>wro4j-core\u003C/artifactid>\n \u003Cversion>${wro4jversion}\u003C/version>\n \u003C/dependency>\n \u003Cdependency>\n \u003Cgroupid>ro.isdc.wro4j\u003C/groupid>\n \u003Cartifactid>wro4j-extensions\u003C/artifactid>\n \u003Cversion>${wro4jversion}\u003C/version>\n \u003C/dependency>\n \u003C/dependencies>\n\u003C/plugin>\n\n",[471,45025,45026,45030,45034,45039,45044,45049,45053,45058,45063,45068,45072,45077,45081,45086,45091,45096,45100,45104,45108,45113,45117,45121,45126],{"__ignoreMap":469},[474,45027,45028],{"class":476,"line":477},[474,45029,917],{"emptyLinePlaceholder":916},[474,45031,45032],{"class":476,"line":507},[474,45033,44754],{},[474,45035,45036],{"class":476,"line":547},[474,45037,45038],{}," \u003Cgroupid>org.eclipse.jetty\u003C/groupid>\n",[474,45040,45041],{"class":476,"line":584},[474,45042,45043],{}," \u003Cartifactid>jetty-maven-plugin\u003C/artifactid>\n",[474,45045,45046],{"class":476,"line":607},[474,45047,45048],{}," \u003Cversion>9.2.0.RC0\u003C/version>\n",[474,45050,45051],{"class":476,"line":642},[474,45052,44774],{},[474,45054,45055],{"class":476,"line":663},[474,45056,45057],{}," \u003Cwebapp>\n",[474,45059,45060],{"class":476,"line":694},[474,45061,45062],{}," \u003Coverridedescriptor>${project.basedir}/src/test/resources/override-web.xml\u003C/overridedescriptor>\n",[474,45064,45065],{"class":476,"line":700},[474,45066,45067],{}," \u003C/webapp>\n",[474,45069,45070],{"class":476,"line":913},[474,45071,44789],{},[474,45073,45074],{"class":476,"line":920},[474,45075,45076],{}," \u003Cdependencies>\n",[474,45078,45079],{"class":476,"line":926},[474,45080,6272],{},[474,45082,45083],{"class":476,"line":932},[474,45084,45085],{}," \u003Cgroupid>ro.isdc.wro4j\u003C/groupid>\n",[474,45087,45088],{"class":476,"line":938},[474,45089,45090],{}," \u003Cartifactid>wro4j-core\u003C/artifactid>\n",[474,45092,45093],{"class":476,"line":944},[474,45094,45095],{}," \u003Cversion>${wro4jversion}\u003C/version>\n",[474,45097,45098],{"class":476,"line":950},[474,45099,6292],{},[474,45101,45102],{"class":476,"line":956},[474,45103,6272],{},[474,45105,45106],{"class":476,"line":962},[474,45107,45085],{},[474,45109,45110],{"class":476,"line":4876},[474,45111,45112],{}," \u003Cartifactid>wro4j-extensions\u003C/artifactid>\n",[474,45114,45115],{"class":476,"line":4888},[474,45116,45095],{},[474,45118,45119],{"class":476,"line":4900},[474,45120,6292],{},[474,45122,45123],{"class":476,"line":4913},[474,45124,45125],{}," \u003C/dependencies>\n",[474,45127,45128],{"class":476,"line":4921},[474,45129,44834],{},[439,45131,45132],{},"Finally we configure the build to exclude Sass resources and wro4j configuration from the WAR:",[439,45134,45135],{},[448,45136,22193],{},[464,45138,45140],{"className":6253,"code":45139,"language":6255,"meta":469,"style":469},"\n\u003Cplugin>\n \u003Cartifactid>maven-war-plugin\u003C/artifactid>\n \u003Cconfiguration>\n \u003Cwarsourceexcludes>\n WEB-INF/wro.*,\n sass/**\n \u003C/warsourceexcludes>\n \u003C/configuration>\n\u003C/plugin>\n\n",[471,45141,45142,45146,45150,45155,45159,45164,45169,45174,45179,45183],{"__ignoreMap":469},[474,45143,45144],{"class":476,"line":477},[474,45145,917],{"emptyLinePlaceholder":916},[474,45147,45148],{"class":476,"line":507},[474,45149,44754],{},[474,45151,45152],{"class":476,"line":547},[474,45153,45154],{}," \u003Cartifactid>maven-war-plugin\u003C/artifactid>\n",[474,45156,45157],{"class":476,"line":584},[474,45158,44774],{},[474,45160,45161],{"class":476,"line":607},[474,45162,45163],{}," \u003Cwarsourceexcludes>\n",[474,45165,45166],{"class":476,"line":642},[474,45167,45168],{}," WEB-INF/wro.*,\n",[474,45170,45171],{"class":476,"line":663},[474,45172,45173],{}," sass/**\n",[474,45175,45176],{"class":476,"line":694},[474,45177,45178],{}," \u003C/warsourceexcludes>\n",[474,45180,45181],{"class":476,"line":700},[474,45182,44789],{},[474,45184,45185],{"class":476,"line":913},[474,45186,44834],{},[439,45188,45189],{},"That’s it. With this setup developers are now able to modify scss files and see the resulting css just by reloading the\npage in the browser.",[439,45191,45192],{},[448,45193,45194],{},"webapp project structure",[439,45196,45197],{},[1002,45198,45201],{"href":45199,"rel":45200},"https://media.synyx.de/uploads//2014/05/projectstructure.png",[1006],[2205,45202],{"alt":45203,"src":45199},"projectstructure",[1024,45205,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":45207},[],[1030],"2014-05-26T07:31:24","Suppose we voted for Sass as the css preprocessor of our choice for a web application. Knowing\\nthat css must be generated from our Sass code everytime a scss file is modified, we want to set up the project in a way\\nthat enables fast turnaround cycles during development.","https://synyx.de/blog/sass-support-for-web-applications-with-jetty-and-wro4j/",{},"/blog/sass-support-for-web-applications-with-jetty-and-wro4j",{"title":44684,"description":45215},"Suppose we voted for Sass as the css preprocessor of our choice for a web application. Knowing\nthat css must be generated from our Sass code everytime a scss file is modified, we want to set up the project in a way\nthat enables fast turnaround cycles during development.","blog/sass-support-for-web-applications-with-jetty-and-wro4j",[45218,22988,45219,45220],"jetty","sass","wro4j","Suppose we voted for Sass as the css preprocessor of our choice for a web application. Knowing that css must be generated from our Sass code everytime a scss file…","2m1pqJHnCv2mRrS3cGs-YVVEGl5c11N_zHK45JABzNE",{"id":45224,"title":45225,"author":45226,"body":45227,"category":45423,"date":45424,"description":45425,"extension":1034,"link":45426,"meta":45427,"navigation":916,"path":45428,"seo":45429,"slug":45231,"stem":45431,"tags":45432,"teaser":45435,"__hash__":45436},"blog/blog/nosql-still-matters.md","NoSQL still matters",[238],{"type":432,"value":45228,"toc":45421},[45229,45232,45245,45256,45264,45267,45279,45303,45308,45324,45333,45356,45361,45368,45375,45398],[435,45230,45225],{"id":45231},"nosql-still-matters",[439,45233,45234,45235,45237,45238,45241,45244],{},"Vom 28. April bis zum 30. April fand die",[990,45236,35673],{}," in Köln statt. Austragungsort war das ",[448,45239,45240],{},"KOMED im MediaPark,",[448,45242,45243],{},"nur knapp 15 Gehminuten von Kölner Hauptbahnhof und Dom entfernt."," Neben zwei Tagen mit Vorträgen gab es auch einen\nTrainingstag. Die angebotenen Workshops waren hochwertig, und einige Firmen wie Neotechnology haben die Chance genutzt\num neben der Konferenz auch ein Meetup durchzuführen. Die Vorträge auf der Tagung waren breit gestreut, und aufgrund der\nnoch überschaubaren Teilnehmerzahl gab es viel Gelegenheit für Diskussion. Die Teilnahme an diesen Veranstaltungen kann\nich uneingeschränkt all denjenigen empfehlen, die sich für neue Datenbanktechnologien und BigData interessieren, und\neinen Einblick aus sowohl anwendungsorientierter als auch theoretisch fundierter Sicht gewinnen wollen. Inhalt dieses\nkurzen BlogPosts sollen jedoch ein paar Punkte sein, an denen sich die Geister noch immer scheiden, und die auch auf der\nKonferenz in Köln immer wieder zu Diskussionen geführt haben.",[439,45246,45247,8351,45250,8351,45253],{},[448,45248,45249],{},"Was",[448,45251,45252],{},"gehört zu",[448,45254,45255],{},"NoSQL?",[439,45257,45258,45260,45261,45263],{},[448,45259,35757],{}," ist eine noch junge und damit beständigem Wandel und der Suche nach Definitionen unterworfene Disziplin der\nInformatik. Wie so oft trifft man hier oft auf altbekannte Konzepte in neuem Gewand. Es besteht nach wie vor keine\neinheitliche Meinung, was unter dem Begriff",[990,45262,35757],{}," genau zu verstehen ist. Einigkeit herrscht mittlerweile in der\nKlassifikation der Datastores in vier Kategorien: Key/Value Stores, Document Stores, Wide Column Stores und\nGraphdatenbanken. Darüber hinaus werden viele Schlagworte und deren Zugehörigkeit zu NoSQL kontrovers diskutiert und je\nnach Anwendungskontext auch unterschiedlich interpretiert: CAP, BASE, ACID, Datenredundanz, horizontale und vertikale\nSkalierung, Normalisierung und Denormalisierung, Sharding und Replikation, und viele mehr.",[439,45265,45266],{},"Diskussionen über die folgenden drei Punkte sind mir in Köln wiederholt aufgefallen, die mir nicht immer gut und\neinheitlich verstanden schienen.",[439,45268,45269,45272,45273,8351,45276],{},[448,45270,45271],{},"Namensgebung r**","elationale*",[448,45274,45275],{},"*r",[448,45277,45278],{},"Datenbanksysteme",[439,45280,45281,45282,45285,45286,32075,45289,45292,45293,45295,45296,8351,45299,45302],{},"Relational sind relationale Datenbanksysteme, weil sie ihren Ursprung in den mathematischen Relationen haben.Relationen\nmeinen nicht die Beziehungen zwischen den Tupeln der Datenbanktabellen, sondern bezeichnen Mengen von Tupeln. Formal ist\neine Relation nichts weiter als eine benannte Teilmenge des kartesischen Produktes nicht-leerer Mengen, und deren\nElemente sind Tupel. Die ",[448,45283,45284],{},"relationale Algebra",", bzw. äquivalente Kalküle wie der ",[448,45287,45288],{},"TRC",[448,45290,45291],{},"DRC",", sind die\nGrundlage für relationale Anfragesprachen (und damit letztlich auch für ",[448,45294,44140],{},"). Dieser Relationsbegriff hat erst\neinmal nichts zu tun mit referentieller Integrität, d.h. der ",[990,45297,45298],{},"inneren",[990,45300,45301],{},"Konsistenz"," der in einer relationalen Datenbank\ngespeicherten Tupel.",[439,45304,45305],{},[448,45306,45307],{},"Konsistenz: Das C in ACID und CAP",[439,45309,45310,45311,45313,45314,2040,45316,45319,45320,45323],{},"Der Buchstabe ",[448,45312,44181],{}," in den Akronymen ",[448,45315,44364],{},[448,45317,45318],{},"CAP"," steht beide Male für Konsistenz (engl. ",[448,45321,45322],{},"Consistency","), meint\naber jeweils einen anderen Konsistenzbegriff. Die Konsistenz in ACID bedeutet, dass nur Übergänge von gültigen Zuständen\nin gültige Zustände erlaubt sind, und zwar im Sinne der für die Datenbank definierten referentiellen Integrität. Dies\nmeint also eine ‘innere Konsistenz’ der in einer Datenbank gespeicherten Tupel. Konsistenz im Sinne von CAP bedeutet,\ndass jeder Knoten im verteilten System zu jedem Zeitpunkt die ‘richtige’ Antwort zu jeder Anfrage liefert. Die\nAnforderung an die Konsistenz hängt damit vom Service ab. Der vielleicht natürlichste Konsistenzbegriff ist der der\nlinearisierbaren Konsistenz: Dies bedeutet, das jeder Client, der Anfragen an das verteilte System stellt, den Eindruck\nhat, das alle Request/Response-Operationen von einem einzigen, zentralen Server beantwortet werden. (Formal heißt das,\ndass eine totale Ordnung dieser Operationen existieren muss, so das jede Operation als sofort und exklusiv ausgeführt\nerscheint.)",[439,45325,45326,45329,45330],{},[448,45327,45328],{},"Vielleicht konsistent**","e Systeme*",[448,45331,45332],{},"*?",[439,45334,45335,45336,45339,45340,44274,45342,45344,45345,45347,45348,45351,45352,45355],{},"In der NoSQL Welt wird häufig der Begriff der ",[448,45337,45338],{},"eventual consiste**","ncy** angetroffen (u.a. als Bestandteil des\nAkronyms BASE: ",[448,45341,44273],{},[448,45343,44175],{},"vailable, ",[448,45346,44280],{},"oft state, **E**ventual consistency). Das englische Wort ",[990,45349,45350],{},"eventual","\nist nicht mit dem deutschen Wort ",[990,45353,45354],{},"eventuell"," zu übersetzen, sondern eher mit *schlussendlich*! Eventual consistency ist\nein Konsistenzmodel für verteilte Systeme: Ein verteiltes System erfüllt dieses Modell, wenn es für ein gespeichertes\nDatum, vorausgesetzt das keine Updates mehr für dieses erfolgen, irgendwann für alle Anfragen nach diesem Datum den\nselben, zuletzt aktualisierten Wert liefert. Eventual consistency bedeutet nicht, dass das System ‘vielleicht mal’ einen\nkonsistenten Zustand erreicht.",[439,45357,45358],{},[448,45359,45360],{},"Diskussion",[439,45362,45363,45364,45367],{},"Durch die zunehmende Zahl an verfügbaren Datastores und deren Spezialisierung wird es auch zunehmend wichtiger, genau zu\nwissen, für welchen Anwendungsbereich und Use-Case ein Datastore gewählt werden soll. Davon hängt ab, welches\nKonsistenzmodell zur Anwendung kommen soll, welches Datenmodell sich anbietet und wie Sharding- und\nReplikationsstrategien zu wählen sind. Insbesondere dann, wenn auch ",[990,45365,45366],{},"polyglotte"," Lösungen zum Einsatz kommen sollen. Die\nteilweise noch vorhandenen unterschiedlichen Auffassungen der Definitionen und die resultierenden Missverständnisse\nmachen die Diskussionen im Bereich NoSQL nach wie vor spannend, zumal umfassende Langzeiterfahrungen aufgrund des Alters\ndieser Technologien noch nicht vorliegen können. Kommentare und Meinungen hierzu sind ausdrücklich erwünscht!",[439,45369,45370,8351,45373],{},[448,45371,45372],{},"Ausgewählte",[448,45374,32543],{},[439,45376,45377,8351,45380,8351,45383,8351,45386,45393,45394,45397],{},[448,45378,45379],{},"Es gibt eine Vielzahl lesenswerter Artikel und",[448,45381,45382],{},"Bücher",[448,45384,45385],{},"zu den oben diskutierten Themen. Eine",[448,45387,45388,45389,45392],{},"nützliche\n** ",[448,45390,45391],{},"(**","wenn auch subjektive*","*)** ",[448,45395,45396],{},"Auswahl findet sich nachstehend. Auch d","ie in diesem Blog geäußerten\nGedanken und Ausführungen folgen teilweise diese Büchern und Artikeln.",[994,45399,45400,45403,45406,45409,45412,45415,45418],{},[997,45401,45402],{},"Seth Gilbert and Nancy Lynch. Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant\nweb services. SIGACT News, vol. 33, no. 2, pp. 51-59, 2002",[997,45404,45405],{},"Stefan Edlich, Achim Friedland, Jens Hampe, Benjamin Brauer und Markus Brückner. NoSQL: Einstieg in die Welt\nnichtrelationaler Web 2.0 Datenbanken. Hanser Verlag. 2011",[997,45407,45408],{},"Eric Brewer, “CAP Twelve Years Later: How the “Rules” Have Changed,” Computer, vol. 45, no. 2, pp. 23-29, 2012",[997,45410,45411],{},"Seth Gilbert and Nancy A. Lynch. Perspectives on the CAP Theorem. Computer, vol. 45, no. 2, pp. 30-36, 2012",[997,45413,45414],{},"Pramod J. Sadalage and Martin Fowler. NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence.\nAddison-Wesley Professional. 2012",[997,45416,45417],{},"Eric Redmond and Jim R. Wilson. Seven Databases in Seven Weeks. Pragmatic Bookshelf. 2012",[997,45419,45420],{},"Katarina Grolinger, Wilson A Higashino, Abhinav Tiwari, and Miriam AM Capretz. Data management in cloud environments:\nNoSQL and NewSQL data stores. Journal of Cloud Computing: Advances, Systems and Applications, vol. 2, no. 22, 2013",{"title":469,"searchDepth":507,"depth":507,"links":45422},[],[1030],"2014-05-16T00:10:11","Vom 28. April bis zum 30. April fand dieNoSQL matters in Köln statt. Austragungsort war das KOMED im MediaPark,nur knapp 15 Gehminuten von Kölner Hauptbahnhof und Dom entfernt. Neben zwei Tagen mit Vorträgen gab es auch einen\\nTrainingstag. Die angebotenen Workshops waren hochwertig, und einige Firmen wie Neotechnology haben die Chance genutzt\\num neben der Konferenz auch ein Meetup durchzuführen. Die Vorträge auf der Tagung waren breit gestreut, und aufgrund der\\nnoch überschaubaren Teilnehmerzahl gab es viel Gelegenheit für Diskussion. Die Teilnahme an diesen Veranstaltungen kann\\nich uneingeschränkt all denjenigen empfehlen, die sich für neue Datenbanktechnologien und BigData interessieren, und\\neinen Einblick aus sowohl anwendungsorientierter als auch theoretisch fundierter Sicht gewinnen wollen. Inhalt dieses\\nkurzen BlogPosts sollen jedoch ein paar Punkte sein, an denen sich die Geister noch immer scheiden, und die auch auf der\\nKonferenz in Köln immer wieder zu Diskussionen geführt haben.","https://synyx.de/blog/nosql-still-matters/",{},"/blog/nosql-still-matters",{"title":45225,"description":45430},"Vom 28. April bis zum 30. April fand dieNoSQL matters in Köln statt. Austragungsort war das KOMED im MediaPark,nur knapp 15 Gehminuten von Kölner Hauptbahnhof und Dom entfernt. Neben zwei Tagen mit Vorträgen gab es auch einen\nTrainingstag. Die angebotenen Workshops waren hochwertig, und einige Firmen wie Neotechnology haben die Chance genutzt\num neben der Konferenz auch ein Meetup durchzuführen. Die Vorträge auf der Tagung waren breit gestreut, und aufgrund der\nnoch überschaubaren Teilnehmerzahl gab es viel Gelegenheit für Diskussion. Die Teilnahme an diesen Veranstaltungen kann\nich uneingeschränkt all denjenigen empfehlen, die sich für neue Datenbanktechnologien und BigData interessieren, und\neinen Einblick aus sowohl anwendungsorientierter als auch theoretisch fundierter Sicht gewinnen wollen. Inhalt dieses\nkurzen BlogPosts sollen jedoch ein paar Punkte sein, an denen sich die Geister noch immer scheiden, und die auch auf der\nKonferenz in Köln immer wieder zu Diskussionen geführt haben.","blog/nosql-still-matters",[44675,44676,35897,45433,45434],"nosql-matters","relationale-datenbanksysteme","Vom 28. April bis zum 30. April fand die NoSQL matters in Köln statt. Austragungsort war das KOMED im MediaPark, nur knapp 15 Gehminuten von Kölner Hauptbahnhof und Dom entfernt. Neben…","GlaGdlYSIlDm0ys1WiCUbckmRqMC-oTJl0c3TNet_HI",{"id":45438,"title":45439,"author":45440,"body":45441,"category":45710,"date":45711,"description":45712,"extension":1034,"link":45713,"meta":45714,"navigation":916,"path":45715,"seo":45716,"slug":45445,"stem":45718,"tags":45719,"teaser":45723,"__hash__":45724},"blog/blog/synyx-berlin-expert-days-2014.md","synyx @ Berlin Expert Days 2014",[97],{"type":432,"value":45442,"toc":45703},[45443,45446,45454,45456,45459,45463,45475,45478,45489,45492,45500,45504,45527,45530,45540,45544,45559,45600,45609,45613,45668,45672,45681,45684,45688,45697,45700],[435,45444,45439],{"id":45445},"synyx-berlin-expert-days-2014",[439,45447,45448,45449,45453],{},"Last weekend our conference train got rolling again. A group of twelve synyx guys and gals boarded the ICE to our\ncapital, heading for the ",[1002,45450,36379],{"href":45451,"rel":45452},"http://bed-con.org/",[1006],", a nice and small two-day developer conference. The\nanticipation was high as the topics and speakers were promising and we were looking forward to having a nice time inside\nand outside of the conference.",[435,45455,20574],{"id":20573},[439,45457,45458],{},"The following talks were my (highly subjective) Top 3 of the conference:",[1065,45460,45462],{"id":45461},"_1-eberhard-wolff-death-to-java-app-servers","1. Eberhard Wolff – Death to Java App Servers!",[439,45464,45465,45468,45469,45474],{},[1002,45466,375],{"href":8194,"rel":45467},[1006]," started his talk with the provoking thesis\nthat ",[1002,45470,45473],{"href":45471,"rel":45472},"http://www.slideshare.net/ewolff/java-application-servers-are-dead",[1006],"Java app servers are dead"," and that there is\nno point in using them any longer. At first I thought this was strongly exeggarated and probably not true but then he\nbacked his statement with a surprisingly high number of really good arguments. He made me hate application servers :-(.",[439,45476,45477],{},"His key points among others were:",[994,45479,45480,45483,45486],{},[997,45481,45482],{},"In reality there is rarely such a thing as multiple applications running on one app server. The reason are numerous\nisolation issues",[997,45484,45485],{},"The application server is just another part of the application itself, individually configured for only this\napplication. The application depends on the app server and vice versa",[997,45487,45488],{},"App servers lead to an unnecessary complex infrastructure with high turnaround and deployment costs, making continuous\nintegration and delivery really hard",[439,45490,45491],{},"In the end Wolff did not yet provide an elaborated alternative but hinted that an architecture of standalone micro\nservice applications could be a much better way to go.",[1065,45493,45495],{"id":45494},"bedconewolff",[1002,45496,45499],{"href":45497,"rel":45498},"https://media.synyx.de/uploads//2014/04/BedCon.jpeg",[1006],"BedConEWolff",[1065,45501,45503],{"id":45502},"_2-jochen-mader-vertx-for-world-domination","2. Jochen Mader – VERT.X for World Domination",[439,45505,45506,45511,45512,45517,45518,45520,45521,45526],{},[1002,45507,45510],{"href":45508,"rel":45509},"https://twitter.com/codepitbull",[1006],"Jochen Mader’s"," talk about the distributed JVM platform ",[1002,45513,45516],{"href":45514,"rel":45515},"http://vertx.io/",[1006],"VERT.X"," was\nhighly informative and entertaining. While talking with incredible speed and a stylish outfit (wearing Star Trek\nt-shirt ",[448,45519,18225],{}," socks) he presented\nhis ",[1002,45522,45525],{"href":45523,"rel":45524},"http://www.slideshare.net/codepitbull/vertx-for-worlddomination",[1006],"plan to conquer the world with robots",". He\ndemonstrated the prototype of his version of Skynet, consisting of a RasPi cluster and a Lego Mindstorms robot running\nVERT.X instances.",[439,45528,45529],{},"This effectively indicated the power of VERT.X and its many features, as you can connect services of many different JVM\nlanguages without much effort in a VERT.X cluster, that very intelligently takes care of all needed communication and\nresilience issues by itself. Mader pointed out the modularity, event-driven architecture, scalability and multi\nlanguage capability of VERT.X clusters, convincing his audience of the power of this solution.",[439,45531,45532],{},[1002,45533,45536],{"href":45534,"rel":45535},"https://media.synyx.de/uploads//2014/04/worlddomination.jpg",[1006],[2205,45537],{"alt":45538,"src":45539},"worlddomination","https://media.synyx.de/uploads//2014/04/worlddomination-300x225.jpg",[1065,45541,45543],{"id":45542},"_3-michael-plöd-caching-in-business-applications","3. Michael Plöd – Caching in Business Applications",[439,45545,45546,45547,45552,45553,45558],{},"While the first two of this top three list were a clear pick for me, there were many equally good talks battling for the\nthird rank. In the end I picked ",[1002,45548,45551],{"href":45549,"rel":45550},"https://twitter.com/bitboss",[1006],"Michael Plöd’s"," talk,\nwho ",[1002,45554,45557],{"href":45555,"rel":45556},"http://de.slideshare.net/mploed/caching-fur-business-anwendungen-deutsch",[1006],"shed light on the many aspects of caching",".\nAfter explaining the demand and applications of caching in general, he enumerated a nice list of best practices and\npatterns everyone should follow when designing caches:",[994,45560,45561,45564,45567,45570,45573,45576,45579,45582,45585,45588,45591,45594,45597],{},[997,45562,45563],{},"Identify appropriate layers for caching",[997,45565,45566],{},"Stay local as long as possible",[997,45568,45569],{},"Prefer invalidation over replication",[997,45571,45572],{},"Avoid large heap sizes only for caching",[997,45574,45575],{},"Consider a distributed cache for very large amounts of data",[997,45577,45578],{},"The Op-Guy is your best friend!",[997,45580,45581],{},"Only cache data that is suited for caching (read-mostly, expensive to get)",[997,45583,45584],{},"Use proved and tested cache implementations and NEVER EVER write your own implementation",[997,45586,45587],{},"Introduce caches in three steps (optimize the application itself → introduce local cache → introduce distributed\ncache)",[997,45589,45590],{},"Optimize serialization of objects",[997,45592,45593],{},"Abstract your cache provider",[997,45595,45596],{},"Store often used data as near to your application as possible",[997,45598,45599],{},"Use off-heap storage for cache instances of 4GB or more",[439,45601,45602],{},[1002,45603,45606],{"href":45604,"rel":45605},"https://media.synyx.de/uploads//2014/04/caching.jpg",[1006],[2205,45607],{"alt":44929,"src":45608},"https://media.synyx.de/uploads//2014/04/caching-300x225.jpg",[1065,45610,45612],{"id":45611},"_4-n-many-many-more","4.-n. Many, many more",[439,45614,45615,45616,45620,45621,45626,45627,45630,45631,18175,45636,45641,45642,520,45647,45651,45652,45657,45658,45662,45663,1402],{},"In addition to the top three there was a high density of competent speakers with interesting and informative talks. I\nwant to point out our homeboy ",[1002,45617,149],{"href":45618,"rel":45619},"https://twitter.com/fhopf",[1006]," who gave a\nnice ",[1002,45622,45625],{"href":45623,"rel":45624},"https://speakerdeck.com/exensio/search-driven-applications",[1006],"overview on the state of search driven applications","\ntogether with his colleague ",[1002,45628,37811],{"href":37809,"rel":45629},[1006],". They demonstrated that it is worthier than ever\ntaking a look on Solr and ElasticSearch to implement your queries. Also definitely worth mentioning\nare ",[1002,45632,45635],{"href":45633,"rel":45634},"https://twitter.com/stilkov",[1006],"Stefan Tilkov",[1002,45637,45640],{"href":45638,"rel":45639},"https://twitter.com/Eigenbrodtm",[1006],"Martin Eigenbrodt"," with their\ndemonstration and evaluation\nof ",[1002,45643,45646],{"href":45644,"rel":45645},"https://speakerdeck.com/stilkov/restful-http-on-the-jvm",[1006],"REST HTTP implementations in different JVM languages",[1002,45648,378],{"href":45649,"rel":45650},"https://twitter.com/StefanZoerner",[1006],"\nwith a useful categorization\nof ",[1002,45653,45656],{"href":45654,"rel":45655},"http://www.embarc.de/vortrag-berlin-expert-days-2014-verunfallte-softwarearchitektur/",[1006],"software architecture and different ways to evaluate your architecture",",\nand ",[1002,45659,36704],{"href":45660,"rel":45661},"https://twitter.com/timmo_gierke",[1006]," with a\ninsightful ",[1002,45664,45667],{"href":45665,"rel":45666},"https://speakerdeck.com/timmo/micro-services-die-verheissungen-konnten-eintreten",[1006],"practical overview on micro services",[435,45669,45671],{"id":45670},"the-trend","The Trend",[439,45673,45674,45675,45680],{},"The clear winner in the category “trending buzzword” was ",[448,45676,45677],{},[990,45678,45679],{},"micro service",". It ran like a common thread through the\nwhole conference. Not only Freudl-Gierke’s talk about micro services in practice took care of the topic. Over the\nconference we learned that it is time to inaugurate an era of standalone micro service applications of any language that\ntalk together over REST interfaces, get clustered with VERT.X, collect their logs centrally using logstash and kibana\nand intelligently use their local and distributed caches thus saving failed architectures. We saw how to create them\neasily using Spring Boot and how to supply continuous integration for all of them using Docker.",[439,45682,45683],{},"These are the highly anticipated expectations – it lies in the nature of hyped buzzwords that fulfilling these\nexpectations is a completely different matter. Let’s see what the future has in stock for us!",[435,45685,45687],{"id":45686},"time-off","Time Off",[439,45689,45690,45691,45696],{},"So much for the technical mumble jumble. Let’s take some time off and talk about one of the greatest benefits of the\nBEDCon – that it takes place in Berlin! There are not many cities where you can enjoy your evenings in the same way as\nin our capital. During our three days in Berlin we got yelled at by an Italian waiter for not ordering another Grappa,\nconsumed numerous instances of “",[1002,45692,45695],{"href":45693,"rel":45694},"http://en.wikipedia.org/wiki/Shandy#Diesel",[1006],"Diesel","”, visited a punk rock concert, an\nunderground live music club and a russian disco and on the walk between the different locations we enjoyed our\non-the-way-beers, that we got from a “Späti”.",[439,45698,45699],{},"A tech-savvy taxi driver with a dash cam showed us a video of a police car causing an accident on his Galaxy Note\n10.1 (while speeding through the city way above the speed limit) and an US-American waitress in a Cuban restaurant\ntook turns in laughing and singing while serving delicious meals and Cocktails.",[439,45701,45702],{},"It is difficult to collect so many different experiences in such a short time anywhere else and we enjoyed every night\nuntil as late as reasonably possible. These great experiences in combination with the high quality of conference talks\nwill ensure that next year a large amount of synyx dudes and dudettes will again board the ICE to Berlin.",{"title":469,"searchDepth":507,"depth":507,"links":45704},[45705,45706,45707,45708,45709],{"id":45461,"depth":547,"text":45462},{"id":45494,"depth":547,"text":45499},{"id":45502,"depth":547,"text":45503},{"id":45542,"depth":547,"text":45543},{"id":45611,"depth":547,"text":45612},[1030],"2014-04-09T11:05:51","Last weekend our conference train got rolling again. A group of twelve synyx guys and gals boarded the ICE to our\\ncapital, heading for the Berlin Expert Days, a nice and small two-day developer conference. The\\nanticipation was high as the topics and speakers were promising and we were looking forward to having a nice time inside\\nand outside of the conference.","https://synyx.de/blog/synyx-berlin-expert-days-2014/",{},"/blog/synyx-berlin-expert-days-2014",{"title":45439,"description":45717},"Last weekend our conference train got rolling again. A group of twelve synyx guys and gals boarded the ICE to our\ncapital, heading for the Berlin Expert Days, a nice and small two-day developer conference. The\nanticipation was high as the topics and speakers were promising and we were looking forward to having a nice time inside\nand outside of the conference.","blog/synyx-berlin-expert-days-2014",[45720,20860,16640,44929,8276,45721,45722],"application-servers","micro-services","vertx","Last weekend our conference train got rolling again. A group of twelve synyx guys and gals boarded the ICE to our capital, heading for the Berlin Expert Days, a nice…","6fZcOrn2lZ1pCWP5Z8L_qDizKwQWC7G0BPa5PE9pkNg",{"id":45726,"title":45727,"author":45728,"body":45729,"category":45835,"date":45836,"description":45837,"extension":1034,"link":45838,"meta":45839,"navigation":916,"path":45840,"seo":45841,"slug":45733,"stem":45843,"tags":45844,"teaser":45845,"__hash__":45846},"blog/blog/planning-and-designing-your-it-infrastructure-part-ii-evaluating-your-requirements-and-designing-your-data-center.md","Planning and designing your IT Infrastructure – Part II: Evaluating your requirements and designing your data center",[178],{"type":432,"value":45730,"toc":45833},[45731,45734,45741,45744,45749,45752,45766,45769,45774,45777,45782,45785,45796,45801,45804,45807,45813,45819,45826],[435,45732,45727],{"id":45733},"planning-and-designing-your-it-infrastructure-part-ii-evaluating-your-requirements-and-designing-your-data-center",[439,45735,43313,45736,45740],{},[1002,45737,45739],{"href":43380,"rel":45738},[1006],"my last blog post",",\ni talked about all these little things you need to keep in mind when setting up a new or replace your current network\ninfrastructure. I came up with that topic first, because in our case we are not only building a new data center, but we\nare also building a whole new office location. Providing a working network infrastructure to the office floor was\ncrucial in our case, that’s why we took care of that first.",[439,45742,45743],{},"In this blog post, i will talk about planning a data center itself. The most important step in data center design, is to\nevaluate what you want and what you really need: Pretty much everybody wants a Tier 3 design, but hardly anyone needs\nit. In fact, with todays supply of professional webhosting solutions, colocation centers, cloud computing and other\nsolutions, you need to evaluate if your company really needs to build their own data center at all. Which brings me back\nto remind you of the 2 most important rules when planning it infrastructure: First of all: Don’t overbuild, it’s a waste\nof money! Second of all: Don’t plan for now, plan for the future! Sounds contradictory? Well, it really isn’t… 😉",[439,45745,45746],{},[448,45747,45748],{},"Don’t overbuild!",[439,45750,45751],{},"Before you go ahead trying to find a location for your new data center, calculate your UPS supply, thinking about air\nconditioning, air flow management, redundancy, raised floors, server racks, hardware, storage, networking and what not,\nfind out what you really need. Helpful information can be found with the Uptime Institutes tier level concept, which\nuses a standardized methodology to determine availability of the physical topology in a data center [1] [2]. There are\ncurrently four levels:",[994,45753,45754,45757,45760,45763],{},[997,45755,45756],{},"Tier I: Basic non-redundant site infrastructure. Non-redundant capacity components. Expected availability: 99.67%",[997,45758,45759],{},"Tier II: Redundant capacity components site infrastructure. Meets Tier I requirements. Provides redundant\ninfrastructure components. Expected availability: 99.74%",[997,45761,45762],{},"Tier III: Concurrently maintainable site infrastructure. Meets Tier II requirements. Multiple independent distribution\npaths service the IT equipment. All equipment must be dual-powered. Expected Availability: 99.98%",[997,45764,45765],{},"Tier IV: Fault tolerant site infrastructure. Meets Tier III requirements. All cooling equipment is independently\ndual-powered. Fault-tolerant electrical power storage and distribution. Expected availability of 99.995%",[439,45767,45768],{},"The difference between the tier availability seems next-to-nothing, but could be significant depending on the\napplication: Tier I allows ~1729 minutes of down time, whilst Tier IV would allow only 26 Minutes to meet the tier\ncertification criteria. Even though it’s hard enough to meet these criterias, it still should give you a general idea of\nwhat you are planning for: Determine your data center business goals, the possible/acceptable risk profile and the\navailable capital. This sets the foundation for a proper data center design.",[439,45770,45771],{},[448,45772,45773],{},"Don’t plan for now, plan for the future!",[439,45775,45776],{},"That would be the next step to consider. As i said in my last blog entry: You probably want to add some extra resources\nto easily be able to extend your infrastructure at a later time. Make sure to have all information necessary to properly\ndesign your future data center. What are you building the data center for? What kind of growth does your company expect\nin the future? Do you want to add the possibility to re-purpose your data center at any later point in time? Keep all\npossibilities in mind when adding additional resources that might be useful in the future.",[439,45778,45779],{},[448,45780,45781],{},"Cost of Ownership!",[439,45783,45784],{},"Always take your total cost of ownership (TCO) into account! It’s easy to get yourself trapped in focusing on initial\ncost only! It might not seem that expensive to build a data center (well, of course we could have a philosophical\ndiscussion about defining expensive here, but lets keep that for later). However, maintaining a data center can be\nstaggering. There are three basic TCO parameters:",[994,45786,45787,45790,45793],{},[997,45788,45789],{},"Capital expense: Basically building the whole thing – Not only the construction costs itself, but also all costs for\nplanning and designing are added here.",[997,45791,45792],{},"Operations and maintenance expense: Maintenance costs are the costs associated with proper maintenance of all\ninfrastructure, for instance license costs, maintenance contracts or soft/hardware upgrades. Operating costs are the\ncosts associated with the daily operation, for instance personnel, training and procedures.",[997,45794,45795],{},"Energy costs: It is essential to calculate any possible future development in energy costs. Electricity prices will be\na MAJOR parameter in the future, don’t get yourself trapped in underestimating future costs! Also, this is where you\nmight want to think about investing extra capital to optimize energy efficiency in your data center: A growing number\nof data centers are redirecting heat from their servers to air condition systems to heat their offices or support\ntheir hot water supply. There are lots of possibilities in how to use your data center to lower energy costs at your\nother locations [3].",[439,45797,45798],{},[448,45799,45800],{},"Select a proper data center site!",[439,45802,45803],{},"A common mistake is to start searching for what you think would be the perfect space to build a data center, before\nhaving your data center design in place. Without having a proper data center design, there is not much sense in spending\ntime looking for a possible location. The main problem with selecting a site before even having set up your design\nrequirements, is that the site possibly can not meet these later determined design requirements! While it surely is\nconvenient to have your data center located in a basement of your office building, it might turn out to be extremely\ncounterproductive if your data center is located right next to your buildings main water supply facilities. Depending on\nthe desired tier and/or security level, you have to take into consideration power availability (which, for instance,\nalso includes diesel supplies and contractors) , connectivity, accessibility and other possible geographic issues such\nas earth quakes, flooding, tornados and other disasters. [4]",[439,45805,45806],{},"Once you answered all the above questions, your next step will be to take care of all the “little” details such as\nthinking about your possible server hardware, calculating power efficiency, choosing a UPS solution, thinking about air\ncondition and about a thousand other things. I will try cover these topics step by step in the next couple of weeks.\nUntil then, feel free to comment, add any ideas of your own and point out any mistakes i made.",[439,45808,27166,45809],{},[1002,45810,45811],{"href":45811,"rel":45812},"http://www.fibertown.com/pdf/uptimeinstitutetiersystem.pdf",[1006],[439,45814,27173,45815],{},[1002,45816,45817],{"href":45817,"rel":45818},"http://www.gpxglobal.net/wp-content/uploads/2012/10/TIERSTANDARD_Topology_120801.pdf",[1006],[439,45820,45821,45822],{},"[3] ",[1002,45823,45824],{"href":45824,"rel":45825},"http://www.datacenterknowledge.com/archives/2011/03/18/energy-efficiency-guide-heat-recycling/",[1006],[439,45827,45828,45829],{},"[4] ",[1002,45830,45831],{"href":45831,"rel":45832},"http://www.datacenterknowledge.com/archives/2013/03/13/dont-delay-on-lessons-learned-from-sandy/",[1006],{"title":469,"searchDepth":507,"depth":507,"links":45834},[],[9045],"2014-04-07T15:39:29","In my last blog post,\\ni talked about all these little things you need to keep in mind when setting up a new or replace your current network\\ninfrastructure. I came up with that topic first, because in our case we are not only building a new data center, but we\\nare also building a whole new office location. Providing a working network infrastructure to the office floor was\\ncrucial in our case, that’s why we took care of that first.","https://synyx.de/blog/planning-and-designing-your-it-infrastructure-part-ii-evaluating-your-requirements-and-designing-your-data-center/",{},"/blog/planning-and-designing-your-it-infrastructure-part-ii-evaluating-your-requirements-and-designing-your-data-center",{"title":45727,"description":45842},"In my last blog post,\ni talked about all these little things you need to keep in mind when setting up a new or replace your current network\ninfrastructure. I came up with that topic first, because in our case we are not only building a new data center, but we\nare also building a whole new office location. Providing a working network infrastructure to the office floor was\ncrucial in our case, that’s why we took care of that first.","blog/planning-and-designing-your-it-infrastructure-part-ii-evaluating-your-requirements-and-designing-your-data-center",[],"In my last blog post, i talked about all these little things you need to keep in mind when setting up a new or replace your current network infrastructure. I…","xF4HnurSVbe2BotfX5b2kTAR1BMlzs5ay18xcSVC_Fc",{"id":45848,"title":45849,"author":45850,"body":45851,"category":45939,"date":45940,"description":45941,"extension":1034,"link":45942,"meta":45943,"navigation":916,"path":45944,"seo":45945,"slug":45946,"stem":45947,"tags":45948,"teaser":45949,"__hash__":45950},"blog/blog/devcamp_karlsruhe.md","DevCamp Karlsruhe: viele nette Leute, interessante Vorträge und jede Menge Spaß",[238,172],{"type":432,"value":45852,"toc":45937},[45853,45856,45859,45868,45877,45880,45883,45892,45895,45903,45908,45916,45919,45924,45927,45930],[435,45854,45849],{"id":45855},"devcamp-karlsruhe-viele-nette-leute-interessante-vorträge-und-jede-menge-spaß",[439,45857,45858],{},"Letztes Wochenende fand zum zweiten Mal das DevCamp in Karlsruhe statt. Nicht nur weil synyx auch dieses Mal als Sponsor\nauftritt, war ich beide Tage vor Ort. Nein, ich finde diese Art der Veranstaltungen einfach klasse. Eine lockere\nAtmosphäre, man trifft viele nette, aufgeschlossene Leute. Außerdem gefällt mir das Format. Es gibt hier keine\nZuschauer, sondern nur Mitwirkende. Jeder Teilnehmer bekommt dabei die Zeit, seinen Vortrag, Workshop oder eine einfache\nDiskussionsrunde durchzuführen.",[439,45860,45861],{},[1002,45862,45865],{"href":45863,"rel":45864},"https://media.synyx.de/uploads//2014/03/sessionplanung.jpg",[1006],[2205,45866],{"alt":45867,"src":45863},"sessionplanung",[439,45869,45870,45871,45876],{},"Diese Gelegenheit haben mein Kollege Christian und ich uns nicht entgehen lassen und haben gleich am Samstagmorgen eine\nSession zum Thema Devoxx4Kids gehalten. Wer sich dafür interessiert, kann sich die Slides auf\nder ",[1002,45872,45875],{"href":4175,"rel":45873,"title":45874},[1006],"D4K","deutschen D4K-Website"," anschauen. Natürlich gibt dort noch mehr Informationen\nzu dem Thema. In einer lockeren Atmosphäre hat es uns sehr viel Spaß gemacht, den Anwesenden die Devoxx4Kids näher zu\nbringen.",[439,45878,45879],{},"Ich ließ mich anschließend von meinen Kollegen noch zu einer weiteren Session „JavaScript – Bad Practises“ überreden.\nHey, und die Hälfte davon hab ich als Marketing-Mensch sogar noch verstanden. Dann war es auch schon Zeit für das\nMittagessen. Ich habe mir etwas Zeit gelassen, da der Andrang recht hoch war. Aber lecker war´s. Den Samstagnachmittag\nund den größten Teil des Sonntags, habe ich dann mit vielen Gesprächen mit sehr netten Menschen verbracht.",[439,45881,45882],{},"Mein Fazit: es war ein tolles Event mit tollen Leuten. Jeder, der vor hat irgendwann einen Vortrag zu halten, sollte\nsich diese Gelegenheit nicht entgehen lassen. In lockerer Runde macht das Spaß und das Lampenfieber geht ganz schnell\nweg. An dieser Stelle sei den Organisatioren, den Sponsoren, Fabian Beiner für seine tolle Moderation und zuletzt\nnatürlich auch ALLEN Teilnehmern gedankt, denn ohne die würde nichts funktionieren.",[439,45884,45885],{},[1002,45886,45889],{"href":45887,"rel":45888},"https://media.synyx.de/uploads//2014/03/devcamp.jpg",[1006],[2205,45890],{"alt":45891,"src":45887},"devcamp",[439,45893,45894],{},"Wie schon erwähnt, war mein Kollege Christian Mennerich auch dabei. Er hat ebenfalls eine kleine Zusammenfassung zum\nDevCamp geschrieben. Hier folgt sie:",[439,45896,45897,45898,45902],{},"Es gab viele technisch sehr interessante Vorträge auf dem DevCamp, die thematisch von Hausautomatisierung und\nHeimarbeitsorganisation über Suche und Java Performanceanalyse bis hin zu Programmierworkshops reichten. Wen es\ninteressiert was er verpasst hat, kann dies noch hier nachschauen: ",[1002,45899,45900],{"href":45900,"rel":45901},"http://lanyrd.com/2014/dcka/schedule/",[1006],". Ein großes\nThema waren immer wieder Webanwendungen, JavaScript und JavaScript Frameworks, aber auch die Graphdatenbank Neo4j wurde\nmit einem Vortrag beehrt. Im Folgenden werden für jeden Veranstaltungstag exemplarisch zwei Vorträge näher beschrieben,\ndie besonders gefallen haben.",[439,45904,45905],{},[448,45906,45907],{},"Der Samstag",[439,45909,45910,45911,1402],{},"Alexander Reelsen von ElasticSearch hat einen sehr guten Vortrag gehalten über die Kombination von ElasticSearch,\nLogStash und Kibana. Einführend motivierte er kurz die Notwendigkeit des zentralisierten Loggings, einerseits zur\nAnalyse der betriebenen Systeme und zur Früherkennung von Kapazitätenknappheit oder Angriffen, andererseits aber auch\nzur Informationsgewinnung z.B. zu Marketingzwecken. LogStash als Mittel der Wahl folgt hier im wesentlichem dem Muster\nEingabe –> Filterung –> Ausgabe. Daten können aus den verschiedensten Quellen in LogStash gelangen, darunter\nMonitoringtools, Datenbanken, Warteschlangen und viele andere. Ebenso vielfältig sind die Ausgabemöglichkeiten und\n-formate. Filter bieten zahlreiche Möglichkeiten, Daten zu konvertieren, anzureichern oder auszublenden. Exemplarisch\nhat Alexander demonstriert, wie syslog oder JSON Dateien eingelesen und in ElasticSearch gespeichert und durchsuchbar\ngemacht werden können. Mittels Kibana können Daten auf einfach konfigurierbaren Dashboards visualisiert werden. In einer\nschönen Live-Demo hat er gezeigt, wie sich beispielsweise die von meetup.com zur Verfügung gestellten Streams einfach\nindizieren lassen, um sie mittels Kibana und ElasticSearch analysieren und visualisieren zu können. Alexander hat seine\nVortragsfolien öffentlich zur Verfügung\ngestellt: ",[1002,45912,45915],{"href":45913,"rel":45914},"https://speakerdeck.com/elasticsearch/using%5C-elasticsearch%5C-logstash%5C-and%5C-kibana%5C-to%5C-create%5C-realtime%5C-dashboards",[1006],"https://speakerdeck.com/elasticsearch/using\\-elasticsearch\\-logstash\\-and\\-kibana\\-to\\-create\\-realtime\\-dashboards",[439,45917,45918],{},"Petar Petrov hat über seine Erfahrungen berichtet, die er gesammelt hat, als er neben seiner beruflichen Tätigkeit in 30\nNächten im Rahmen des von GitHub veranstalteten Game Off Wettbewerbs ein Spiel mit JavaScript programmiert hat. Das\nErgebnis heißt Psiral, hat guten Platz im Wettbewerb belegt und ist unter psiral.herokuapp.com zu bewundern und zu\nspielen. Neben den Schwierigkeiten der technischen Umsetzung des Spiels hat Petar psychologische Komponenten beleuchtet:\nDie Schwankungen der Motivation, die Schwierigkeiten der Organisation ein solches Projekt neben seinem eigentlichen\nBeruf zu realisieren und die Notwendigkeit zu schlafen! Gespickt war sein Erfahrungsbericht neben technischen Aspekten\nauch immer wieder von kurzen Anekdoten über die Zusammenarbeit mit der Spieldesignerin, die nebenbei auch seine Ehefrau\nist. Insgesamt ein sehr interessanter Bericht über die vielen Facetten der Projektplanung und -durchführung.",[439,45920,45921],{},[448,45922,45923],{},"Der Sonntag",[439,45925,45926],{},"Tim Suchanek hat einen Workshop gleich in zwei Teilen gehalten, um live zu demonstrieren wie gut MEAN funktioniert. MEAN\nsteht für MongoDB (eine dokumentenbasierte NoSQL Datenbank), Express (ein Webframework für Node.js), Angular.js (ein\nJavaScript MVC Framework) und Node.js (eine Laufzeitumgebung für JavaScript). Den Rahmen für beide Vorträge bildete ein\nonline Einkaufszettel (wage angelehnt an die Meet&Eat Plattform Crowddining, an der Tim mitarbeitet).Vorbereitet hatte\nTim lediglich die HTML und CSS Dateien für den fertigen Einkaufszettel, als als Ausgangsprojekt diente das auf GitHub\nzur Verfügung stehende angular-seed. Der erste Vortrag bestand aus der Erstellung eines Backends basierend auf der\nMongoDB, Node.js und Express. Vorbildlich testgetrieben entwickelte Tim in 45 Minuten eine REST-Schnittstelle, die die\nnötigen CRUD (create, read, update, delete) Operationen bereitstellt, um den Einkaufszettel zu füllen und zu bearbeiten.",[439,45928,45929],{},"Im zweiten Workshop zeigte er, wie mit Angular.js ein zugehöriges Frontend entwickelt werden kann. Zusätzlich kam neben\nAngular noch Restangular zur Anwendung. Tim demonstrierte, wie einfach es ist, Kenntnisse in der Technologie und den\nFramworks vorausgesetzt, die notwendigen Anzeigeelemente und -effekte zu generieren und mit Funktionalität füllen.",[439,45931,45932,45933,1402],{},"Der Workshop war sehr gut besucht und lief beeindruckend flüssig. Die wenigen kleinen Stolpersteine nutzte Tim\ngeschickt, um auch die Debuggingmöglichkeiten und -techniken zu demonstrieren. Der Einkaufszettel steht auf GitHub zur\nVerfügung: ",[1002,45934,45935],{"href":45935,"rel":45936},"https://github.com/crowddining/einkaufszettel",[1006],{"title":469,"searchDepth":507,"depth":507,"links":45938},[],[1031],"2014-03-27T13:33:56","Letztes Wochenende fand zum zweiten Mal das DevCamp in Karlsruhe statt. Nicht nur weil synyx auch dieses Mal als Sponsor\\nauftritt, war ich beide Tage vor Ort. Nein, ich finde diese Art der Veranstaltungen einfach klasse. Eine lockere\\nAtmosphäre, man trifft viele nette, aufgeschlossene Leute. Außerdem gefällt mir das Format. Es gibt hier keine\\nZuschauer, sondern nur Mitwirkende. Jeder Teilnehmer bekommt dabei die Zeit, seinen Vortrag, Workshop oder eine einfache\\nDiskussionsrunde durchzuführen.","https://synyx.de/blog/devcamp_karlsruhe/",{},"/blog/devcamp_karlsruhe",{"title":45849,"description":45858},"devcamp_karlsruhe","blog/devcamp_karlsruhe",[45891,18808],"Letztes Wochenende fand zum zweiten Mal das DevCamp in Karlsruhe statt. Nicht nur weil synyx auch dieses Mal als Sponsor auftritt, war ich beide Tage vor Ort. Nein, ich finde…","RWDl9w7n9CqOsnEJtVA-q430j1GB5n2KkFugrOv6wU4",{"id":45952,"title":45953,"author":45954,"body":45955,"category":46094,"date":46095,"description":46096,"extension":1034,"link":46097,"meta":46098,"navigation":916,"path":46099,"seo":46100,"slug":45959,"stem":46101,"tags":46102,"teaser":46103,"__hash__":46104},"blog/blog/planning-and-designing-your-it-infrastructure-part-i-networking.md","Planning and designing your IT Infrastructure – Part I: Networking",[178],{"type":432,"value":45956,"toc":46092},[45957,45960,45963,45969,45977,45980,45983,45988,45991,45996,45999,46010,46015,46018,46023,46026,46031,46034,46039,46053,46056,46059,46062,46067,46070,46078,46086,46089],[435,45958,45953],{"id":45959},"planning-and-designing-your-it-infrastructure-part-i-networking",[439,45961,45962],{},"Welcome 2014! This year will be pretty big for us here at synyx – business has been doing well and in the last couple of\nyears, we have been grown to, as of now, employ a dedicated team of almost 50 individuals. The downside though: We are\nquickly running out of office space. Guess what, it’s time to move!",[439,45964,45965],{},[2205,45966],{"alt":45967,"src":45968},"Our new office. In the faaaar back: Our CEO Thomas Kraft","https://media.synyx.de/uploads//2014/02/Tormod_GrossesBuero.jpg",[994,45970,45971,45974],{},[997,45972,45973],{},"The first goal was to replace our partially somewhat improvised network layout with a well documented high performance\nnetwork infrastructure.",[997,45975,45976],{},"The second goal was to design and build a dedicated server room to improve hardware life & management, maintenance,\nserver availability and security.",[439,45978,45979],{},"Planning a network infrastructure sounds easy: Just buy a few switches, a bunch of network cables, throw it all together\nand there you go, right? Wrong! Designing a reliable network layout can be a very challenging task: You rely on your\nnetwork to deliver information internally and externally to coworkers as well as to clients. Network vulnerabilities and\nreliability issues can prevent your data from moving efficiently and can even hinder productivity in your office, so it\nis essential to plan ahead to provide a network infrastructure that is fast, efficient, able to scale quickly and also\naffordable. Let me give you a few basic ideas on what you need to keep in mind when planning for a new network\ninfrastructure!",[439,45981,45982],{},"First, you need to clarify a few key elements:",[439,45984,45985],{},[448,45986,45987],{},"What networking hardware do you already have in place and are you planning any changes to your technology now or in\nthe future (such as upcoming technology standards, additional employees or new applications) that might have an impact\non your networking needs?",[439,45989,45990],{},"As with most complex technologies, there is no “one fits all” solution in networking. The needs and resources of each\ncompany will lead to a different set of networking solutions and you need to carefully consider the current\ninfrastructure layout and the future requirements to determine the optimal network design for your situation. As with\nall other aspects of IT, there is a wild variety of hardware, software, standards and an almost unlimited amount of\nterms and concepts to keep track of. At thorough knowledge about current and upcoming technologies is required to make\nsmart, effective design decisions.",[439,45992,45993],{},[448,45994,45995],{},"Are you planning to build a LAN, WAN or CAN infrastructure?",[439,45997,45998],{},"The scale of your network is the most important decision in planning your network, as it will influence all of your\nfollowing decisions:",[994,46000,46001,46004,46007],{},[997,46002,46003],{},"A local area network (LAN) is usually implemented in a single office or even a single building. In most cases, it\nconnects the local office IT (as in: Your employees’ computers) within your organization as well as with the internet.",[997,46005,46006],{},"A wide area network (WAN) connects a single office or a single building to a larger network, like it’s parent\norganizations network. WANs links usually are maintained by ISPs or telecommunication companies and vary in terms of\nbandwidth and costs.",[997,46008,46009],{},"A campus area network (CAN) connects multiple LANs within the same organization in close proximity. Like WANS, CANs\nlinks are most often maintained by ISPs or telecommunication companies.",[439,46011,46012],{},[448,46013,46014],{},"What network applications do your users rely on the most, and how much bandwidth do these applications need?",[439,46016,46017],{},"Are you providing access to file and print servers? Do you rely on local resources or do you need to provide connections\nto remote logins? Do you plan on using Vlans or restrict acces to certain networks? Do users need access to database or\napplication servers and if so, are they regularly moving large amounts of data throughout the network?",[439,46019,46020],{},[448,46021,46022],{},"How many employees and therefore how many networked devices do you have? Will this number change in the future?",[439,46024,46025],{},"Depending on your business, you might want to add some extra resources to easily be able to extend your network at a\nlater time. Don’t plan for now, plan for the future!",[439,46027,46028],{},[448,46029,46030],{},"Does your office layout impose any constrains on your planning? Is there available room in your floors or ceilings to\nrun network cables?",[439,46032,46033],{},"Find out if your office layout might pose any problems to your network infrastructure. If you can, run dedicated and\ndocumented conduits exclusively for your network infrastructure.",[439,46035,46036],{},[448,46037,46038],{},"What physical hardware and software components are needed to implement the network infrastructure you are planning?",[994,46040,46041,46044,46047,46050],{},[997,46042,46043],{},"Where will your hardware components be located?",[997,46045,46046],{},"How are the hardware components connected?",[997,46048,46049],{},"How are the hardware components to be installed?",[997,46051,46052],{},"How are the hardware components to be configured?",[439,46054,46055],{},"Again, don’t plan for now, plan for the future! When you are designing and building a network infrastructure from\nscratch, are you going with yesterdays standard or are you going to install something that will meet not only todays but\nalso future needs? Labor is the most expensive part of your project, so investing in high-end cabling without using\nit’s full potential today, might seem a little bit pricier now, but will be cheaper than retreating to low-end cabling\nthat you need to replace 5 years from now in the end!",[439,46057,46058],{},"Also, make sure to not run your network cabling parallel with your electrical installation! Unshielded and even shielded\ncabling communication can be disrupted by the magnetic field created by electric cables, making your bandwidth drop\nconsiderably! Also, keep your network cabling away from devices like fluorescent lighting, fans, a/c units and similar\nitems.",[439,46060,46061],{},"Another thing to keep in mind, is to use cable management. Adding a network rack and planning a nice rack-based cable\nmanagement, will add to the cost of your project, but makes maintenance much much easier! Verify cable lengths and\nspecifications. Also, make sure to test your network infrastructure after installation! You should test every cable\nusing appropriate tools to make sure it is suitable for its intended use.",[439,46063,46064],{},[448,46065,46066],{},"And last, but not least: What budget do you have available for the installation and maintenance of your network\ninfrastructure?",[439,46068,46069],{},"Once you determined the above factors, the remaining planning process – theoretically speaking – pretty simple and\nstraight forward:",[994,46071,46072,46075],{},[997,46073,46074],{},"Purchase, assemble and install the necessary hardware (the physical infrastructure).",[997,46076,46077],{},"Install and configure the software and necessary applications (the logical infrastructure).",[439,46079,46080,46081,17548],{},"(A quick note: At this point, i could go on explaining the OSI model and lose myself in details… I don’t think doing so\nis necessary here. For those of you who do need more information about this topic, i recommend having a look\nat ",[1002,46082,46085],{"href":46083,"rel":46084},"http://en.wikipedia.org/wiki/OSI%5C_model",[1006],"http://en.wikipedia.org/wiki/OSI\\_model",[439,46087,46088],{},"I don’t want to go too much into detail about our own Infrastructure and Hardware (for obvious reasons), so i just wrote\ndown some basic ideas and best practices that might help you get some ideas when planning your own network\ninfrastructure. If you want to add some more ideas or point out some stupid mistakes that i made, feel free to\ncomment :).",[439,46090,46091],{},"The second goal in our IT infrastructure – planning and building a server room – is a little more complex than “just\ndesigning and installing” a network infrastructure, so i will follow up with a series of blogposts about doing so in the\nnext couple of weeks. Bare with me, please 🙂",{"title":469,"searchDepth":507,"depth":507,"links":46093},[],[9045],"2014-02-18T10:58:09","Welcome 2014! This year will be pretty big for us here at synyx – business has been doing well and in the last couple of\\nyears, we have been grown to, as of now, employ a dedicated team of almost 50 individuals. The downside though: We are\\nquickly running out of office space. Guess what, it’s time to move!","https://synyx.de/blog/planning-and-designing-your-it-infrastructure-part-i-networking/",{},"/blog/planning-and-designing-your-it-infrastructure-part-i-networking",{"title":45953,"description":45962},"blog/planning-and-designing-your-it-infrastructure-part-i-networking",[],"Welcome 2014! This year will be pretty big for us here at synyx – business has been doing well and in the last couple of years, we have been grown…","GkSzUp7ZFShRsZrYBrIOpBrOwwXm7IeUTeOXtn40G74",{"id":46106,"title":46107,"author":46108,"body":46109,"category":46180,"date":46181,"description":46182,"extension":1034,"link":46183,"meta":46184,"navigation":916,"path":46185,"seo":46186,"slug":46188,"stem":46189,"tags":46190,"teaser":46194,"__hash__":46195},"blog/blog/fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis.md","FAIRantwortung übernehmen ist nicht nur ein Lippenbekenntnis",[172],{"type":432,"value":46110,"toc":46178},[46111,46114,46124,46127,46130,46133,46136,46139,46156,46165,46175],[435,46112,46107],{"id":46113},"fairantwortung-übernehmen-ist-nicht-nur-ein-lippenbekenntnis",[439,46115,46116,46117,46123],{},"Am 22.01.14 fand im IHK Bildungszentrum das\nerste ",[1002,46118,46122],{"href":46119,"rel":46120,"title":46121},"http://www.fairantwortung.net/",[1006],"Fairantwortung","Karlsruher Unternehmerforum"," statt. Thema war: Verantwortung\nübernehmen, Flagge zeigen, Zukunft sichern.",[439,46125,46126],{},"Da wir von synyx dieses Thema, auch Corporate Social Responsibility genannt, ebenfalls als sehr wichtig empfinden und\nbereits entsprechend Maßnahmen ergriffen haben, war mir schnell klar, dass ich an dieser Veranstaltung teilnehmen\nmöchte. Einerseits um weitere Ideen zu sammeln, andererseits natürlich auch, um neue Kontakte zu knüpfen.",[439,46128,46129],{},"Ich möchte hier gar nicht lange über die Vorträge oder Impulse eingehen, das würde den Rahmen sprengen.",[439,46131,46132],{},"Dennoch gab es Talks, welche einen bleibenden Eindruck hinterlassen haben. So z. B. Torsten Matthias, Marketingleiter\nder FRoSTA AG. Er hat ohne Umschweife aufgezeigt, wie sich die Umsätze des Unternehmens entwickelt haben. Vor allem die\nsinkenden Umsätze nach der Einführung von nachhaltigen Produkten. Erstaunlich fand ich, dass die FRoSTA AG trotz der\nnegativen Zahlen an die Idee der Nachhaltigkeit geglaubt haben und diesen Weg weiter gegangen sind. Letztlich hat sich\ndas ausgezahlt.",[439,46134,46135],{},"Auch die Aussagen von Max Wittrock, Gründer der mymuesli GmbH, in der Podiumsdiskussion hatten Hand und Fuß.\nBeeindruckend wie ein junges Unternehmen sich zu diesen Themen platziert. Im Übrigen war das ebenfalls ein Thema: die\nveränderten Werte der Generation Y (“Why”). Das spielt vor allem für Arbeitgeber und Personaler eine Rolle. Ihre Werte\nwerden in Zukunft immer mehr Raum in den Unternehmen einnehmen.",[439,46137,46138],{},"Fazit: Es war eine gelungene Veranstaltung, welche mit vielen Impulsen anregen konnte.",[439,46140,46141,46142,46148,46149,46155],{},"Wie ich eingangs schon erwähnt hatte, ist das Thema CSR für synyx nicht nur eine Floskel. Wir haben bereits viele\nMaßnahmen ergriffen, aber es ist auch noch einiges zu tun. Wie heißt es so schön? Der Weg ist das Ziel. Wir sind\nvielleicht nicht in der Lage die Welt zu verbessern, aber wir können unseren Teil dazu beitragen. Und das muss nicht\nimmer viel Geld kosten. Natürlich ist es nicht damit getan, Umwelt-Toilettenpapier zu verwenden, so eine Aussage bei\nder Veranstaltung. Jedoch ist ein Anfang. Dann lasst uns doch einfach weiter machen. Wir beziehen z. B. unseren Kaffee\nvon ",[1002,46143,46147],{"href":46144,"rel":46145,"title":46146},"http://www.cafe-libertad.de",[1006],"Cafe Libertad","Café Libertad Kollektiv e.G","., oder unser Wasser\nvon ",[1002,46150,46154],{"href":46151,"rel":46152,"title":46153},"http://www.vivaconagua.org/",[1006],"Viva con agua","Viva con Agua"," und unsere anderen Getränke beziehen wir nicht über die\ngroßen Konzerne, sondern auch hier unterstützen wir kleine Marken. Als IT-Unternehmen mit hohem Stromverbrauch ist es\nfür uns selbstverständlich, auf 100% Öko-Strom zu setzen.",[439,46157,46158,46159,46164],{},"Ergänzend stehen die Mitarbeiter bei synyx ebenfalls im Mittelpunkt. Seit 2008 sind wir Mitglied\nbei ",[1002,46160,46163],{"href":46161,"rel":46162,"title":46163},"https://web.archive.org/web/20140924063622/http://faircompany.karriere.de/",[1006],"Fair Company",". Unseren\nMitarbeitern stehen jährlich 2.000 EUR Fortbildungsbudget zur Verfügung. Auch flexible Arbeitszeiten und die\nindividuelle Arbeitsplatzgestaltung gehören dazu.",[439,46166,46167,46168,46174],{},"Es gibt so viele Möglichkeiten, sich als Unternehmen zu engagieren. Und nicht alles kostet Unmengen an Geld. Doch was\nhaben die Unternehmen davon? Wenn diese Philosophie mehr als ein Lippenbekenntnis ist, liegen die Vorteile auf der Hand:\nzufriedene Mitarbeiter, ein gutes Team und letztlich das gute Gefühl, die Welt ein kleines Bisschen zu verbessern. Auf\nunserer ",[1002,46169,46173],{"href":46170,"rel":46171,"title":46172},"http://www.synyx.de/unternehmen/verantwortung_csr/",[1006],"Verantwortung","Hompage"," kann man unsere Maßnahmen\nnachlesen.",[439,46176,46177],{},"Wir sind von der Initiative FAIRantwortung begeistert. Deshalb sind wir als FAIRpeople Förderer der Initiative.",{"title":469,"searchDepth":507,"depth":507,"links":46179},[],[1031],"2014-02-04T15:10:42","Am 22.01.14 fand im IHK Bildungszentrum das\\nerste Karlsruher Unternehmerforum statt. Thema war: Verantwortung\\nübernehmen, Flagge zeigen, Zukunft sichern.","https://synyx.de/blog/fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis/",{},"/blog/fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis",{"title":46107,"description":46187},"Am 22.01.14 fand im IHK Bildungszentrum das\nerste Karlsruher Unternehmerforum statt. Thema war: Verantwortung\nübernehmen, Flagge zeigen, Zukunft sichern.","fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis","blog/fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis",[46191,46192,46193],"csr","nachhaltigkeit","verantwortung","Am 22.01.14 fand im IHK Bildungszentrum das erste Karlsruher Unternehmerforum statt. Thema war: Verantwortung übernehmen, Flagge zeigen, Zukunft sichern. Da wir von synyx dieses Thema, auch Corporate Social Responsibility genannt,…","kJctTGDkJ8CNpWRwP81uhVn_cKtYR7rXVkZ8mDR7aTs",{"id":46197,"title":46198,"author":46199,"body":46200,"category":47219,"date":47220,"description":469,"extension":1034,"link":47221,"meta":47222,"navigation":916,"path":47223,"seo":47224,"slug":46204,"stem":47225,"tags":47226,"teaser":47233,"__hash__":47234},"blog/blog/code-gluse.md","Code gluse",[42],{"type":432,"value":46201,"toc":47213},[46202,46205,46208,46215,46218,46221,46224,46227,46234,46241,46244,46247,46254,46324,46327,46330,46358,46365,46417,46420,46612,46615,46618,46622,46625,46642,46645,46648,46655,46658,46661,46668,46675,46704,46707,46710,46742,46745,46748,46801,46806,46809,46812,46815,46911,46913,46917,46920,46925,46932,46935,46938,46941,46943,46963,46966,46994,47104,47107,47110,47113,47116,47119,47122,47125,47128,47210],[435,46203,46198],{"id":46204},"code-gluse",[1065,46206,46198],{"id":46207},"code-gluse-1",[439,46209,46210,46211,46214],{},"Today’s post targets an API, which has been released on Dec. 11, 2006; the ",[471,46212,46213],{},"javax.scripting"," package [1] and a lot of\ngood articles that have been written around it.",[439,46216,46217],{},"The intention for this post is not about ‘how to use the scripting packaging’, but about gluse. So what do I mean with\nthe phrase gluse? Gluse is a coinage",[439,46219,46220],{},"for glue and (re)usage. As many of the Java developer know about the plenty of good libraries from maven central /\ngithub and the integration process, a few of them",[439,46222,46223],{},"might ask how to integrate libraries from other languages as well. As many of the every day problems have already bean\naddressed, there is a good chance that someone else has done the job for you and is willing to share.",[439,46225,46226],{},"Sometimes it’s written in pure Java, sometimes in a different language. Let’s see how to integrate the latter\nlibraries. (StackOverflow lists a dozen [2] btw.)",[439,46228,46229,46230,46233],{},"The next parts will give you some information in form of three examples. The first and second example will address\nJavascript, as Javascript is getting more and more into the focus of developers and Oracle will ship their new engine\n‘Nashorn’ with the next Java 8 release, while the third example will target a more complex example using JRuby. ",[448,46231,46232],{},"All\nexamples"," can be downloaded from [3]. So it’s up to you if you want to read the sources in parallel, afterwards, or by\nplaying with the code in your IDE instantly.",[439,46235,46236,46237,46240],{},"All code examples have been written to be compatible with Java 1.6. See the note in example two, when it comes to the\n",[471,46238,46239],{},"bind"," function.",[1065,46242,46243],{"id":29263},"Proxy",[439,46245,46246],{},"Lets discuss the first example: We want to replace parts of a string using a regular expression, but hook into the\nprocess of manipulating the matching elements, before the replacement eventually takes place – by adding some new\ncontent or returning something completely different.",[439,46248,46249,46250,46253],{},"In Java you would probably end up using the ",[471,46251,46252],{},"java.util.regex.Pattern",", creating a matcher and iterating over the matched\ngroups and so on. There’s nothing wrong about it, but Javascript already defines that kind of behaviour [4].",[464,46255,46257],{"className":15199,"code":46256,"language":15201,"meta":469,"style":469},"\"first 000 second\".replace(/[a-zA-Z]+/g, function (match) {\n return \"[\" + match.toUpperCase() + \"]\";\n});\n",[471,46258,46259,46294,46320],{"__ignoreMap":469},[474,46260,46261,46264,46266,46269,46271,46273,46276,46278,46280,46283,46285,46287,46289,46292],{"class":476,"line":477},[474,46262,46263],{"class":484},"\"first 000 second\"",[474,46265,1402],{"class":503},[474,46267,46268],{"class":480},"replace",[474,46270,1483],{"class":503},[474,46272,6875],{"class":484},[474,46274,46275],{"class":510},"[a-zA-Z]",[474,46277,23407],{"class":810},[474,46279,6875],{"class":484},[474,46281,46282],{"class":810},"g",[474,46284,520],{"class":503},[474,46286,14021],{"class":810},[474,46288,15250],{"class":503},[474,46290,46291],{"class":14037},"match",[474,46293,23418],{"class":503},[474,46295,46296,46298,46301,46304,46307,46310,46313,46315,46318],{"class":476,"line":507},[474,46297,23910],{"class":810},[474,46299,46300],{"class":484}," \"[\"",[474,46302,46303],{"class":810}," +",[474,46305,46306],{"class":503}," match.",[474,46308,46309],{"class":480},"toUpperCase",[474,46311,46312],{"class":503},"() ",[474,46314,23407],{"class":810},[474,46316,46317],{"class":484}," \"]\"",[474,46319,2139],{"class":503},[474,46321,46322],{"class":476,"line":547},[474,46323,15357],{"class":503},[439,46325,46326],{},"As both, Rhino and Nashorn, support the javax.script.Invocable type, we will create an interface to address the\nproblem – you’ll find the whole documentation in the mentioned project [3], but for the sake of completeness:",[439,46328,46329],{},"Apply the ‘pattern’ on the ‘sequence’ and call the ‘callback’ on each matched element. Either on ‘all’ matching\nelements, or on ‘any’ (first makes sense here).",[464,46331,46333],{"className":709,"code":46332,"language":711,"meta":469,"style":469},"\n public interface Replacement {\n public abstract CharSequence any (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n public abstract CharSequence all (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n }\n\n",[471,46334,46335,46339,46344,46349,46354],{"__ignoreMap":469},[474,46336,46337],{"class":476,"line":477},[474,46338,917],{"emptyLinePlaceholder":916},[474,46340,46341],{"class":476,"line":507},[474,46342,46343],{}," public interface Replacement {\n",[474,46345,46346],{"class":476,"line":547},[474,46347,46348],{}," public abstract CharSequence any (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[474,46350,46351],{"class":476,"line":584},[474,46352,46353],{}," public abstract CharSequence all (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[474,46355,46356],{"class":476,"line":607},[474,46357,1276],{},[439,46359,46360,46361,46364],{},"The final Java code would look like the following (Java 8 users will flavour the new lambda syntax:\n",[471,46362,46363],{},"(match) -> { return \"[\" + match + \"]\"; }","):",[464,46366,46368],{"className":709,"code":46367,"language":711,"meta":469,"style":469},"\n Replacement replacement;\n replacement = replacement ();\n CharSequence enclosed = replacement.all (Pattern.compile (\"\\\\d+\"), \"could you please enclose 1234, 789, 345 with brackets?\", new Function\u003CCharSequence, CharSequence> () {\n @Override\n public CharSequence apply (CharSequence sequence) {\n return \"[\" + sequence + \"]\";\n }\n });\n /* replacement () returns a proxy of the type Replacement, using the shipped js scripting engine. the evaluated script returns an instance, which can be encapsulated using the Invocable#getInterface signature */\n\n",[471,46369,46370,46374,46379,46384,46389,46394,46399,46404,46408,46412],{"__ignoreMap":469},[474,46371,46372],{"class":476,"line":477},[474,46373,917],{"emptyLinePlaceholder":916},[474,46375,46376],{"class":476,"line":507},[474,46377,46378],{}," Replacement replacement;\n",[474,46380,46381],{"class":476,"line":547},[474,46382,46383],{}," replacement = replacement ();\n",[474,46385,46386],{"class":476,"line":584},[474,46387,46388],{}," CharSequence enclosed = replacement.all (Pattern.compile (\"\\\\d+\"), \"could you please enclose 1234, 789, 345 with brackets?\", new Function\u003CCharSequence, CharSequence> () {\n",[474,46390,46391],{"class":476,"line":607},[474,46392,46393],{}," @Override\n",[474,46395,46396],{"class":476,"line":642},[474,46397,46398],{}," public CharSequence apply (CharSequence sequence) {\n",[474,46400,46401],{"class":476,"line":663},[474,46402,46403],{}," return \"[\" + sequence + \"]\";\n",[474,46405,46406],{"class":476,"line":694},[474,46407,5704],{},[474,46409,46410],{"class":476,"line":700},[474,46411,15314],{},[474,46413,46414],{"class":476,"line":913},[474,46415,46416],{}," /* replacement () returns a proxy of the type Replacement, using the shipped js scripting engine. the evaluated script returns an instance, which can be encapsulated using the Invocable#getInterface signature */\n",[439,46418,46419],{},"The Javascript implementation would look like:",[464,46421,46423],{"className":15199,"code":46422,"language":15201,"meta":469,"style":469},"\n(function () {\n function replace (regex, content, callback) {\n ...\n }\n var Replacement = function () {};\n Replacement.prototype.any = function (regex, content, callback) {\n return replace (new RegExp (regex), content, callback);\n };\n Replacement.prototype.all = function (regex, content, callback) {\n return replace (new RegExp (regex, 'g'), content, callback);\n };\n return new Replacement ();\n}) ();\n\n",[471,46424,46425,46429,46437,46461,46465,46469,46484,46517,46535,46540,46570,46591,46595,46607],{"__ignoreMap":469},[474,46426,46427],{"class":476,"line":477},[474,46428,917],{"emptyLinePlaceholder":916},[474,46430,46431,46433,46435],{"class":476,"line":507},[474,46432,1483],{"class":503},[474,46434,14021],{"class":810},[474,46436,29622],{"class":503},[474,46438,46439,46442,46445,46447,46450,46452,46454,46456,46459],{"class":476,"line":547},[474,46440,46441],{"class":810}," function",[474,46443,46444],{"class":480}," replace",[474,46446,15250],{"class":503},[474,46448,46449],{"class":14037},"regex",[474,46451,520],{"class":503},[474,46453,34586],{"class":14037},[474,46455,520],{"class":503},[474,46457,46458],{"class":14037},"callback",[474,46460,23418],{"class":503},[474,46462,46463],{"class":476,"line":584},[474,46464,31207],{"class":810},[474,46466,46467],{"class":476,"line":607},[474,46468,1276],{"class":503},[474,46470,46471,46474,46477,46479,46481],{"class":476,"line":642},[474,46472,46473],{"class":810}," var",[474,46475,46476],{"class":480}," Replacement",[474,46478,1661],{"class":810},[474,46480,29842],{"class":810},[474,46482,46483],{"class":503}," () {};\n",[474,46485,46486,46489,46491,46494,46496,46499,46501,46503,46505,46507,46509,46511,46513,46515],{"class":476,"line":663},[474,46487,46488],{"class":510}," Replacement",[474,46490,1402],{"class":503},[474,46492,46493],{"class":510},"prototype",[474,46495,1402],{"class":503},[474,46497,46498],{"class":480},"any",[474,46500,1661],{"class":810},[474,46502,29842],{"class":810},[474,46504,15250],{"class":503},[474,46506,46449],{"class":14037},[474,46508,520],{"class":503},[474,46510,34586],{"class":14037},[474,46512,520],{"class":503},[474,46514,46458],{"class":14037},[474,46516,23418],{"class":503},[474,46518,46519,46522,46524,46526,46529,46532],{"class":476,"line":694},[474,46520,46521],{"class":810}," return",[474,46523,46444],{"class":480},[474,46525,15250],{"class":503},[474,46527,46528],{"class":810},"new",[474,46530,46531],{"class":480}," RegExp",[474,46533,46534],{"class":503}," (regex), content, callback);\n",[474,46536,46537],{"class":476,"line":700},[474,46538,46539],{"class":503}," };\n",[474,46541,46542,46544,46546,46548,46550,46552,46554,46556,46558,46560,46562,46564,46566,46568],{"class":476,"line":913},[474,46543,46488],{"class":510},[474,46545,1402],{"class":503},[474,46547,46493],{"class":510},[474,46549,1402],{"class":503},[474,46551,38086],{"class":480},[474,46553,1661],{"class":810},[474,46555,29842],{"class":810},[474,46557,15250],{"class":503},[474,46559,46449],{"class":14037},[474,46561,520],{"class":503},[474,46563,34586],{"class":14037},[474,46565,520],{"class":503},[474,46567,46458],{"class":14037},[474,46569,23418],{"class":503},[474,46571,46572,46574,46576,46578,46580,46582,46585,46588],{"class":476,"line":920},[474,46573,46521],{"class":810},[474,46575,46444],{"class":480},[474,46577,15250],{"class":503},[474,46579,46528],{"class":810},[474,46581,46531],{"class":480},[474,46583,46584],{"class":503}," (regex, ",[474,46586,46587],{"class":484},"'g'",[474,46589,46590],{"class":503},"), content, callback);\n",[474,46592,46593],{"class":476,"line":926},[474,46594,46539],{"class":503},[474,46596,46597,46599,46602,46604],{"class":476,"line":932},[474,46598,23104],{"class":810},[474,46600,46601],{"class":810}," new",[474,46603,46476],{"class":480},[474,46605,46606],{"class":503}," ();\n",[474,46608,46609],{"class":476,"line":938},[474,46610,46611],{"class":503},"}) ();\n",[439,46613,46614],{},"The Java code for this example would probably be less – measured in LOC – but the basic steps needed for an integration\ncan be shown pretty good and two worlds might benefit from your certainly approved works 🙂",[439,46616,46617],{},"One nice feature about this kind of mechanism is, that you can quickly prototype your functionality, while still able to\nchange parts of the implementation using pure Java afterwards.",[1065,46619,46621],{"id":46620},"modularity","Modularity",[439,46623,46624],{},"Let’s come to the second example. You may have written a bunch of Javascript files in a modular way or just don’t want\nto put everything in a single file. While the first example showed how to proxy your implementation, the second example\nwill show you a basic approach for dynamically loading further resource and/or code files. The following signature\nshould be provided and accessible from all scripts.",[464,46626,46628],{"className":15199,"code":46627,"language":15201,"meta":469,"style":469},"require(\"org.geonames.reverse\");\n",[471,46629,46630],{"__ignoreMap":469},[474,46631,46632,46635,46637,46640],{"class":476,"line":477},[474,46633,46634],{"class":480},"require",[474,46636,1483],{"class":503},[474,46638,46639],{"class":484},"\"org.geonames.reverse\"",[474,46641,1495],{"class":503},[439,46643,46644],{},"The similarity to requirejs [5] is intentional and you may want to extend the signature to be fully compliant, but this\nwill be left for your curiosity 🙂",[439,46646,46647],{},"Loading resources from ‘unknown’, or from ‘at runtime unknown’ sources is by nature critical, as the code is executed in\nthe same JVM which hosts your application as well. Therefore, you should only load resources you really trust.",[439,46649,46650,46651,46654],{},"You could achieve this by verifying the signature of the reviewed files using a PKI [6] infrastructure – ",[471,46652,46653],{},"javax.crypto","\nis your friend here and fortunately you can implement this in Java and/or use a security library to accomplish this\ntask.",[439,46656,46657],{},"Simply spoken: Always check the integrity if you provide a way for modifications.",[439,46659,46660],{},"If you are already familiar with the scripting engine API, you might have noticed that require is a function and not an\n‘object’.",[439,46662,46663,46664,46667],{},"Even when functions in Javascript are objects, there is no semantic way to say that ",[990,46665,46666],{},"“this object is a function and can\nbe invoked”"," if you share it between the environment.",[439,46669,46670,46671,46674],{},"There might be support for some engines, but not for the others and ",[471,46672,46673],{},"javax.script"," API is designed for general purpose –\ndepending on engine internal interfaces is not desired.",[464,46676,46678],{"className":15199,"code":46677,"language":15201,"meta":469,"style":469},"obj.require(\n \"org.geonames.reverse\",\n); /* nah, okay but requires additional knowledge obj. */\n",[471,46679,46680,46689,46696],{"__ignoreMap":469},[474,46681,46682,46685,46687],{"class":476,"line":477},[474,46683,46684],{"class":503},"obj.",[474,46686,46634],{"class":480},[474,46688,1555],{"class":503},[474,46690,46691,46694],{"class":476,"line":507},[474,46692,46693],{"class":484}," \"org.geonames.reverse\"",[474,46695,4715],{"class":503},[474,46697,46698,46701],{"class":476,"line":547},[474,46699,46700],{"class":503},"); ",[474,46702,46703],{"class":2277},"/* nah, okay but requires additional knowledge obj. */\n",[439,46705,46706],{},"Fortunately there is a solution. You can attach a script context to the evaluation process and reuse the context later\non, but you shouldn’t use the internal context as it could leak if your engine leaks.",[439,46708,46709],{},"Pseudo Algorithm:",[8310,46711,46712,46730,46733,46736,46739],{},[997,46713,46714,46715,520,46718,520,46721,520,46724,46726,46727],{},"create a java function object which can load your resources from the ",[990,46716,46717],{},"classpath",[990,46719,46720],{},"internet",[990,46722,46723],{},"local filesystem",[990,46725,27402],{},"\nwith a method signature you know. (a function/SAM object) like: ",[471,46728,46729],{},"void apply (String)",[997,46731,46732],{},"create a script context and attach the object from 1. to it with a variable called ‘whatever’ (really whatever name\nyou like)",[997,46734,46735],{},"evaluate an inline require function before you evaluate your business code, which puts your require function into the\nscope of the context from 2.",[997,46737,46738],{},"evaluate your business codes which relies on the require function with the same scope from 2.",[997,46740,46741],{},"have fun",[439,46743,46744],{},"var require = function (library) { whatever.apply (library); }",[439,46746,46747],{},"The above code would be sufficient, but has some drawbacks as it only works if the ‘whatever’ object is in the correct\nexecution scope and if it provides the correct signature – someone could overwrite the binding or does something you\nsimply don’t want him/her to do. We need some slight improvements to correct this.",[464,46749,46751],{"className":15199,"code":46750,"language":15201,"meta":469,"style":469},"var require = function (library) {\n this.apply(library);\n}.bind(whatever);\ndelete whatever;\n",[471,46752,46753,46771,46783,46793],{"__ignoreMap":469},[474,46754,46755,46758,46760,46762,46764,46766,46769],{"class":476,"line":477},[474,46756,46757],{"class":810},"var",[474,46759,30809],{"class":480},[474,46761,1661],{"class":810},[474,46763,29842],{"class":810},[474,46765,15250],{"class":503},[474,46767,46768],{"class":14037},"library",[474,46770,23418],{"class":503},[474,46772,46773,46775,46777,46780],{"class":476,"line":507},[474,46774,24106],{"class":510},[474,46776,1402],{"class":503},[474,46778,46779],{"class":480},"apply",[474,46781,46782],{"class":503},"(library);\n",[474,46784,46785,46788,46790],{"class":476,"line":547},[474,46786,46787],{"class":503},"}.",[474,46789,46239],{"class":480},[474,46791,46792],{"class":503},"(whatever);\n",[474,46794,46795,46798],{"class":476,"line":584},[474,46796,46797],{"class":810},"delete",[474,46799,46800],{"class":503}," whatever;\n",[11947,46802,46803],{},[439,46804,46805],{},"“The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a\ngiven sequence of arguments preceding any provided when the new function is called.” [7]",[439,46807,46808],{},"If the bind function is not available by your Rhino environment, you may want to look at the implementation from\nPrototype [8] et al. and add it manually with the same procedure.",[439,46810,46811],{},"We delete the ‘whatever’ object from the script context afterwards.",[439,46813,46814],{},"You need the following Java code as glue:",[464,46816,46818],{"className":709,"code":46817,"language":711,"meta":469,"style":469},"\n// internal context for variables\nfinal Bindings bindings = scripting.newBindings ();\n bindings.put (Importer, new Function\u003CString, Void> () {\n // load a library 'argument' from the 'lib' directory\n @Override\n public Void apply (String argument) {\n // trusting returns either a valid stream object or throws an 'untrusted code' exception\n String resource;\n resource = String.format (\"/lib/%s.js\", argument);\n scripting.evaluate (trusting (resource), context);\n return null;\n }\n});\ncontext.setBindings (bindings, ScriptContext.ENGINE_SCOPE);\n// add require function to the scope before the application script is loaded\nscripting.evaluate (requirejs (Importer), context);\n// execute the script ultimately\nscripting.evaluate (applicationjs (), context);\n\n",[471,46819,46820,46824,46829,46834,46839,46844,46848,46853,46858,46863,46868,46873,46878,46882,46886,46891,46896,46901,46906],{"__ignoreMap":469},[474,46821,46822],{"class":476,"line":477},[474,46823,917],{"emptyLinePlaceholder":916},[474,46825,46826],{"class":476,"line":507},[474,46827,46828],{},"// internal context for variables\n",[474,46830,46831],{"class":476,"line":547},[474,46832,46833],{},"final Bindings bindings = scripting.newBindings ();\n",[474,46835,46836],{"class":476,"line":584},[474,46837,46838],{}," bindings.put (Importer, new Function\u003CString, Void> () {\n",[474,46840,46841],{"class":476,"line":607},[474,46842,46843],{}," // load a library 'argument' from the 'lib' directory\n",[474,46845,46846],{"class":476,"line":642},[474,46847,21043],{},[474,46849,46850],{"class":476,"line":663},[474,46851,46852],{}," public Void apply (String argument) {\n",[474,46854,46855],{"class":476,"line":694},[474,46856,46857],{}," // trusting returns either a valid stream object or throws an 'untrusted code' exception\n",[474,46859,46860],{"class":476,"line":700},[474,46861,46862],{}," String resource;\n",[474,46864,46865],{"class":476,"line":913},[474,46866,46867],{}," resource = String.format (\"/lib/%s.js\", argument);\n",[474,46869,46870],{"class":476,"line":920},[474,46871,46872],{}," scripting.evaluate (trusting (resource), context);\n",[474,46874,46875],{"class":476,"line":926},[474,46876,46877],{}," return null;\n",[474,46879,46880],{"class":476,"line":932},[474,46881,1276],{},[474,46883,46884],{"class":476,"line":938},[474,46885,15357],{},[474,46887,46888],{"class":476,"line":944},[474,46889,46890],{},"context.setBindings (bindings, ScriptContext.ENGINE_SCOPE);\n",[474,46892,46893],{"class":476,"line":950},[474,46894,46895],{},"// add require function to the scope before the application script is loaded\n",[474,46897,46898],{"class":476,"line":956},[474,46899,46900],{},"scripting.evaluate (requirejs (Importer), context);\n",[474,46902,46903],{"class":476,"line":962},[474,46904,46905],{},"// execute the script ultimately\n",[474,46907,46908],{"class":476,"line":4876},[474,46909,46910],{},"scripting.evaluate (applicationjs (), context);\n",[439,46912,33932],{},[1065,46914,46916],{"id":46915},"reporting","Reporting",[439,46918,46919],{},"In the last example I want to explain how to integrate a script engine, which is not shipped by default – JRuby [9].\nThe idea behind is to embed code of ruby gems into your application, especially PDFKit [10] for this example. PDFKit\ndescribes itself with",[11947,46921,46922],{},[439,46923,46924],{},"“Create PDFs using plain old HTML+CSS. Uses wkhtmltopdf on the back-end which renders HTML using Webkit.”",[439,46926,46927,46928,46931],{},"Mostly you don’t want to handle HTML content directly, as your data is often stored in form of a ‘model’. Our solution\nshould therefore target the transformation from: ",[471,46929,46930],{},"Model -> HTML -> PDF",", which can be achieved using e.g. the nice Jade\n[11] language for the rendering process, especially Jade4j [12].",[439,46933,46934],{},"Instead of writing the integration code for wkhtmltopdf, we will base on the work of PDFKit and write some JRuby glue.",[439,46936,46937],{},"If you need some information about ‘gem bundling’ I would recommend the articles/examples from Sieger [13] and Harada\n[14] as a starting point.",[439,46939,46940],{},"You will find a local file-based repository in the project, as I wanted Maven [15] to handle all the dependencies, but\nany other repository might work fine. It simply depends on your infrastructure and what suits you best.",[439,46942,46709],{},[8310,46944,46945,46948,46951,46954,46957,46960],{},[997,46946,46947],{},"put jruby-complete on your classpath, as the library ships the jsr223 implementation",[997,46949,46950],{},"put the converted pdfkit bundle on your classpath",[997,46952,46953],{},"put any other needed library on your classpath [jade4j, guava, …]",[997,46955,46956],{},"write some jruby code to instantiate a configured pdfkit object",[997,46958,46959],{},"proxy the returned jruby object from 4. with a java interface",[997,46961,46962],{},"convert a jade (or differently) generated html stream to pdf using the proxy from 5.",[439,46964,46965],{},"I’ll show you the glue for the proxy only. Please download the project under [3] if you want to see the remaining\nparts.",[464,46967,46969],{"className":709,"code":46968,"language":711,"meta":469,"style":469},"\npublic interface Pdfy {\n public boolean convert (InputStream streamin, OutputStream streamout);\n public boolean convert (InputStream streamin, OutputStream streamout, Map\u003CString, String> options);\n}\n\n",[471,46970,46971,46975,46980,46985,46990],{"__ignoreMap":469},[474,46972,46973],{"class":476,"line":477},[474,46974,917],{"emptyLinePlaceholder":916},[474,46976,46977],{"class":476,"line":507},[474,46978,46979],{},"public interface Pdfy {\n",[474,46981,46982],{"class":476,"line":547},[474,46983,46984],{}," public boolean convert (InputStream streamin, OutputStream streamout);\n",[474,46986,46987],{"class":476,"line":584},[474,46988,46989],{}," public boolean convert (InputStream streamin, OutputStream streamout, Map\u003CString, String> options);\n",[474,46991,46992],{"class":476,"line":607},[474,46993,703],{},[464,46995,46999],{"className":46996,"code":46997,"language":46998,"meta":469,"style":469},"language-ruby shiki shiki-themes github-light github-dark","\nclass Pdfy\n def initialize(stylesheet)\n @stylesheet = stylesheet\n end\n def convert(streamin, streamout, options = {})\n begin\n html = streamin.to_io.read\n kit = PDFKit.new(html, options)\n if @stylesheet\n kit.stylesheets \u003C\u003C @stylesheet\n end\n out = streamout.to_io\n out.binmode \u003C\u003C kit.to_pdf\n out.flush\n rescue\n return false\n end\n true\n end\nend\n\n","ruby",[471,47000,47001,47005,47010,47015,47020,47025,47030,47035,47040,47045,47050,47055,47060,47065,47070,47075,47080,47085,47090,47095,47099],{"__ignoreMap":469},[474,47002,47003],{"class":476,"line":477},[474,47004,917],{"emptyLinePlaceholder":916},[474,47006,47007],{"class":476,"line":507},[474,47008,47009],{},"class Pdfy\n",[474,47011,47012],{"class":476,"line":547},[474,47013,47014],{}," def initialize(stylesheet)\n",[474,47016,47017],{"class":476,"line":584},[474,47018,47019],{}," @stylesheet = stylesheet\n",[474,47021,47022],{"class":476,"line":607},[474,47023,47024],{}," end\n",[474,47026,47027],{"class":476,"line":642},[474,47028,47029],{}," def convert(streamin, streamout, options = {})\n",[474,47031,47032],{"class":476,"line":663},[474,47033,47034],{}," begin\n",[474,47036,47037],{"class":476,"line":694},[474,47038,47039],{}," html = streamin.to_io.read\n",[474,47041,47042],{"class":476,"line":700},[474,47043,47044],{}," kit = PDFKit.new(html, options)\n",[474,47046,47047],{"class":476,"line":913},[474,47048,47049],{}," if @stylesheet\n",[474,47051,47052],{"class":476,"line":920},[474,47053,47054],{}," kit.stylesheets \u003C\u003C @stylesheet\n",[474,47056,47057],{"class":476,"line":926},[474,47058,47059],{}," end\n",[474,47061,47062],{"class":476,"line":932},[474,47063,47064],{}," out = streamout.to_io\n",[474,47066,47067],{"class":476,"line":938},[474,47068,47069],{}," out.binmode \u003C\u003C kit.to_pdf\n",[474,47071,47072],{"class":476,"line":944},[474,47073,47074],{}," out.flush\n",[474,47076,47077],{"class":476,"line":950},[474,47078,47079],{}," rescue\n",[474,47081,47082],{"class":476,"line":956},[474,47083,47084],{}," return false\n",[474,47086,47087],{"class":476,"line":962},[474,47088,47089],{}," end\n",[474,47091,47092],{"class":476,"line":4876},[474,47093,47094],{}," true\n",[474,47096,47097],{"class":476,"line":4888},[474,47098,47024],{},[474,47100,47101],{"class":476,"line":4900},[474,47102,47103],{},"end\n",[439,47105,47106],{},"Maven will produce an assembly as zip file, which can be extracted elsewhere with a shell script for windows and *nix\nbased systems.",[439,47108,47109],{},"You need to provide the full qualified path for wkhtmltopdf as first argument and the full qualified path of the output\nfile with file extension as second argument.",[439,47111,47112],{},"I did not implement any special CLI handling for this prototype.",[439,47114,47115],{},"You need to install wkhtmltopdf [16] as a consequence. I installed wkhtmltopdf 0.11.0 rc2 on windows 7 x64 and\nwkhtmltopdf 0.9.9 on ubuntu 13.10 x64 (virtualization).",[439,47117,47118],{},"Even if writing some boilerplate is not that interesting, writing less boilerplate is! So instead of writing your own\nwheel, you might want to spend your energy on making another wheel feel more rounded.",[439,47120,47121],{},"Whether a script engine can be used in your production environment depends on your configuration, of course, but writing\nsome glue to reuse another solutions might be worth thinking about it.",[439,47123,47124],{},"The effort could be less in comparison to a full rewrite. Stick to a separation of interfaces and implementations and\nlet the environment decide.",[439,47126,47127],{},"The devs. from Rhino/Nashorn/JRuby did quite a good job! As well as the devs. from the mentioned libraries. You should\ncompile the project with Java 1.6(!), 1.7 and 1.8 and look at the results.",[439,47129,47130,47131,47135,47136,47140,47141,47145,47146,47150,47151,47155,47156,47160,47161,47165,47166,47170,47171,47175,47176,47180,47181,47185,47186,47190,47191,47195,47196,47200,47201,47205,47206],{},"[ 1]\n",[1002,47132,47133],{"href":47133,"rel":47134},"http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform",[1006],"\n[ 2]\n",[1002,47137,47138],{"href":47138,"rel":47139},"http://stackoverflow.com/questions/11838369/where-can-i-find-a-list-of-available-jsr-223-scripting-languages",[1006],"\n[ 3]\n",[1002,47142,47143],{"href":47143,"rel":47144},"https://media.synyx.de/uploads//2014/01/synyx.sample.zip",[1006],"\n[ 4]\n",[1002,47147,47148],{"href":47148,"rel":47149},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",[1006],"\n[ 5]\n",[1002,47152,47153],{"href":47153,"rel":47154},"http://requirejs.org/",[1006],"\n[ 6]\n",[1002,47157,47158],{"href":47158,"rel":47159},"http://de.wikipedia.org/wiki/Public-Key-Infrastruktur",[1006],"\n[ 7]\n",[1002,47162,47163],{"href":47163,"rel":47164},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind",[1006],"\n[ 8]\n",[1002,47167,47168],{"href":47168,"rel":47169},"http://prototypejs.org/doc/latest/language/Function/prototype/bind/",[1006],"\n[ 9]\n",[1002,47172,47173],{"href":47173,"rel":47174},"http://www.jruby.org/",[1006],"\n[10]\n",[1002,47177,47178],{"href":47178,"rel":47179},"https://github.com/pdfkit/pdfkit",[1006],"\n[11]\n",[1002,47182,47183],{"href":47183,"rel":47184},"http://jade-lang.com/",[1006],"\n[12]\n",[1002,47187,47188],{"href":47188,"rel":47189},"https://github.com/neuland/jade4j",[1006],"\n[13]\n",[1002,47192,47193],{"href":47193,"rel":47194},"http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar/",[1006],"\n[14]\n",[1002,47197,47198],{"href":47198,"rel":47199},"http://yokolet.blogspot.de/2010/10/gems-in-jar-with-redbridge.html",[1006],"\n[15]\n",[1002,47202,47203],{"href":47203,"rel":47204},"http://maven.apache.org/",[1006],"\n[16]\n",[1002,47207,47208],{"href":47208,"rel":47209},"https://code.google.com/p/wkhtmltopdf/",[1006],[1024,47211,47212],{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":469,"searchDepth":507,"depth":507,"links":47214},[47215,47216,47217,47218],{"id":46207,"depth":547,"text":46198},{"id":29263,"depth":547,"text":46243},{"id":46620,"depth":547,"text":46621},{"id":46915,"depth":547,"text":46916},[1030],"2014-01-22T10:47:14","https://synyx.de/blog/code-gluse/",{},"/blog/code-gluse",{"title":46198,"description":469},"blog/code-gluse",[711,15201,47227,47228,47229,47230,47231,47232],"jruby","jsr-223","mozilla-rhino","oracle-nashorn","pdfkit","wkhtmltopdf","Code gluse Today’s post targets an API, which has been released on Dec. 11, 2006; the javax.scripting package [1] and a lot of good articles that have been written around…","0mHHVmGkqcIuSqfGmf2Y2k5dl12AMxwLerwX_CH7G00",{"id":47236,"title":47237,"author":47238,"body":47239,"category":47526,"date":47527,"description":47528,"extension":1034,"link":47529,"meta":47530,"navigation":916,"path":47531,"seo":47532,"slug":47243,"stem":47534,"tags":47535,"teaser":47538,"__hash__":47539},"blog/blog/talking-tech-to-the-business-guy.md","Talking tech to the business guy",[97],{"type":432,"value":47240,"toc":47516},[47241,47244,47253,47318,47321,47325,47328,47339,47342,47346,47349,47371,47380,47384,47387,47429,47432,47436,47439,47443,47446,47450,47453,47461,47464,47468,47471,47473,47476,47496,47499,47502,47509],[435,47242,47237],{"id":47243},"talking-tech-to-the-business-guy",[439,47245,47246,47247,47252],{},"Every development project has a business guy attached, who holds the project money and makes the decisions what the team\nshould implement. That guy can be your customer, sales manager, product manager, the product owner in a scrum project or\nsimply your boss. In this article we will conveniently call him “manager”. Constant small refactoring, test coverage and\nother technical things that you do while developing features don’t really concern him. But from time to time you have a\nbig, technical issue, that does not have apparent business value and does not add any features. You see it as absolutely\nnecessary but you need the time and approval from your manager to do it. Watch this conversation between a developer and\nthe well known “pointy haired boss”, that I stole from\na ",[1002,47248,47251],{"href":47249,"rel":47250,"title":47251},"http://programmers.stackexchange.com/questions/157928/how-to-justify-code-refactoring-time",[1006],"stackexchange.com post","\nand that seems awkwardly familiar to every developer:",[11947,47254,47255,47261,47267,47272,47277,47282,47286,47291,47296,47301,47306,47310,47315],{},[439,47256,47257,47260],{},[990,47258,47259],{},"Developer",": We need time to do this technical redesign.",[439,47262,47263,47266],{},[990,47264,47265],{},"PHB",": Why?",[439,47268,47269,47271],{},[990,47270,47259],{},": It’ll make things easier to fix",[439,47273,47274,47276],{},[990,47275,47265],{},": So what?",[439,47278,47279,47281],{},[990,47280,47259],{},": It’ll increase throughput – we’ll get new builds out the door quicker?",[439,47283,47284,47276],{},[990,47285,47265],{},[439,47287,47288,47290],{},[990,47289,47259],{},": Err…Happier customers?",[439,47292,47293,47295],{},[990,47294,47265],{},": WTF?",[439,47297,47298,47300],{},[990,47299,47259],{},": I mean increased recommendations, greater satisfaction, more profit sooner due to low turnaround.",[439,47302,47303,47305],{},[990,47304,47265],{},": Oh! Sounds nice. But it only “sounds” nice can I see it?",[439,47307,47308,47295],{},[990,47309,47259],{},[439,47311,47312,47314],{},[990,47313,47265],{},": Show me some numbers!",[439,47316,47317],{},"…. and so on…",[439,47319,47320],{},"The problem is that the manager often can not see the benefits of technical necessities like refactorings, redesign,\nestablishing test coverage. How could he? He probably never learned their importance and the consequences when you omit\nthem. The guy who knows better and who’s job it is to convince the manager is you! Let’s talk about some aspects that\nwill help you do this.",[3938,47322,47324],{"id":47323},"_1-trust","1. Trust",[439,47326,47327],{},"Some of you will be surprised but in my opinion the most important factor in this matter is that your manager trusts\nyou! If he knows you well and has the feeling that you want the best for him and the project then there is a chance that\nthe conversation takes this path:",[11947,47329,47330,47334],{},[439,47331,47332,47260],{},[990,47333,47259],{},[439,47335,47336,47338],{},[990,47337,47265],{},": Well, it is not particularly cheap but you are the expert and your advice has always been valuable, let’s do\nit!",[439,47340,47341],{},"It doesn’t get any easier than that! The way to achieve this trust is to always be honest and straightforward and talk\nopenly. Don’t try to trick your manager into approving something by leaving out facts or by faking estimations.\nTransparency is key in the relationship between manager and developer. If you earn your trust, a good, non-sociopathic\nmanager will reward you with more freedom. The less your manager trusts you the more trouble you will have convincing\nhim of a technical necessity.",[3938,47343,47345],{"id":47344},"_2-technical-affinity-of-the-manager","2. Technical affinity of the manager",[439,47347,47348],{},"Try to figure out the technical affinity and experience of your manager. This will decide what kind of arguments will\nhelp your case. If he has a lot of technical experience, maybe even is a former developer himself, then chances are high\nthat you are able to convince him by technical reasoning. The conversation might go like this:",[11947,47350,47351,47356,47361,47366],{},[439,47352,47353,47355],{},[990,47354,47259],{},": Before we implement any new functionality we need to write tests for this legacy code first.",[439,47357,47358,47360],{},[990,47359,47265],{},": Why is that? We never had any tests back in the nineties when developing software while listening to MC Hammer?",[439,47362,47363,47365],{},[990,47364,47259],{},": Yeah, but remember that it was difficult to change anything without breaking another thing? Nowadays you\nwrite automated unit tests so you can always make sure that changes to the code don’t introduce unwanted side effects\ninto the software.",[439,47367,47368,47370],{},[990,47369,47265],{},": Oh yeah, that always bothered me. And unit tests sound like a good way to fix that problem. Let’s write some\ntests first!",[439,47372,47373,47374,47379],{},"Points like “only tested code is easy to change without side effects” or “refactoring and redesign\nare ",[1002,47375,47378],{"href":47376,"rel":47377,"title":47378},"http://xprogramming.com/blog/why-is-refactoring-a-must/",[1006],"necessary in an agile process","\nto adapt to the requirements” or even “no one writes code perfectly the first time” are likely to convince a tech-savvy\nmanager. But they won’t help with a manager, who doesn’t have any technical knowledge at all. If you are unlucky they\nconfuse him even more and drive him away from the decision you want him to make.",[3938,47381,47383],{"id":47382},"_3-find-a-leverage-that-the-manager-understands","3. Find a leverage that the manager understands",[439,47385,47386],{},"If you sense that the manager won’t understand what you are talking about, don’t blame him. He probably has his\nqualities elsewhere. Maybe you still can find a way to make your point. Most business guys think in buzzwords like time,\nbudget, quality. Get to know your manager’s favourite buzzword and try to tell him how your proposition helps this\nparticular priority.",[11947,47388,47389,47393,47398,47403,47409,47414,47420,47423],{},[439,47390,47391,47260],{},[990,47392,47259],{},[439,47394,47395,47266],{},[990,47396,47397],{},"Time-PHB",[439,47399,47400,47402],{},[990,47401,47259],{},": Because after that we can introduce new features into the software way faster!",[439,47404,47405,47408],{},[990,47406,47407],{},"Budget-PHB",": I don’t see the real benefit.",[439,47410,47411,47413],{},[990,47412,47259],{},": Well, the code will be easier to handle so we will need less resources to make changes. Even new\ndevelopers will be able to understand and expand the code without training.",[439,47415,47416,47419],{},[990,47417,47418],{},"Quality-PHB",": Meh, I still don’t see it!",[439,47421,47422],{},"Developer: But the refactored code will also be more robust to side effects so we will be able to introduce changes\nwithout fear of bugs.",[439,47424,47425,47428],{},[990,47426,47427],{},"Time-PHB, Budget-PHB, Quality-PHB unisonous",": THAT IS THE BEST THING EVER, WHY HAVEN’T WE ALREADY DONE THIS?",[439,47430,47431],{},"Also helpful are analogies to something that the manager knows. As an example one of the most famous analogies is to\nrefer to undone technical necessities by the phrase “technical debt”. If you explain to the manager that like real debt\nit is growing all the time and has to be paid back with interest then maybe he will understand the consequences.",[3938,47433,47435],{"id":47434},"_4-business-case","4. Business case",[439,47437,47438],{},"If the manager is too cautious and insists on numbers like in our first conversation with the PHB then it might be best\nto give them to him. Try to make an estimation on how much the development will be accelerated or on how much the\nquality will improve and put it into numbers to present to him as business case. You know quite well that the numbers\nwill be highly imprecise and probably won’t come true exactly as estimated but the business case will help your manager\nto see the benefits. You just have to clarify that the numbers are what they are – rough estimations. That way the\nmanager can make a more informed decision and no one can really blame you afterwards when the effects don’t meet the\nestimations.",[3938,47440,47442],{"id":47441},"_5-play-the-expert-card","5. Play the expert card",[439,47444,47445],{},"In a typical developer-manager-relationship you are not only the guy who codes but also a consultant. You are the\ntechnical expert and are entitled to advise your manager. Don’t be afraid to do that! Often it helps to say something in\nthe lines of “as your technical expert I strongly recommend to…” because it reminds the manager that you are the one who\nknows how to develop software. But keep in mind that you always have to back your recommendation with valid points and\nreasons! Playing the expert card and then lacking the arguments to explain your reasons is just a cheap bluff.",[3938,47447,47449],{"id":47448},"_6-pdd-pain-driven-development","6. PDD – Pain driven development",[439,47451,47452],{},"You really exerted yourself and tried everything to make your point. But everything fails. The manager ignores your\nadvice and insists on doing it his way. At some point it is best not to stress the matter any further and just leave it.\nYou know it will cause pain but sometimes pain is good. You will have the opportunity to use it for your argumentation\nnext time.",[11947,47454,47455,47458],{},[439,47456,47457],{},"“If we had any test coverage, half of the reported bugs would not have occured”.",[439,47459,47460],{},"“We have to make estimations that high, because it is difficult to implement new features in that part of the software\nwithout the big redesign we didn’t do three months ago”.",[439,47462,47463],{},"Just try not to make your manager look bad for his decisions. Blame improves nothing and your further cooperation will\nbe much more comfortable if you maintain a good relationship.",[3938,47465,47467],{"id":47466},"_7-understand-the-other-side","7. Understand the other side",[439,47469,47470],{},"You should never forget that the manager might have good reason to reject your proposition. He has to make the project\nprofitable and he gains nothing if you do technical changes just because you think they are cool. Always reflect on\nyourself and try to figure out if your proposition really helps the project or if it is just something that sounded cool\nin some blog post *cough*.",[3938,47472,42276],{"id":42275},[439,47474,47475],{},"That’s it, I hope this list will help you communicating your desires to your manager.",[439,47477,47478,47479,47482,47483,47488,47489,47491,47492,47495],{},"One last important piece of advice: To a certain degree technical issues are something your manager doesn’t have to know\nabout at all and doesn’t even want to know about. ",[990,47480,47481],{},"You"," are the\nsoftware ",[1002,47484,47487],{"href":47485,"rel":47486,"title":47487},"http://manifesto.softwarecraftsmanship.org/",[1006],"craftsman"," and often ",[990,47490,27720],{}," know best what is the\nright choice for your project. Things like constant refactoring and maintaining test coverage are natural parts of the\nsoftware development process and you ",[990,47493,47494],{},"have"," to do them to deliver a good product, you don’t have to ask for permission\nto do them.",[439,47497,47498],{},"It’s the same as with every other craftsmanship. You also don’t ask your plumber how he installs your bathtub, you only\ncare that he installs it at all and you know it takes the time it takes. And if it doesn’t start to leak at any time you\nwill never question that he did a good job.",[439,47500,47501],{},"Some Links:",[439,47503,47504],{},[1002,47505,47508],{"href":47506,"rel":47507},"http://stackoverflow.com/questions/3138823/what-is-the-best-way-to-explain-refactoring-to-non-technical-people",[1006],"stackoverflow.com thread on explaining refactoring to non-tech guys",[439,47510,47511],{},[1002,47512,47515],{"href":47513,"rel":47514},"http://www.infoq.com/news/2010/07/explaining-refactoring",[1006],"explaining refactoring to management",{"title":469,"searchDepth":507,"depth":507,"links":47517},[47518,47519,47520,47521,47522,47523,47524,47525],{"id":47323,"depth":507,"text":47324},{"id":47344,"depth":507,"text":47345},{"id":47382,"depth":507,"text":47383},{"id":47434,"depth":507,"text":47435},{"id":47441,"depth":507,"text":47442},{"id":47448,"depth":507,"text":47449},{"id":47466,"depth":507,"text":47467},{"id":42275,"depth":507,"text":42276},[1030],"2014-01-15T08:11:56","Every development project has a business guy attached, who holds the project money and makes the decisions what the team\\nshould implement. That guy can be your customer, sales manager, product manager, the product owner in a scrum project or\\nsimply your boss. In this article we will conveniently call him “manager”. Constant small refactoring, test coverage and\\nother technical things that you do while developing features don’t really concern him. But from time to time you have a\\nbig, technical issue, that does not have apparent business value and does not add any features. You see it as absolutely\\nnecessary but you need the time and approval from your manager to do it. Watch this conversation between a developer and\\nthe well known “pointy haired boss”, that I stole from\\na stackexchange.com post\\nand that seems awkwardly familiar to every developer:","https://synyx.de/blog/talking-tech-to-the-business-guy/",{},"/blog/talking-tech-to-the-business-guy",{"title":47237,"description":47533},"Every development project has a business guy attached, who holds the project money and makes the decisions what the team\nshould implement. That guy can be your customer, sales manager, product manager, the product owner in a scrum project or\nsimply your boss. In this article we will conveniently call him “manager”. Constant small refactoring, test coverage and\nother technical things that you do while developing features don’t really concern him. But from time to time you have a\nbig, technical issue, that does not have apparent business value and does not add any features. You see it as absolutely\nnecessary but you need the time and approval from your manager to do it. Watch this conversation between a developer and\nthe well known “pointy haired boss”, that I stole from\na stackexchange.com post\nand that seems awkwardly familiar to every developer:","blog/talking-tech-to-the-business-guy",[9546,47536,37776,47537],"communication","project-management","Every development project has a business guy attached, who holds the project money and makes the decisions what the team should implement. That guy can be your customer, sales manager,…","6F3kvXH8M_boqJKlC7Me1iiMcyrU2Sxf14Gs9DrJSBM",{"id":47541,"title":47542,"author":47543,"body":47544,"category":47971,"date":47972,"description":47973,"extension":1034,"link":47974,"meta":47975,"navigation":916,"path":47976,"seo":47977,"slug":47548,"stem":47979,"tags":47980,"teaser":47982,"__hash__":47983},"blog/blog/client-code-ignores-repository-implementations-developers-do-not.md","Client code ignores REPOSITORY implementations; developers do not",[247],{"type":432,"value":47545,"toc":47967},[47546,47549,47564,47573,47576,47640,47655,47664,47692,47696,47703,47711,47718,47738,47748,47789,47800,47811,47833,47863,47869,47875,47904,47918,47929,47936,47938,47943,47946,47949,47957,47962,47965],[435,47547,47542],{"id":47548},"client-code-ignores-repository-implementations-developers-do-not",[439,47550,47551,47552,47555,47556,47559,47560,47563],{},"Our team is working on an application for one of our clients, a service provider for container logistics, shipping cargo\nbetween seaports, terminals and other loading sites. The business domain also includes the calculation of shipping\nprices subjected to the agreements met between the shipping company and its customers. We recently implemented the\nconcept of so called ",[990,47553,47554],{},"offers"," into the application, whereas each offer contains multiple ",[990,47557,47558],{},"terminal-specific prices",".\nOne or more offers may be assigned to a ",[990,47561,47562],{},"customer"," (see diagram below, capturing these domain concepts).",[439,47565,47566],{},[1002,47567,47570],{"href":47568,"rel":47569},"https://media.synyx.de/uploads//2013/12/domainmodel.png",[1006],[2205,47571],{"alt":47572,"src":47568},"domainmodel",[439,47574,47575],{},"Our technology stack encompasses Spring Framework and JPA as the persistence technology. All the applications data is\nstored in a relational database.",[464,47577,47579],{"className":709,"code":47578,"language":711,"meta":469,"style":469},"\n@Entity\npublic class Offer {\n @ManyToMany(fetch = FetchType.EAGER)\n private Map\u003C Long, Price> importBargePrices;\n @ManyToMany(fetch = FetchType.EAGER)\n private Map\u003C Long, Price> exportBargePrices;\n @ManyToMany(fetch = FetchType.EAGER)\n private Map\u003C Long, Price> importRailPrices;\n @ManyToMany(fetch = FetchType.EAGER)\n private Map\u003C Long, Price> exportRailPrices;\n ...\n}\n\n",[471,47580,47581,47585,47590,47595,47600,47605,47609,47614,47618,47623,47627,47632,47636],{"__ignoreMap":469},[474,47582,47583],{"class":476,"line":477},[474,47584,917],{"emptyLinePlaceholder":916},[474,47586,47587],{"class":476,"line":507},[474,47588,47589],{},"@Entity\n",[474,47591,47592],{"class":476,"line":547},[474,47593,47594],{},"public class Offer {\n",[474,47596,47597],{"class":476,"line":584},[474,47598,47599],{}," @ManyToMany(fetch = FetchType.EAGER)\n",[474,47601,47602],{"class":476,"line":607},[474,47603,47604],{}," private Map\u003C Long, Price> importBargePrices;\n",[474,47606,47607],{"class":476,"line":642},[474,47608,47599],{},[474,47610,47611],{"class":476,"line":663},[474,47612,47613],{}," private Map\u003C Long, Price> exportBargePrices;\n",[474,47615,47616],{"class":476,"line":694},[474,47617,47599],{},[474,47619,47620],{"class":476,"line":700},[474,47621,47622],{}," private Map\u003C Long, Price> importRailPrices;\n",[474,47624,47625],{"class":476,"line":913},[474,47626,47599],{},[474,47628,47629],{"class":476,"line":920},[474,47630,47631],{}," private Map\u003C Long, Price> exportRailPrices;\n",[474,47633,47634],{"class":476,"line":926},[474,47635,697],{},[474,47637,47638],{"class":476,"line":932},[474,47639,703],{},[439,47641,47642,47643,47646,47647,47650,47651,47654],{},"Note the ",[990,47644,47645],{},"collection-valued associations"," between offer and price: in the current implementation these have been\nconfigured to be ",[990,47648,47649],{},"eagerly"," fetched. Setting the fetch type to ",[990,47652,47653],{},"eager loading"," forces the JPA provider to instantly fetch\nthese entity attributes from the database when an offer is read.",[439,47656,47657,47658,47663],{},"Beyond JPA we also use ",[1002,47659,47662],{"href":47660,"rel":47661},"http://projects.spring.io/spring-data/",[1006],"Spring Data",". So, there’s a repository representing a\ncollection of offers and encapsulating the internal details of database access:",[464,47665,47667],{"className":709,"code":47666,"language":711,"meta":469,"style":469},"\npublic interface OfferRepository extends\n JpaRepository\u003C Offer, Long> {\n Offer findByLabel(String label);\n}\n\n",[471,47668,47669,47673,47678,47683,47688],{"__ignoreMap":469},[474,47670,47671],{"class":476,"line":477},[474,47672,917],{"emptyLinePlaceholder":916},[474,47674,47675],{"class":476,"line":507},[474,47676,47677],{},"public interface OfferRepository extends\n",[474,47679,47680],{"class":476,"line":547},[474,47681,47682],{}," JpaRepository\u003C Offer, Long> {\n",[474,47684,47685],{"class":476,"line":584},[474,47686,47687],{}," Offer findByLabel(String label);\n",[474,47689,47690],{"class":476,"line":607},[474,47691,703],{},[1065,47693,47695],{"id":47694},"when-things-went-wrong","When things went wrong",[439,47697,47698,47699,47702],{},"Before deploying our application to production, we did some ",[990,47700,47701],{},"exploratory testing"," in a production-like environment.\nDuring testing, we realized that the application ran out of memory when doing one of the the following operations:",[994,47704,47705,47708],{},[997,47706,47707],{},"loading an offer from the database (in order to display its details)",[997,47709,47710],{},"assigning an offer to a customer",[439,47712,47713,47714,47717],{},"These two use cases have been tested constantly by our acceptance tests, every time the ",[990,47715,47716],{},"Continuous Integration"," server\nfinnished building and deploying the application into a test server. Nevertheless, both use cases now turn out to be\nbroken in a production-like environment. One obvious difference between the environment for CI acceptance testing and\nthe production-like environment is the amount of data stored in the database.",[439,47719,47720,47721,47726,47727,34572,47730,47733,47734,47737],{},"Java Virtual Machine monitoring and profiling\nwith ",[1002,47722,47725],{"href":47723,"rel":47724},"http://docs.oracle.com/javase/7/docs/technotes/tools/share/jvisualvm.html",[1006],"jvisualvm"," revealed huge memory\nconsumption when reconstituting a single stored offer entity using the repository method ",[471,47728,47729],{},"findOne",[471,47731,47732],{},"OfferRepository","\nextends ",[471,47735,47736],{},"JpaRepository"," and therefore provides the aforementioned method;",[439,47739,47740,47741,47744,47745,47747],{},"Spring Datas ",[471,47742,47743],{},"SimpleJpaRepository"," implements this method to simply call JPA EntityManagers ",[471,47746,29924],{}," method.",[464,47749,47751],{"className":709,"code":47750,"language":711,"meta":469,"style":469},"\n@Override\npublic T findOne(ID id) {\n ...\n Class\u003C T> domainType = getDomainClass();\n return type == null ? em.find(domainType, id)\n : em.find(domainType, id, type);\n}\n\n",[471,47752,47753,47757,47761,47766,47770,47775,47780,47785],{"__ignoreMap":469},[474,47754,47755],{"class":476,"line":477},[474,47756,917],{"emptyLinePlaceholder":916},[474,47758,47759],{"class":476,"line":507},[474,47760,29178],{},[474,47762,47763],{"class":476,"line":547},[474,47764,47765],{},"public T findOne(ID id) {\n",[474,47767,47768],{"class":476,"line":584},[474,47769,697],{},[474,47771,47772],{"class":476,"line":607},[474,47773,47774],{}," Class\u003C T> domainType = getDomainClass();\n",[474,47776,47777],{"class":476,"line":642},[474,47778,47779],{}," return type == null ? em.find(domainType, id)\n",[474,47781,47782],{"class":476,"line":663},[474,47783,47784],{}," : em.find(domainType, id, type);\n",[474,47786,47787],{"class":476,"line":694},[474,47788,703],{},[439,47790,47791,47792,47795,47796,47799],{},"Analyzing the SQL that gets constructed and executed by the JPA provider discloses a relatively big number of ",[990,47793,47794],{},"outer\njoins"," in the generated ",[990,47797,47798],{},"select"," statement; this is not an issue with Spring Data.",[11947,47801,47802,47805,47808],{},[439,47803,47804],{},"“Spring Data JPA itself does not control the interaction with the database directly. All it does is interacting with\nthe EntityManager, so effectively all behavioral effects are defined by JPA and",[439,47806,47807],{},"the underlying OR-mapper.”",[439,47809,47810],{},"[Oliver Gierke, Spring Data Project]",[439,47812,47813,47814,47817,47818,47820,47821,47823,47824,47826,47827,47829,47830,47832],{},"Introducing the finder method ",[471,47815,47816],{},"findById"," into ",[471,47819,47732],{}," and replacing calls to ",[471,47822,47729],{}," by calls to ",[471,47825,47816],{},"\nhad an effect on the generated SQL: instead of one ",[990,47828,47798],{}," statement with more than twenty outer joins, we now have\nmultiple ",[990,47831,47798],{}," statements each having three outer joins at most.",[464,47834,47836],{"className":709,"code":47835,"language":711,"meta":469,"style":469},"\npublic interface OfferRepository extends\n JpaRepository\u003C Offer, Long> {\n Offer findById(Long id);\n Offer findByLabel(String label);\n}\n\n",[471,47837,47838,47842,47846,47850,47855,47859],{"__ignoreMap":469},[474,47839,47840],{"class":476,"line":477},[474,47841,917],{"emptyLinePlaceholder":916},[474,47843,47844],{"class":476,"line":507},[474,47845,47677],{},[474,47847,47848],{"class":476,"line":547},[474,47849,47682],{},[474,47851,47852],{"class":476,"line":584},[474,47853,47854],{}," Offer findById(Long id);\n",[474,47856,47857],{"class":476,"line":607},[474,47858,47687],{},[474,47860,47861],{"class":476,"line":642},[474,47862,703],{},[439,47864,47865,47866,47868],{},"In both cases, because the offers collection-valued associations are configured to be eagerly loaded, price data is\nimplicitly fetched from the database when an offer is initially read. And in case of using ",[471,47867,47729],{},", the generated SQL\nstatement causes the application to crash with an OutOfMemoryError.",[439,47870,47871,47872,47874],{},"But just replacing any call of ",[471,47873,47729],{}," falls too short. Look at the following code that assigns the specified offer to\na customer:",[464,47876,47878],{"className":709,"code":47877,"language":711,"meta":469,"style":469},"\nOffer offer = offerRepository.findById(offerId);\nCustomer customer = customerRepository.findById(customerId);\ncustomer.addOffer(offer);\ncustomerRepository.save(customer);\n\n",[471,47879,47880,47884,47889,47894,47899],{"__ignoreMap":469},[474,47881,47882],{"class":476,"line":477},[474,47883,917],{"emptyLinePlaceholder":916},[474,47885,47886],{"class":476,"line":507},[474,47887,47888],{},"Offer offer = offerRepository.findById(offerId);\n",[474,47890,47891],{"class":476,"line":547},[474,47892,47893],{},"Customer customer = customerRepository.findById(customerId);\n",[474,47895,47896],{"class":476,"line":584},[474,47897,47898],{},"customer.addOffer(offer);\n",[474,47900,47901],{"class":476,"line":607},[474,47902,47903],{},"customerRepository.save(customer);\n",[439,47905,47906,47907,47910,47911,47914,47915,1402],{},"First both, the offer and the customer (each specified by Id) are queried from the database. Then, the offer is assigned\nto the customer and finally the customer gets updated. A programmer not aware of JPA and Spring Data internals might\noverlook that ",[471,47908,47909],{},"save",", which is actually provided by Spring Data, invokes the JPA EntityManagers ",[471,47912,47913],{},"merge"," method and he\nmight not know the details of the ",[448,47916,47917],{},"JPA concepts of detachment, merging and transaction-scoped persistence contexts",[439,47919,47920,47921,47924,47925,47928],{},"Now, assume that retrieving the offer and customer and eventually updating the customer are executed not within the same\n",[990,47922,47923],{},"transaction",". This means that the offer entity is no longer in managed state when the JPA merge operation does its\nwork. JPA ",[990,47926,47927],{},"persistence contexts"," are tied to the lifecycle of a transaction which implies that the JPA provider again\nreads the offer from database, instantly fetching all of its collection-valued associations, which in our case causes\nthe application to run out of memory (again, there are lots of outer joins in the select statement sent to the\ndatabase).",[439,47930,47931,47932,47935],{},"Ensuring that reading offers and updating the customer are handled in the same transaction fixes the memory issue, but\nthe better approach would have been to avoid eager loading at all. This not only decreases the impact on memory but also\nreduces the amount of SQL and speeds up the queries and object loading. These are the reasons that JPA defaults to ",[990,47933,47934],{},"lazy\nloading"," (aka deferred loading, on-demand fetching) for collection-valued associations; but keep in mind that lazy\nloading is just a hint to the JPA provider, i.e. behaviour will depend on its implementation.",[1065,47937,9392],{"id":9391},[439,47939,47940],{},[448,47941,47942],{},"Deepen the knowledge of the technologies you are using",[439,47944,47945],{},"Care about the internal details of the persistence technology. Generally speaking, decoupling clients from repository\nimplementation and the underlying technology is great, but this does not relieve the developers from the need to\nunderstand the consequences of using encapsulated behaviour, including its performance and resource-related\nimplications.",[439,47947,47948],{},"In other words:",[11947,47950,47951,47954],{},[439,47952,47953],{},"“Client code ignores REPOSITORY implementations; developers do not”",[439,47955,47956],{},"[Eric Evans, Domain-Driven Design]",[439,47958,47959],{},[448,47960,47961],{},"Measure, don’t guess",[439,47963,47964],{},"Testing the application should not only be done on a regular basis and in an automated fashion, but equally important,\nperformance measuring and memory monitoring in production-like environments is essential and must not be ignored. In an\niterative development process, these tests should be done in each iteration.",[1024,47966,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":47968},[47969,47970],{"id":47694,"depth":547,"text":47695},{"id":9391,"depth":547,"text":9392},[1030],"2013-12-28T15:21:04","Our team is working on an application for one of our clients, a service provider for container logistics, shipping cargo\\nbetween seaports, terminals and other loading sites. The business domain also includes the calculation of shipping\\nprices subjected to the agreements met between the shipping company and its customers. We recently implemented the\\nconcept of so called offers into the application, whereas each offer contains multiple terminal-specific prices.\\nOne or more offers may be assigned to a customer (see diagram below, capturing these domain concepts).","https://synyx.de/blog/client-code-ignores-repository-implementations-developers-do-not/",{},"/blog/client-code-ignores-repository-implementations-developers-do-not",{"title":47542,"description":47978},"Our team is working on an application for one of our clients, a service provider for container logistics, shipping cargo\nbetween seaports, terminals and other loading sites. The business domain also includes the calculation of shipping\nprices subjected to the agreements met between the shipping company and its customers. We recently implemented the\nconcept of so called offers into the application, whereas each offer contains multiple terminal-specific prices.\nOne or more offers may be assigned to a customer (see diagram below, capturing these domain concepts).","blog/client-code-ignores-repository-implementations-developers-do-not",[47981,1427,21404],"jpa","Our team is working on an application for one of our clients, a service provider for container logistics, shipping cargo between seaports, terminals and other loading sites. The business domain…","J-ESiwrnNDVh8L3ppooBU17nw-pvH4OuHi08PhY2-Uo",{"id":47985,"title":47986,"author":47987,"body":47988,"category":48243,"date":48244,"description":48245,"extension":1034,"link":48246,"meta":48247,"navigation":916,"path":48248,"seo":48249,"slug":47992,"stem":48251,"tags":48252,"teaser":48258,"__hash__":48259},"blog/blog/my-first-time-on-a-conference-with-synyx.md","My first time on a conference with synyx",[332],{"type":432,"value":47989,"toc":48241},[47990,47993,48000,48006,48009,48012,48027,48041,48056,48091,48132,48151,48154,48169,48192,48195,48198,48215,48218],[435,47991,47986],{"id":47992},"my-first-time-on-a-conference-with-synyx",[439,47994,47995,47996,47999],{},"I am a fresh employee at synyx. I started early in 2013 to work for the company and the",[990,47997,47998],{},"Devoxx 2013"," in Antwerp was my\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.",[439,48001,48002,48003,48005],{},"Till November nothing big happens. I searched through the Devoxx site for speakers and talks, but not so much. I wanted\nto be free and just do what I want to do on the Devoxx and be ",[990,48004,9546],{},", of course. Then, one week before the Devoxx, I\ntalked to my colleague to know when the train leaves, where we are going to sleep, where to get the tickets and so on.\nAt this point I realized that synyx has everything organized and well planed. Good guy synyx!",[439,48007,48008],{},"When we arrived at the main station in Frankfurt the first cuba libre was ready to go. Of course with ice and lime. Yes\nwe had ice. We also shared some with friends we met before. We chatted on the train about talks, speakers, the company,\nfuture projects, our new office and challenges in some of our projects. It was fun to speak about things that bother you\nin this different atmosphere than our office or meeting rooms. It was very relaxing and fun.",[439,48010,48011],{},"The next day was the first day on the Devoxx. We started early to get our access keys and traveled there via subway. Its\na nice subway with a ‘nice’ smell, something like mushrooms.",[439,48013,48014,48015,34572,48020,18175,48023,48026],{},"One of the first talks I listened to was ",[1002,48016,48019],{"href":48017,"rel":48018},"http://fhornain.wordpress.com/2013/11/12/devoxx13-java-ee-7-whats-new-in-the-java-ee-platform/http://",[1006],"**Java EE 7: What’s New in the Java EE Platform\n**",[990,48021,48022],{},"Argun\nGupta",[990,48024,48025],{},"Antonio Goncalves"," talked about new features of Java EE7. It was very interesting and a clear presentation\nwith good speakers. They talked about 3 hours, so it was good that they also did a lot of jokes on the stage especially\nwhen questions arrived like “why do you do not use spring? It has every feature you represent since two years.” Good\nSpeakers, good presentation. I learned a lot about Java EE in general.",[439,48028,48029,48032,48033,48040],{},[990,48030,48031],{},"David Gageot"," showed us in a quick 30 minutes live coding session called ",[1002,48034,48037],{"href":48035,"rel":48036},"http://www.devoxx.be/dv13-david-gageot.html?presId=3108",[1006],[448,48038,48039],{},"From Legacy to Cloud Under an Hour – Live\nCoding"," how easy it is to take old nasty unreadable code with\nabout a million of if/else statements and other dirty stuff in it and a good IDEA to get a nice clean code without\nduplicates and all the if/else and put it on the cloud at the end. Very impressive to see that in 30 minutes. He must\nhave trained a lot! Notice to me: Take time for your IDEA and learn what you can do with it, even if you think you know\na lot.",[439,48042,48043,48044,48047,48048,48055],{},"At the second day I was excited about the talk from ",[990,48045,48046],{},"José Paumard"," called ",[1002,48049,48052],{"href":48050,"rel":48051},"http://www.devoxx.be/dv13-jos-paumard.html?presId=3540",[1006],[448,48053,48054],{},"Autumn Collection: from iterable to\nlambdas, streams and collectors"," because this stuff you are\nusing and will use all the time when programming. It gave a real clear view on lambdas, streams and what you can do with\nit. The syntax looked quite nice, if you take care of it. Do not start to use it for everything and make things more\ncomplicated than they already are. Let’s see how it will be in future projects with Java 8. It feels like parallel\nprogramming will be made available and easier for a wider mass of developers.",[439,48057,48058,48059,8993,48066,48069,48070,520,48079,48084,48085,48090],{},"The next talk I watched was ",[1002,48060,48063],{"href":48061,"rel":48062},"http://www.devoxx.be/dv13-matt-raible.html?presId=3648",[1006],[448,48064,48065],{},"The Modern Java Web Developer",[990,48067,48068],{},"Matt Raible"," is a very good speaker with a lot of self irony and sarcasm. Which makes the talk even more interesting.\nHe talked about the modern solutions of web development\nlike ",[1002,48071,48075],{"href":48072,"rel":48073,"title":48074},"http://angularjs.org/",[1006],"Angular JS",[2205,48076],{"alt":48077,"src":48078},"20131112_133433","https://media.synyx.de/uploads//2013/12/20131112_133433-150x150.jpg",[1002,48080,48083],{"href":48081,"rel":48082},"http://jquery.com/",[1006],"jQuery",",\nCSS3, ",[1002,48086,48089],{"href":48087,"rel":48088},"http://www.html5rocks.com/en/",[1006],"HTML5"," and so on. It was a great overview over the preferred technologies for web\ndevelopers. At the end of his talk the showed us a video of him coding a complete dashboard application at one\nafternoon. Of course with a lot of copy paste. But it was nice to see the problems he got stuck, how he fixed it and how\npowerful and easy to use modern web frameworks and libraries are.",[439,48092,48093,48094,48097,48098,48105,48106,48109,48110,48113,48114,8351,48117,18175,48122,48127,48128,48131],{},"When I wrote this blog entry about the third day I could not remember what talks were really good at that day, besides\nthe talks of ",[990,48095,48096],{},"Martijn Verburg",". The problem was not that they were bad, but maybe not so forceful. On the other hand I\nwatched ",[1002,48099,48102],{"href":48100,"rel":48101},"http://www.devoxx.be/dv13-chet-haase.html?presId=3193",[1006],[448,48103,48104],{},"Patterns Shmatterns"," by ",[990,48107,48108],{},"Chet Haase",". And maybe that\nmakes me forget all the other talks. It was a ",[990,48111,48112],{},"fun quickie"," about patterns and their ‘pendant’. Great show, great funny\nface at the front. Definitely one talk to watch between other real talks to relax. The talks from ",[990,48115,48116],{},"Martijn Verburg:",[1002,48118,48121],{"href":48119,"rel":48120},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3214",[1006],"*\n*The Habits of Highly Effective Technical Teams**",[1002,48123,48126],{"href":48124,"rel":48125},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3223",[1006],"*\n*The Bleeding Edge**"," were both very good. The first named\ntalk was one of my highlights on the Devoxx, because it forced me to think about companies and how they treat their\nemployees and how they should treat them. For example why companies do not gave their employees rights to the productive\nsystem for the project they work on ",[990,48129,48130],{},"and so on and so forth",". A lot of questions where thrown into the room which I\nnever really asked myself and their was one moment where I realized that synyx is making a good job. Definitely a talk\nemployees and employer should watch and think about their process.",[439,48133,48134,48142,48143,48146,48147,48150],{},[1002,48135,48138],{"href":48136,"rel":48137},"https://media.synyx.de/uploads//2013/12/20131111_231326.jpg",[1006],[2205,48139],{"alt":48140,"src":48141},"20131111_231326","https://media.synyx.de/uploads//2013/12/20131111_231326-150x150.jpg","\nThis was not only a effective day at the Devoxx. it was also my birthday. So we went for lunch this evening with\neveryone and then went into a student bar somewhere in Antwerp. It was really funny to go into a small student bar with\nround about 13 people on an evening where they had a 90’s party. Of course we knew all the great 90’s songs like Barbie\nGirl (O",[990,48144,48145],{},"o). But we were not prepared for it and the girls with their shards, yeah shards. Sometimes it is funny if you\nhave a close look at it. After that ‘crazy’ party we moved on to the _Beer Central"," to taste all their 300+ beer.\nLet’s say we ",[990,48148,48149],{},"almost"," did it. Great but different birthday than the years before. Thanks synyx!",[439,48152,48153],{},"At the last two days of the conference I watched a lot of talks, just one after another and running from room to room.\nTwo of them I do remind very well, because they where my points of lights these days.",[439,48155,48156,48157,48160,48161,48168],{},"First of all another highlight of my Devoxx week was the talk from ",[990,48158,48159],{},"Joey Cieplinski"," with the title ",[1002,48162,48165],{"href":48163,"rel":48164},"http://www.devoxx.be/dv13-joe-cieplinski.html?presId=3330",[1006],[448,48166,48167],{},"Business\nStrategies for Small Independent Developers"," he talked\nabout premium and none premium applications for mobile devices and his view to make higher price applications. Then to\nconcentrate on the 10% of customers that care about your product and do not see it as a throwaway piece of software to\nbuy for less then 1€. This talk confirmed me in my opinion to create quality software and get what you earn for a good\nproduct and concentrate of the concerns of real customers and their problems. Inspiring talk, thank you for sharing it.",[439,48170,48171,48178,48179,48182,48183,48191],{},[1002,48172,48175],{"href":48173,"rel":48174},"http://www.devoxx.be/dv13-alain-regnier.html?presId=3733",[1006],[448,48176,48177],{},"Introduction to Google Glass"," from ",[990,48180,48181],{},"Alain Regnier","was my\nlast talk I visited. It was a demonstration about what can be done with the glass and what could be done with it in the\nfuture. Alain also explained a lot on how Google act by spreading the glass and what you have to do to get the\nglass. ",[1002,48184,48187],{"href":48185,"rel":48186},"https://media.synyx.de/uploads//2013/12/20131115_105017.jpg",[1006],[2205,48188],{"alt":48189,"src":48190},"20131115_105017","https://media.synyx.de/uploads//2013/12/20131115_105017-150x150.jpg","\nHe explained how to program with the Mirror API and his hopes of getting the GDK (Glass Development Kit) soon. Google\nGlass seems to be a product which can change parts of our lives, but for this it needs a real case in which it will be\nnecessary to have one. I am excited to see what will be possible in the future.",[439,48193,48194],{},"And at the end a small summary.",[439,48196,48197],{},"Antwerp:",[994,48199,48200,48203,48206,48209,48212],{},[997,48201,48202],{},"The subway smells like mushrooms.",[997,48204,48205],{},"The subway is small and carries a lot of people.",[997,48207,48208],{},"Awesome and horrible beer at once.",[997,48210,48211],{},"Beautiful main train station.",[997,48213,48214],{},"They love mayonnaise.",[439,48216,48217],{},"Devoxx:",[994,48219,48220,48223,48226,48229,48232,48235,48238],{},[997,48221,48222],{},"Great speakers with very interesting talks.",[997,48224,48225],{},"Nice People.",[997,48227,48228],{},"Learned a lot of new stuff.",[997,48230,48231],{},"Raspberry Pis will rule the world.",[997,48233,48234],{},"Crab sandwiches are delicious. Yeah! Ate a lot of them.",[997,48236,48237],{},"Tuna salad with only tuna are not that fun.",[997,48239,48240],{},"Belgian fries are awesome.",{"title":469,"searchDepth":507,"depth":507,"links":48242},[],[1031],"2013-12-06T08:59:44","I am a fresh employee at synyx. I started early in 2013 to work for the company and theDevoxx 2013 in Antwerp was my\\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.","https://synyx.de/blog/my-first-time-on-a-conference-with-synyx/",{},"/blog/my-first-time-on-a-conference-with-synyx",{"title":47986,"description":48250},"I am a fresh employee at synyx. I started early in 2013 to work for the company and theDevoxx 2013 in Antwerp was my\nfirst conference with my new colleagues. Everything started around august when a colleague asked „Whoooo want to go to\nthe Devoxx in November. Are you in? And you?“, he was so enthusiastic that I thought: “Hm first conference with these\nguys? One week with them in another country? And that on my birthday? Let’s do it!”.","blog/my-first-time-on-a-conference-with-synyx",[8276,37617,48253,48254,48255,9556,48256,48257],"gogle-glass","java-8","java-ee7","modern-web-development","premium-apps","I am a fresh employee at synyx. I started early in 2013 to work for the company and the Devoxx 2013 in Antwerp was my first conference with my new colleagues.…","eVUq9b1WClNnEr_8e-Dj8NNuyffQHOQJrPs6z3v10lw",{"id":48261,"title":48262,"author":48263,"body":48264,"category":48449,"date":48450,"description":469,"extension":1034,"link":48451,"meta":48452,"navigation":916,"path":48453,"seo":48454,"slug":48268,"stem":48455,"tags":48456,"teaser":48457,"__hash__":48458},"blog/blog/nosql-matters-it-does-but-think-about-your-data.md","NoSQL matters – It does! But think about your data!",[238],{"type":432,"value":48265,"toc":48440},[48266,48269,48273,48285,48289,48292,48296,48299,48303,48327,48344,48347,48351,48358,48364,48374,48380,48385,48391,48397,48404,48408],[435,48267,48262],{"id":48268},"nosql-matters-it-does-but-think-about-your-data",[3938,48270,48272],{"id":48271},"the-confernence-venue","The confernence venue",[439,48274,22944,48275,48277,48278,48280,48281,48284],{},[448,48276,39710],{}," took place in Barcelona, Spain, from 29-30 November. Barcelona is a big, beautiful (\nbut crowded) city. The conference venue, the ",[990,48279,35683],{},", belongs to the complex of the ",[990,48282,48283],{},"Hospital de la Santa\nCreu i Sant Pau"," which was declared World Cultural Heritage Site by the UNESCO. It has a great atmosphere! The\nconference itself was sold out, and thus more than 150 participants came together to discuss about the field of NoSQL\nand related technologies. It was well organized, and the schedule left time for discussions and to change the rooms. The\nconcluding ‘session’ brought all the participants together for tapas and beer and encouraged them for further lively\ndiscussions.",[3938,48286,48288],{"id":48287},"day-1-the-training-day","Day 1: The training day",[439,48290,48291],{},"The first day was a training day. The training sessions were concerned with some of the available NoSQL stores (the\ngraph database Neo4j or the key-value store Riak) or how to model in a NoSQL world. I myself did not participate in the\ntraining sessions, as I arrived late on Friday 29 November in Barcelona.",[3938,48293,48295],{"id":48294},"day-2-the-session-day","Day 2: The session day",[439,48297,48298],{},"The second day of the conference was a session day with talks covering all the different nuances of the NoSQL world. It\nwas a very interesting day, with lots of things to learn and with lots of opportunities to discuss the presented topics.\nI will not report on all the talks I visited in detail, but will only briefly summarize them. But before that, I will\npoint some of the points that I (for myself) found most interesting.",[1065,48300,48302],{"id":48301},"the-gist-think-about-your-data","The gist: Think about your data",[439,48304,48305,48306,48309,48310,18175,48313,48309,48316,48319,48320,48323,48324,48326],{},"A main concern of many speakers was to make developers think about their applications (again): What do these demand, and\nwhat are the best fitting solutions and architectures! Not ‘",[990,48307,48308],{},"one-size-fits-all","‘ ",[990,48311,48312],{},"[Stonebraker 2005],",[990,48314,48315],{},"‘there\nis no free lunch with distributed data",[990,48317,48318],{},"[HP 2005]","! To efficiently use the full potential the new stores and\ntechnologies offer, a basic understanding of the database developments of the last about 50 years is helpful. Starting\nfrom hierarchical and network CODASYL systems, Codds abstraction to tuples was a big step forward ",[990,48321,48322],{},"[Codd 1970]",".\nApplications were freed from caring how data is represented on the storage system, the processes of normalization avoids\nredundancy and anomalies, and the ",[448,48325,44364],{}," properties were desired in most business applications. Further, the\nmathematically precise definition of the tuple calculus made the theory formally accessible by set theory.",[439,48328,48329,48330,48332,48333,520,48336,48339,48340,48343],{},"But the world is in constant change: High availability and horizontal scaling are crucial points in todays business (\nweb) applications, and consistency might sometimes be weakened in these environments, so ",[448,48331,44270],{}," can be an appropriate\nalternative consistency model to the ‘good old’ ACID. With BASE come the phenomena the CAP theorem states ",[990,48334,48335],{},"[Brewer\n2002]",[990,48337,48338],{},"[Gilbert 2002]",". Some speakers warned: Keep in mind that relational database systems can be a perfectly sound\nchoice! To design suitable applications and to make use of the powerful possibilities of ‘polyglot persistence’\n",[990,48341,48342],{},"[Fowler 2010]"," and the ‘Lambda architecture for big data’ [Marz 2012], the needs of the application have to be well\nunderstood, otherwise a sound choice from the high number of database stores is impossible.",[439,48345,48346],{},"Complexity isolation, i. e. partitioning the demands in small parts and address these, can be a good way to achieve good\nsoftware. Schemas are often desirable, as they introduce structure in your data based on explicit knowledge that can be\nexploited to maintain a systems’ integrity.",[1065,48348,48350],{"id":48349},"the-talks","The talks",[439,48352,48353,48354,48357],{},"The conferences great keynote was given by ",[448,48355,48356],{},"N. Marz",". He introduced the ‘Doofus Programmer’ (that we all are) and\ndiscussed the ‘insanity’ of the complexity of the database world. His proposal: To go for ‘Human-Fault-Tolerant’\nsystems by using immutable (but versioned) databases, schemas, precomputation, complexity isolation and the Lambda\narchitecture.",[439,48359,48360,48363],{},[448,48361,48362],{},"C. Gormley"," introduced Elasticsearch and its possibilities in detail, showing many examples that demonstrated\npowerful indexing options. Elasticsearch is a distributed document store, capable to achieve near-real-time data\nanalysis.",[439,48365,48366,48367,48370,48371,48373],{},"The talk by ",[448,48368,48369],{},"M. Hausenblas"," was about the ",[990,48372,39756],{}," and how to use NoSQL technologies to harness it. The\namount of data collected on a daily basis is huge. Pro-active (automotive) services, optimization of (logistic)\nprocesses, patient monitoring and smart houses and cities are just a few examples. The key technologies to cope with the\ndata can be polyglot persistence and the Lambda architecture. Use different stores for different needs!",[439,48375,48376,48379],{},[448,48377,48378],{},"D. Turnbull"," (a historian and computer scientist) presented a nice historical review on database evolution, starting\nfrom hierarchical and network systems that ‘evolved’ to NoSQL. He discussed the needs that led to the changes in data\nmodeling.",[439,48381,48382,48384],{},[448,48383,35863],{}," nicely illustrated the pitfalls of modeling in a BASE world, not with the aim to scare developers,\nbut to enforce them to think about data and consistency properties. Different consistency modes need different\napproaches, and again: The best fitting store matching the applications demands has to be found! He presented code\nexamples that demonstrated how to deal with some of the BASE ‘phenomena’, and how to achieve consistency properties like\n‘read-your-own-writes’.",[439,48386,48387,48390],{},[448,48388,48389],{},"D. Mytton",", founder of Server density, showed an example of how a replicated, fast out-of-the-box fault tolerant\ndatabase store can be build up with MongoDB. The solution is used for time series analysis and able to serve up to 3333\nwrites/s with fast response time.",[439,48392,48393,48396],{},[448,48394,48395],{},"J. Reijn"," showed how real-time visitor analysis can be achieved by combining Elasticsearch with Couchbase as part of\nHippo CMS. He also gave a nice example for choosing stores according to needs: As part of the system Apache Jackrabbit\nis used, which is a hierarchical (key/value) store.",[439,48398,48399,48400,48403],{},"A scale-in approach to databases was presented by ",[448,48401,48402],{},"N. Björkman",". As hardware and especially RAM are relatively cheap\navailable these days, memory centric databases are possible. By holding database and applications in the same RAM (and\nletting them share heap space), performance can be significantly increased, and object mapping can be completely\navoided. The system Starcounter offers full ACID compliance, a native .NET API and SQL support.",[3938,48405,48407],{"id":48406},"some-recommended-articles","Some recommended articles",[994,48409,48410,48413,48416,48419,48422,48425,48433],{},[997,48411,48412],{},"[Stonebraker 2005] Stonebraker, Çetintemel; “One Size Fits All”: An Idea Whose Time Has Come and Gone, Proceedings of\nthe 21st International Conference on Data Engineering ICDE ’05, IEEE Computer Society, 2005.",[997,48414,48415],{},"[HP 2005] HP; There is no free lunch with distributed data white paper, 2005.",[997,48417,48418],{},"[Codd 1970] Codd; Relational Model of Data for Large Shared Data Banks, Communications of the ACM 13 : 6, 1970.",[997,48420,48421],{},"[Brewer 2000] Brewer; Towards Robust Distributed Systems, Keynote at PODC, 2000.",[997,48423,48424],{},"[Gilbert 2002] Gilbert, Lynch; Brewer’s Conjecture and the Feasability of Consitent, Available and\nPartition-Tolerant Web Services, ACM SIGACT News 23:2, 2002.",[997,48426,48427,48428,1402],{},"[Fowler 2010]\nFowler; ",[1002,48429,48432],{"href":48430,"rel":48431},"http://www.martinfowler.com/bliki/PolyglotPersistence.html",[1006],"http://martinfowler.com/bliki/PolyglotPersistence.html",[997,48434,48435,48436,1402],{},"[Marz 2012] Marz; Big Data – Principles and best practices of scalable realtime data systems, Early access\nedition, ",[1002,48437,48438],{"href":48438,"rel":48439},"http://manning.com/marz",[1006],{"title":469,"searchDepth":507,"depth":507,"links":48441},[48442,48443,48444,48448],{"id":48271,"depth":507,"text":48272},{"id":48287,"depth":507,"text":48288},{"id":48294,"depth":507,"text":48295,"children":48445},[48446,48447],{"id":48301,"depth":547,"text":48302},{"id":48349,"depth":547,"text":48350},{"id":48406,"depth":507,"text":48407},[1030],"2013-12-04T18:46:02","https://synyx.de/blog/nosql-matters-it-does-but-think-about-your-data/",{},"/blog/nosql-matters-it-does-but-think-about-your-data",{"title":48262,"description":469},"blog/nosql-matters-it-does-but-think-about-your-data",[44676,35897,45433],"The confernence venue The NoSQL matters conference took place in Barcelona, Spain, from 29-30 November. Barcelona is a big, beautiful (but crowded) city. The conference venue, the Casa Convalescència, belongs…","Fx5SgQbpuFNWtF0EnN0bOhY43aeJrKvmmyoc1MYOxDI",{"id":48460,"title":48461,"author":48462,"body":48463,"category":48553,"date":48554,"description":48555,"extension":1034,"link":48556,"meta":48557,"navigation":916,"path":48558,"seo":48559,"slug":48467,"stem":48560,"tags":48561,"teaser":48566,"__hash__":48567},"blog/blog/all-aboard-the-google-train-das-gdg-devfest-2013-in-karlsruhe.md","All aboard the Google train – Das GDG DevFest 2013 in Karlsruhe",[202],{"type":432,"value":48464,"toc":48551},[48465,48468,48471,48481,48484,48494,48497,48500,48503,48513,48516,48526,48529,48532,48535,48545,48548],[435,48466,48461],{"id":48467},"all-aboard-the-google-train-das-gdg-devfest-2013-in-karlsruhe",[439,48469,48470],{},"Die Google Developer Group lud nach dem sehr gut besuchten letztjährigen DevFest auch in diesem Jahr wieder alle\nInteressierten in die Duale Hochschule Karlsruhe (ehemals Berufsakademie) ein, um einen mit spannenden Vorträgen und\nWorkshops gespickten Tag ganz im Zeichen der Google Produkte zu erleben, und hoffentlich viel Interessantes und Neues zu\nentdecken und zu lernen.",[439,48472,48473],{},[1002,48474,48477],{"href":48475,"rel":48476},"https://media.synyx.de/uploads//2013/11/tisch.jpg",[1006],[2205,48478],{"alt":48479,"src":48480},"Der synyx Sponsorentisch auf dem GDG DevFest 2013","https://media.synyx.de/uploads//2013/11/tisch-300x177.jpg",[439,48482,48483],{},"Wie auch im letzten Jahr, war synyx auch dieses Mal wieder als \u0004S\u0005ponsor tätig und natürlich auch vor Ort präsent um\nFragen rund um unsere Projekte, Firma, Arbeitsweisen und vieles mehr zu beantworten. Los ging das ganze Samstags morgens\nbereits um 8:30 Uhr mit der Anmeldung und Registrierung der Teilnehmer. Trotz der doch sehr frühen Stunde traf das\nhauptsächlich aus Studenten bestehende Publikum recht zügig ein, versorgte sich an der Anmeldung mit Namensschildchen\nund T-Shirts.",[439,48485,48486],{},[1002,48487,48490],{"href":48488,"rel":48489},"https://media.synyx.de/uploads//2013/11/gespraech.jpg",[1006],[2205,48491],{"alt":48492,"src":48493},"Eines der vielen Gespräche mit einer an synyx und unserem Geschäftsmodell interessierten Person.","https://media.synyx.de/uploads//2013/11/gespraech-300x266.jpg",[439,48495,48496],{},"Bereits vor Beginn der eigentlichen Veranstaltung um 9:15 Uhr war schon Zeit für erste Gespräche mit interessierten\nTeilnehmern. Erfreulicherweise hatten viele der Teilnehmer das grüne synyx-S bereits mehrfach gesehen und wir wurden\nmehrfach darauf angesprochen, dass es toll sei, dass synyx gerade hier in Karlsruhe immer wieder als einer der Sponsoren\nzu sehen sei, die solche für die Teilnehmer kostenlosen Veranstaltungen ermöglichen. Das ist natürlich immer erfreulich\nzu hören, wenn einem solch ein positives Feedback zu den eigenen Marketing- und Sponsoring-Aktionen entgegenschlägt.",[439,48498,48499],{},"Bereits ganz am Anfang zeigten sich viele der Teilnehmer ganz angetan von den grünen Anti-Stress-Bällen, welche wir\nzwar eigentlich nicht mehr offiziell „im Programm“ haben, aber auch die noch im Lager befindlichen Restbestände fanden\nhier auf dem DevFest reißenden Absatz.",[439,48501,48502],{},"Um 9:15 Uhr ging es dann nach kurzen, wohl bei solchen Veranstaltungen immer wieder unvermeidlichen, technischen\nProblemen mit der Soundanlage auch wirklich wie geplant mit der Begrüßung aller Teilnehmer los. Die Veranstaltung war ja\nauf maximal 200 Teilnehmer begrenzt, relativ schnell ausgebucht und die Warteliste lang. Leider haben es trotzdem über\n40 Personen vorgezogen ohne Abmeldung einfach nicht zu erscheinen. Das ist wohl die „Kostet ja nichts, egal!“ Mentalität\nheutzutage.",[439,48504,48505],{},[1002,48506,48509],{"href":48507,"rel":48508},"https://media.synyx.de/uploads//2013/11/balls.jpg",[1006],[2205,48510],{"alt":48511,"src":48512},"Unsere Anti-Stress-Bälle fanden reißenden Absatz.","https://media.synyx.de/uploads//2013/11/balls-300x199.jpg",[439,48514,48515],{},"Die Keynote über das Problem des Internet der Dinge wurde dann auch bereits ein großer Erfolg mit vielen Lachern und\neinigen „Wow!“ Elementen, als Kai Kreuzer anhand praktischer Beispiele im Bereich Wohnraumautomation gezeigt hat, was\nheutzutage mit wenigen Handgriffen und mit einer Handvoll Code im Controller alles möglich ist. Sein Showcase war dabei\neine RGB LED-Lampe. Die er von der Standard-Fernsteuerung nach und nach umgebaut und erweitert hat. Vom kabellosen\nLichtschalter, welcher die Sendeenergie aus dem Tastendruck selbst bezieht bis hin zur 3D \u0004Gestensteuerung\u0005 zum Schalten\ndes Lichts und zum Farbwechsel war alles dabei und hat doch einige erstaunte Gesichter hervorgerufen.",[439,48517,48518],{},[1002,48519,48522],{"href":48520,"rel":48521},"https://media.synyx.de/uploads//2013/11/flyer.jpg",[1006],[2205,48523],{"alt":48524,"src":48525},"Unsere tollen synyx Kugelschreiber und die Flyer","https://media.synyx.de/uploads//2013/11/flyer-300x199.jpg",[439,48527,48528],{},"In der ersten Kaffeepause kam dann der große Run an die Sponsorentische. Wir kamen gar nicht mehr hinterher mit dem\nNachlegen von Anti-Stress-Bällen, Kugelschreibern, Schlüsselbändern und Flyern. Aber dafür haben wir diese ja auch\nmitgebracht. Man ließ sich belegte Brötchen und Kaffee schmecken und bereitete sich auf den ersten beiden Vorträge mit\nden Themen Google Maps sowie JavaScript und HTML 5 vor. Während dieser Vorträge nutzten einige Teilnehmer die\nGelegenheit, uns detailliert über unsere Projekte, Vorgehensweisen, Philosophie und natürlich über die Jobmöglichkeiten\nbei synyx auszufragen. Wir hätten locker direkt 10 neue Kollegen vor Ort rekrutieren können.",[439,48530,48531],{},"Passend zur Mittagspause wurde die bereits eingangs in der Keynote gezeigte Gestensteuerung mit Hilfe einer Leap Motion\nals Showcase mit Hilfe von Google Earth und einem großen Beamer aufgebaut. Hier war es dann möglich, mit Hilfe von\nHandbewegungen innerhalb Google Earth zu zoomen, zu scrollen, den Globus zu drehen, den Kameraneigungswinkel zu\nverstellen und vieles mehr. Durch die hohe Anzahl der möglichen Freiheitsgrade in dem System war es gar nicht so\neinfach, die Kamera da hin zu steuern, wo man wollte. Aber zwei, drei Minuten Übung haben ausgereicht, die Kamera nach\nAbschluss seines Rundflugs um die Welt wieder zielsicher auf die Duale Hochschule auszurichten, auch wenn sich einige\ndann doch bei Tauchgängen im Pazifik völlig verirrt hatten.",[439,48533,48534],{},"Nach dem Mittagessen war dann Workshop-Zeit. Hierzu gab es drei unterschiedliche Tracks. Der erste Track widmete sich\nhauptsächlich der Webprogrammierung mit GWT und jQuery und war relativ visualisierungslastig. Im zweiten Track drehte\nsich alles um das Smartphone-Betriebssystem Android und im Rahmen eines CodeLabs wurde hier eine Einführung in die Welt\nder mobilen Programmierung gegeben. Der dritte Track war mit der Google App Engine ganz den Cloud-Diensten von Google\ngewidmet. Auch hier wurde fleißig direkt am „lebenden“ Beispiel gezeigt, was alles möglich ist.",[439,48536,48537],{},[1002,48538,48541],{"href":48539,"rel":48540},"https://media.synyx.de/uploads//2013/11/stand.jpg",[1006],[2205,48542],{"alt":48543,"src":48544},"Der komplette synyx Sponsorenstand mit Chef","https://media.synyx.de/uploads//2013/11/stand-300x291.jpg",[439,48546,48547],{},"Bei der zweite Kaffeepause war dann ein großes Hallo unter den Teilnehmern, denn es gab Schokoriegel für alle und zwar\nin einer so großen Menge, dass selbst Abends noch welche übrig waren obwohl die meisten gleich zwei oder drei mit in den\nzweiten Teil der Workshops genommen haben, um das 16 Uhr Loch gut zu überstehen. Auch die große „Unboxing“ Aktion, bei\nder ein riesiges Paket voller Google-Giveaways ausgepackt wurde, fand großen Anklang bei den Teilnehmern. Manch einer\nkonnte nach einem wilden Goodies-in-die-brodelnde-Menge-werfen tatsächlich nun 4 neue T-Shirts sein eigen nennen.",[439,48549,48550],{},"Nach Abschluss des zweiten Teils der Workshops gab es dann zum Abschluss noch ein kleines Fest im Foyer der dualen\nHochschule mit Musik und vor allem Bier für alle. Hier nutzten zahlreiche Teilnehmer die Gelegenheit noch einmal mit dem\nSpeakern und Organisatoren vertiefende Gespräche zu führen, bevor dann um 19 Uhr ein gelungender und sehr informativer\nAbend zu Ende ging.",{"title":469,"searchDepth":507,"depth":507,"links":48552},[],[1030,1412],"2013-11-26T15:53:29","Die Google Developer Group lud nach dem sehr gut besuchten letztjährigen DevFest auch in diesem Jahr wieder alle\\nInteressierten in die Duale Hochschule Karlsruhe (ehemals Berufsakademie) ein, um einen mit spannenden Vorträgen und\\nWorkshops gespickten Tag ganz im Zeichen der Google Produkte zu erleben, und hoffentlich viel Interessantes und Neues zu\\nentdecken und zu lernen.","https://synyx.de/blog/all-aboard-the-google-train-das-gdg-devfest-2013-in-karlsruhe/",{},"/blog/all-aboard-the-google-train-das-gdg-devfest-2013-in-karlsruhe",{"title":48461,"description":48470},"blog/all-aboard-the-google-train-das-gdg-devfest-2013-in-karlsruhe",[48562,48563,48564,48565,389],"gdg","gdg-devfest","google","sponsoring","Die Google Developer Group lud nach dem sehr gut besuchten letztjährigen DevFest auch in diesem Jahr wieder alle Interessierten in die Duale Hochschule Karlsruhe (ehemals Berufsakademie) ein, um einen mit…","DQn_zKyUvkihHHK173874FTg6o7z6baAhvKC4x3C00M",{"id":48569,"title":48570,"author":48571,"body":48572,"category":48680,"date":48681,"description":48682,"extension":1034,"link":48683,"meta":48684,"navigation":916,"path":48685,"seo":48686,"slug":48576,"stem":48688,"tags":48689,"teaser":48692,"__hash__":48693},"blog/blog/synyx-bei-den-xp-days-2013.md","synyx bei den XP Days 2013",[83],{"type":432,"value":48573,"toc":48678},[48574,48577,48586,48589,48598,48607,48619,48628,48637,48645,48648,48657,48666,48675],[435,48575,48570],{"id":48576},"synyx-bei-den-xp-days-2013",[439,48578,48579,48580,48585],{},"Vom 14. bis 16. November fanden in Karlsruhe die ",[1002,48581,48584],{"href":48582,"rel":48583,"title":48584},"http://xpdays.de/2013/",[1006],"XP Days"," statt und von synyx\nwaren (inklusive mir) vier Mitarbeiter dort. Da keiner von uns den Open Space am Samstag besuchte, gebe ich hier nur ein\nkurzes persönliches Fazit zu den ersten beiden Konferenztagen.",[439,48587,48588],{},"Ich bin bei synyx sowohl als Scrum Master als auch als Entwickler in Projekten tätig. Daher muss ich für mich persönlich\nauch bei Fortbildungen immer zwischen reinen Prozess Themen, die mich in meiner Arbeit als Scrum Master weiter bringen\nund Themen aus der Softwareentwicklung abwägen. Für mich waren die XP Days eine perfekte Mischung aus diesen beiden\nBereichen. Zumindest hatte ich die Möglichkeit die angebotenen Vorträge und Workshops an den beiden Konferenztagen so\nzusammen zu stellen, dass ich in beiden für mich relevanten Themenbereichen wertvolle neue Erkenntnisse gewinnen konnte.",[439,48590,48591,48592,48597],{},"Die Konferenz began für mich mit dem\nVortrag ",[1002,48593,48596],{"href":48594,"rel":48595},"http://www.xpdays.de/2013/sessions/079-kreative-retrospektiven.html",[1006],"Kreative Retrospektiven"," von Pierluigi\nPugliese, der für mich sehr interessant war, da Retrospektiven bei uns bisher immer nach dem gleichen Muster abliefen.\nObwohl hier keine konkreten Beispiele geliefert wurden, wie man Retrospektiven kreativer gestalten kann (mit dem Hinweis\nauf entsprechende Fachliteratur), lieferte Herr Pugliese doch einen sehr guten Überblick über das Thema und gab vor\nallem zahlreiche Begründungen, warum es in vielen Situationen sinnvoll sein kann, den Ablauf von Retrospektiven zu\nvariieren.",[439,48599,48600,48601,48606],{},"Weiter ging es\nmit ",[1002,48602,48605],{"href":48603,"rel":48604},"http://www.xpdays.de/2013/sessions/049-pair-programming-mythbusters.html",[1006],"Pairprogramming Mysthbusters",". Die Mythen\nwelche Martin Ruprecht sich vornahm zu beseitigen, sind bei synyx meiner Meinung nach kein Thema (mehr), da wir\nPairprogramming schon länger erfolgreich einsetzen und dies auch von den meisten Kunden als Vorteil gesehen wird.\nTrotzdem konnte ich aus diesem Vortrag einige interessante Denkanstöße mitnehmen, wie man die Arbeit im Pair noch weiter\nverbessern kann.",[439,48608,48609,48614,48618],{},[1002,48610,48613],{"href":48611,"rel":48612},"http://www.xpdays.de/2013/sessions/019-ich-will-kein-agiler-coach-mehr-sein-und-du.html",[1006],"„",[1002,48615,48617],{"href":48611,"rel":48616},[1006],"Ich will kein Agiler Coach mehr sein – Und du?“",".\nDieser von Johannes Link sehr provokant gewählte Vortragstitel machte mich neugierig. Einige seiner Argumente, wie etwa,\ndass der agile Ansatz nicht mehr als ein Placebo wäre, klangen sehr schlüssig und zeigten mir, dass man viele\nRichtlinien, die uns die agile Theorie vorgibt, auch kritisch nach ihrem tatsächlichen Nutzen hinterfragen kann.\nLetztendlich überzeugte er mich mit seiner Botschaft zwar nicht, sorgte aber für eine Horizonterweiterung, die mich\neinige der noch folgenden Vorträge auch immer mit einem anderen Ohr hören ließ.",[439,48620,48621,48622,48627],{},"Am Nachmittag überzeugte mich Martin Klose mit seinem\nVortrag ",[1002,48623,48626],{"href":48624,"rel":48625},"http://www.xpdays.de/2013/sessions/100-code-retreat-behind-the-scenes.html",[1006],"Code Retreat – behind the scenes","\ndavon, dass wir sowas unbedingt auch mal bei synyx machen müssen, um Entwickler Know-How besser zwischen den einzelnen\nTeams zu verteilen.",[439,48629,48630,48631,48636],{},"Als letzte Veranstaltung des Tages hörte ich mir noch\ndie ",[1002,48632,48635],{"href":48633,"rel":48634},"http://www.xpdays.de/2013/sessions/052-kanban-nicht-yet-another-development-process.html",[1006],"Einführung zu Kanban"," von\nFlorian Eisenberg an, um mir zum einen Anregungen für meine Arbeit mit unserem Admin Team zu holen und zum anderen Ideen\nzu sammeln, inwiefern Kanban eine Alternative zu Scrum im Softwareentwicklungsprozess darstellen kann. Eine wichtige\nErkenntnis aus diesem Vortrag war für mich, dass Scrum sich in erster Linie durch seine strengeren Regeln und seine\nstrenge Definition als Framework von Kanban unterscheidet und dadurch vor allem in Situationen sinnvoll sein kann, in\ndenen radikale Veränderungen durchgeführt werden sollen. Ob das in unseren Projekten notwendig oder vielleicht eher\nschädlich ist bin ich mir nicht so sicher.",[439,48638,13082,48639,48644],{},[1002,48640,48643],{"href":48641,"rel":48642},"http://www.xpdays.de/2013/sessions/keynote-dan-north-why-agile-doesnt-scale-and-what-you-can-do-about-it.html",[1006],"Keynote","\nam folgenden Tag begeisterte mich vor allem schon durch den hervorragenden Vortragsstil von Dan North. Auch inhaltlich\nwar der Vortrag sehr interessant, nur konnte ich hier leider wenig für meine tägliche Arbeit mitnehmen. Trotzdem bleibt\nes der aus meiner Sicht beste Vortrag den ich an den zwei Konferenztagen gehört habe.",[439,48646,48647],{},"Die bisher beschriebenen Vorträge stellten für mich den Prozess-technischem Teil der Konferenz dar. Zusätzlich zu\ndiesen Vorträgen besuchte ich drei Workshops, in denen die Teilnehmer anhand von konkreten Übungen ihre\nProgrammierkenntnisse erweitern konnten.",[439,48649,48650,48651,48656],{},"Der erste Workshop war für mich gleichzeitig der, welcher mir am meisten gebracht\nhat: ",[1002,48652,48655],{"href":48653,"rel":48654},"http://www.xpdays.de/2013/sessions/060b-object-calisthenics-objekt-gymnastik-praxis.html",[1006],"Object Calisthenics"," mit\nFranziska Sauerwein und David Burkhart. Im Workshop sollte ein einfaches Programm zur Darstellung eines Tennisspiels im\nPair so refactored werden, dass zehn mehr oder weniger restriktive Regeln eingehalten werden. Mein Kollege Michael\nHerbold und ich machten diesen Workshop gemeinsam und wir stellten schnell fest, dass diese Übung uns dazu\nherausforderte uns aus unserer Komfortzone hinsichtlich Programmiertechniken heraus zu bewegen. Zum Beispiel war es im\nersten Moment gar nicht so einfach die Anforderungen: „Alle Primitven Typen müssen in fachlichen Klassen gewrappt\nwerden“, „Maximal zwei Instanzvariablen pro Klasse“ und „Kein public access auf Instanzvariablen (auch keine getter und\nsetter)“ unter einen Hut zu bringen. Wir konnten in den 90 Minuten in jedem Fall viele Anregungen sammeln, die uns\nhoffentlich auch im Alltag helfen werden, wieder gezielt auf Objektorientierung und allgemein guten Codestil zu achten.\nZudem machte uns der Workshop so viel Spass, dass wir uns am Abend nach dem letzten Vortrag nochmal zusammen setzten und\nwährend wir auf unsere Bahn warteten, ein bisschen am Code weiter bastelten.",[439,48658,48659,48660,48665],{},"Der zweite Workshop den ich besuchen wollte,\nwar ",[1002,48661,48664],{"href":48662,"rel":48663},"http://www.xpdays.de/2013/sessions/054-hands-on-test-refactoring.html",[1006],"Hands-On Test Refactoring"," mit Marco\nEmrich. Hier muss ich leider sagen, dass ich den Workshop bereits nach der Einführung verlassen habe. Dies lag vor allem\ndaran dass mir der Vortragsstil und die Art wie der Workshop geleitet wurde nicht zusagte. Zum anderen waren auch die\nThemen, die behandelt werden sollten, nicht auf dem Level wie ich es mir erwartet hatte. Diese Veranstaltung war für\nmich schließlich die einzige, in der meine Erwartungen nicht erfüllt wurden.",[439,48667,48668,48669,48674],{},"Der letzte Workshop und für mich auch die letzte Veranstaltung der Konferenz\nwar ",[1002,48670,48673],{"href":48671,"rel":48672},"http://www.xpdays.de/2013/sessions/093-taking-baby-steps-reloaded.html",[1006],"Taking Baby Steps"," mit Marc Philipp, und\nFabian Knittel. In diesem zweistündigen Workshop sollten die Teilnehmer im Pair ein TicTacToe Spiel programmieren, mit\nder Einschränkung, dass jeweils nur zwei Minuten Zeit waren, um einen Test zu schreiben und diesen zu implementieren.\nLeider konnte ich mit meinem Pairing-Partner den erwünschten Effekt dieses Experiments erst gegen Ende des Workshops so\nrichtig nachfühlen, da wir zu Beginn noch stark durch technische Probleme gehemmt waren (fremde IDE, keine externe\nMaus/Tastatur). Schließlich konnten wir uns aber auch der Erkenntnis der anderen Teilnehmer anschließen: im Alltag will\nman oft viel zu viele Probleme auf einmal lösen und diese starke Restriktion auf zwei Minuten pro Commit zeigte jedem\nvon uns, dass man in kleineren Schritten oft zu einer viel besseren und saubereren Lösung kommt.",[439,48676,48677],{},"Zusammenfassend kann ich sagen, dass sich die zwei Konferenztage für mich auf jeden Fall gelohnt haben und ich die XP\nDays nächstes Jahr gerne wieder besuchen möchte. Als konkrete Maßnahmen habe ich mir als Entwickler vorgenommen das\nerlernte Coding-Know-How sowie die Pairprogramming Tipps im Alltag umzusetzen. Als Scrum Master will ich versuchen\nunsere Retrospektiven etwas mehr zu variieren und auf jeden Fall einen synyx internen Code Retreat mit allen Entwicklern\nzu organisieren. Abschließend bleibt mir nur noch ein großes Lob an die Organisatoren der XP Days 2013 zu geben, die für\nden aus meiner Sicht reibungslosen Ablauf der Konferenz gesorgt haben.",{"title":469,"searchDepth":507,"depth":507,"links":48679},[],[1030],"2013-11-26T12:56:17","Vom 14. bis 16. November fanden in Karlsruhe die XP Days statt und von synyx\\nwaren (inklusive mir) vier Mitarbeiter dort. Da keiner von uns den Open Space am Samstag besuchte, gebe ich hier nur ein\\nkurzes persönliches Fazit zu den ersten beiden Konferenztagen.","https://synyx.de/blog/synyx-bei-den-xp-days-2013/",{},"/blog/synyx-bei-den-xp-days-2013",{"title":48570,"description":48687},"Vom 14. bis 16. November fanden in Karlsruhe die XP Days statt und von synyx\nwaren (inklusive mir) vier Mitarbeiter dort. Da keiner von uns den Open Space am Samstag besuchte, gebe ich hier nur ein\nkurzes persönliches Fazit zu den ersten beiden Konferenztagen.","blog/synyx-bei-den-xp-days-2013",[9546,8276,48690,48691],"xp","xp-days","Vom 14. bis 16. November fanden in Karlsruhe die XP Days statt und von synyx waren (inklusive mir) vier Mitarbeiter dort. Da keiner von uns den Open Space am Samstag…","AlFZbG3xUTUaJ_2El4i16UP-wLKyvViaV81FOYDxiaI",{"id":48695,"title":48696,"author":48697,"body":48698,"category":48739,"date":48740,"description":48741,"extension":1034,"link":48742,"meta":48743,"navigation":916,"path":48744,"seo":48745,"slug":48702,"stem":48747,"tags":48748,"teaser":48749,"__hash__":48750},"blog/blog/warum-nicht-auch-mal-sales-know-how-weitergeben.md","Warum nicht auch mal Sales Know-how weitergeben?",[312],{"type":432,"value":48699,"toc":48737},[48700,48703,48720,48723,48731,48734],[435,48701,48696],{"id":48702},"warum-nicht-auch-mal-sales-know-how-weitergeben",[439,48704,48705,48706,48712,48713,48719],{},"Technisches Wissen und Erfahrungen geben wir bereits über unser Firmenblog und auch bei Veranstaltungen gerne und\nkostenfrei weiter. Also warum nicht auch mal Sales Know-how weitergeben? Eine gute Gelegenheit dafür war\nein ",[1002,48707,48711],{"href":48708,"rel":48709,"title":48710},"http://www.cie-kit.de/2013/09/11/cie-kaminabend-b2b-vertrieb-it-dienstleistungen/",[1006],"Kaminabend KIT","Kaminabend","\nbeim ",[1002,48714,48718],{"href":48715,"rel":48716,"title":48717},"http://www.cie-kit.de/",[1006],"CIE","Center für Innovation & Entrepreneurship",", kurz CIE, am KIT. Das CIE hat sich zum\nZiel gesetzt Unternehmensgründungen – unkonventionell und unbürokratisch – zu fördern. Angegliedert an das Institut für\nEntrepreneurship, Technologie-Management und Innovation des KIT, werden studentische Gründer kostenfrei mit diversen\nAngeboten unterstützt.",[439,48721,48722],{},"Dominik Stober und Anne Siebold sind die ersten Ansprechpartner des CIE. Dominik habe ich im Sommer kennengelernt. In\nunseren Gesprächen ist die Idee entstanden, dass synyx sich an einem Kaminabend beteiligen könnte und einen Referenten\nzur Verfügung stellt. Da bei Gründern die Vermarktung Ihrer Geschäftsidee und das Verkaufen eine im wahrsten Sinne des\nWortes tragende Rolle spielen, dachte ich mir, dass ein Vortrag und eine Diskussion zu Vertriebsthemen sicher auf\nInteresse stoßen.",[439,48724,48725,48726,48730],{},"Und so war es dann auch. Der Kaminabend „B2B Vertrieb von IT Dienstleistungen“ war schnell ausgebucht und Dominik musste\nmanchem Interessierten, der sich zu spät angemeldet hatte leider absagen. Sehr wahrscheinlich wird es aber einen zweiten\nAbend zu dem Thema geben und damit eine weitere Möglichkeit dabei zu sein. Also einfach mal auf der Webseite des CIE die\nVeranstaltungsankündigungen verfolgen. Oder sich direkt mit Dominik (",[1002,48727,48729],{"href":48728},"mailto:stober@cie-kit.de","stober@cie-kit.de",") in Verbindung setzen.",[439,48732,48733],{},"Ich habe versucht den Kaminabend möglichst Praxis nah zu gestalten und von meiner persönlichen Erfahrung zu berichten.\nEinstieg war eine Beschreibung einer meiner beruflichen Stationen, bei der es um den Vertrieb von Infrastruktur nahen\nLösungen und Services an Konzernkunden ging. Das ist ein sehr umkämpfter Markt und hier kommt es meiner Meinung nach\nstark auf die eigene Positionierung und den USP an. Wie bei Start-ups auch, waren meine zeitlichen Ressourcen begrenzt.\nMan muss sich also gut überlegen, wie man mit den verfügbaren Mitteln Geld und Zeit schnell vertriebliche Erfolge\nnachweisen kann, wenn man im Job bleiben oder am Markt bestehen möchte. Inhaltlich wurden die Ausgangssituation, die\nZielkunden, der Akquiseprozess, Door-Opener, Positionierung am Markt und weitere wichtige Punkte besprochen. Bereits\njetzt wurden Zwischenfragen gestellt und es ging quasi direkt zum zweiten Teil des Abends, auf den ich mich am meisten\ngefreut habe: Konkrete Fragestellungen der Teilnehmer erörtern.",[439,48735,48736],{},"Da die Teilnehmerrunde sowohl vom eigenen Angebot als auch von der Phase, in der sich das jeweilige Start-up befindet,\nrecht unterschiedlich waren, fand ich diesen Part des Abends besonders spannend. Ich hoffe, dass meine Ausführungen und\ndie gemeinsame Diskussion, den Teilnehmern bei ihrer vertrieblichen Arbeit viel helfen werden. In diesem Sinne freue ich\nmich schon auf den zweiten Kaminabend.",{"title":469,"searchDepth":507,"depth":507,"links":48738},[],[1031],"2013-10-18T12:13:05","Technisches Wissen und Erfahrungen geben wir bereits über unser Firmenblog und auch bei Veranstaltungen gerne und\\nkostenfrei weiter. Also warum nicht auch mal Sales Know-how weitergeben? Eine gute Gelegenheit dafür war\\nein Kaminabend\\nbeim Center für Innovation & Entrepreneurship, kurz CIE, am KIT. Das CIE hat sich zum\\nZiel gesetzt Unternehmensgründungen – unkonventionell und unbürokratisch – zu fördern. Angegliedert an das Institut für\\nEntrepreneurship, Technologie-Management und Innovation des KIT, werden studentische Gründer kostenfrei mit diversen\\nAngeboten unterstützt.","https://synyx.de/blog/warum-nicht-auch-mal-sales-know-how-weitergeben/",{},"/blog/warum-nicht-auch-mal-sales-know-how-weitergeben",{"title":48696,"description":48746},"Technisches Wissen und Erfahrungen geben wir bereits über unser Firmenblog und auch bei Veranstaltungen gerne und\nkostenfrei weiter. Also warum nicht auch mal Sales Know-how weitergeben? Eine gute Gelegenheit dafür war\nein Kaminabend\nbeim Center für Innovation & Entrepreneurship, kurz CIE, am KIT. Das CIE hat sich zum\nZiel gesetzt Unternehmensgründungen – unkonventionell und unbürokratisch – zu fördern. Angegliedert an das Institut für\nEntrepreneurship, Technologie-Management und Innovation des KIT, werden studentische Gründer kostenfrei mit diversen\nAngeboten unterstützt.","blog/warum-nicht-auch-mal-sales-know-how-weitergeben",[],"Technisches Wissen und Erfahrungen geben wir bereits über unser Firmenblog und auch bei Veranstaltungen gerne und kostenfrei weiter. Also warum nicht auch mal Sales Know-how weitergeben? Eine gute Gelegenheit dafür…","caLMtuoGnniP5bgR0IjftK5RDYbOaAHKimQUBP94aRg",{"id":48752,"title":48753,"author":48754,"body":48755,"category":49421,"date":49422,"description":49423,"extension":1034,"link":49424,"meta":49425,"navigation":916,"path":49426,"seo":49427,"slug":48759,"stem":49429,"tags":49430,"teaser":49432,"__hash__":49433},"blog/blog/building-android-projects-with-maven-part-2-releases-with-maven.md","Building Android projects with maven – part 2: Releases with maven",[190],{"type":432,"value":48756,"toc":49415},[48757,48760,48774,48778,48781,48784,48813,48816,48825,48878,48887,48896,48900,48903,48957,48960,49029,49033,49036,49039,49042,49110,49113,49314,49317,49331,49334,49353,49356,49359,49363,49366,49375,49378,49381,49390,49393,49398,49401,49409,49412],[435,48758,48753],{"id":48759},"building-android-projects-with-maven-part-2-releases-with-maven",[439,48761,43313,48762,48767,48768,48773],{},[1002,48763,48766],{"href":48764,"rel":48765},"http://blog.synyx.de/?p=4593",[1006],"my previous post",", I showed you the basic setup for android with maven using\nthe ",[1002,48769,48772],{"href":48770,"rel":48771},"http://code.google.com/p/maven-android-plugin/",[1006],"android-maven-plugin",". Now I’ll show you how to configure it to\nmake releases with maven, and how to configure the plugins to save you some work.",[3938,48775,48777],{"id":48776},"configuring-the-keystore-data","Configuring the keystore data",[439,48779,48780],{},"If you have used the release archetype like in the previous post, most of the work is already done. The necessary\nplugins are configured and only need some additional data, like the data for your release keystore.",[439,48782,48783],{},"Looking into the poms, you’ll find the property keys, you have to provide:",[464,48785,48787],{"className":6253,"code":48786,"language":6255,"meta":469,"style":469},"\n \u003Ckeystore>${sign.keystore}\u003C/keystore>\n \u003Calias>${sign.alias}\u003C/alias>\n \u003Cstorepass>${sign.storepass}\u003C/storepass>\n \u003Ckeypass>${sign.keypass}\u003C/keypass>\n\n",[471,48788,48789,48793,48798,48803,48808],{"__ignoreMap":469},[474,48790,48791],{"class":476,"line":477},[474,48792,917],{"emptyLinePlaceholder":916},[474,48794,48795],{"class":476,"line":507},[474,48796,48797],{}," \u003Ckeystore>${sign.keystore}\u003C/keystore>\n",[474,48799,48800],{"class":476,"line":547},[474,48801,48802],{}," \u003Calias>${sign.alias}\u003C/alias>\n",[474,48804,48805],{"class":476,"line":584},[474,48806,48807],{}," \u003Cstorepass>${sign.storepass}\u003C/storepass>\n",[474,48809,48810],{"class":476,"line":607},[474,48811,48812],{}," \u003Ckeypass>${sign.keypass}\u003C/keypass>\n",[439,48814,48815],{},"Simply configure them into your maven settings.xml in the release profile (which is also configured in the parent pom):",[439,48817,48818],{},[990,48819,48820,48821,17548],{},"(If you don’t have a release key yet, check this\nout ",[1002,48822,48823],{"href":48823,"rel":48824},"http://developer.android.com/tools/publishing/app-signing.html#cert",[1006],[464,48826,48828],{"className":6253,"code":48827,"language":6255,"meta":469,"style":469},"\n \u003Cprofile>\n \u003Cid>release\u003C/id>\n \u003Cproperties>\n \u003Csign.keystore>/path/to/your/keystore/keystore.keystore\u003C/sign.keystore>\n \u003Csign.alias>your-key-alias\u003C/sign.alias>\n \u003Csign.storepass>your-keystore-password\u003C/sign.storepass>\n \u003Csign.keypass>your-key-password\u003C/sign.keypass>\n \u003C/properties>\n \u003C/profile>\n\n",[471,48829,48830,48834,48839,48844,48849,48854,48859,48864,48869,48874],{"__ignoreMap":469},[474,48831,48832],{"class":476,"line":477},[474,48833,917],{"emptyLinePlaceholder":916},[474,48835,48836],{"class":476,"line":507},[474,48837,48838],{}," \u003Cprofile>\n",[474,48840,48841],{"class":476,"line":547},[474,48842,48843],{}," \u003Cid>release\u003C/id>\n",[474,48845,48846],{"class":476,"line":584},[474,48847,48848],{}," \u003Cproperties>\n",[474,48850,48851],{"class":476,"line":607},[474,48852,48853],{}," \u003Csign.keystore>/path/to/your/keystore/keystore.keystore\u003C/sign.keystore>\n",[474,48855,48856],{"class":476,"line":642},[474,48857,48858],{}," \u003Csign.alias>your-key-alias\u003C/sign.alias>\n",[474,48860,48861],{"class":476,"line":663},[474,48862,48863],{}," \u003Csign.storepass>your-keystore-password\u003C/sign.storepass>\n",[474,48865,48866],{"class":476,"line":694},[474,48867,48868],{}," \u003Csign.keypass>your-key-password\u003C/sign.keypass>\n",[474,48870,48871],{"class":476,"line":700},[474,48872,48873],{}," \u003C/properties>\n",[474,48875,48876],{"class":476,"line":913},[474,48877,22452],{},[439,48879,48880,48881,48886],{},"For our case, we removed everything related to proguard from the\nconfigs, ",[1002,48882,48885],{"href":48883,"rel":48884},"http://www.youtube.com/watch?v=4Z2Z23SAFVA",[1006],"beacuase"," we don’t use it for this project. Instead of removing\neverything, you could also just disable it with the android-maven-plugin configuration",[464,48888,48890],{"className":6253,"code":48889,"language":6255,"meta":469,"style":469},"\u003Cproguardskip>true\u003C/proguardskip>\n",[471,48891,48892],{"__ignoreMap":469},[474,48893,48894],{"class":476,"line":477},[474,48895,48889],{},[3938,48897,48899],{"id":48898},"adding-scm-connection-and-distribution-management","Adding scm connection and distribution management",[439,48901,48902],{},"Like you would normally, using maven releases, you have to add an scm connection to your parent pom, e.g.:",[464,48904,48906],{"className":15039,"code":48905,"language":15041,"meta":469,"style":469},"\u003Cscm>\n \u003Cdeveloperconnection\n >scm:git:ssh://asd@some.git.server/my-android-project.git\u003C/developerconnection\n >\n \u003Ctag>HEAD\u003C/tag>\n\u003C/scm>\n",[471,48907,48908,48916,48923,48930,48935,48949],{"__ignoreMap":469},[474,48909,48910,48912,48914],{"class":476,"line":477},[474,48911,15048],{"class":503},[474,48913,22200],{"class":523},[474,48915,15070],{"class":503},[474,48917,48918,48920],{"class":476,"line":507},[474,48919,15075],{"class":503},[474,48921,48922],{"class":523},"developerconnection\n",[474,48924,48925,48928],{"class":476,"line":547},[474,48926,48927],{"class":503}," >scm:git:ssh://asd@some.git.server/my-android-project.git\u003C/",[474,48929,48922],{"class":523},[474,48931,48932],{"class":476,"line":584},[474,48933,48934],{"class":503}," >\n",[474,48936,48937,48939,48942,48945,48947],{"class":476,"line":607},[474,48938,15075],{"class":503},[474,48940,48941],{"class":523},"tag",[474,48943,48944],{"class":503},">HEAD\u003C/",[474,48946,48941],{"class":523},[474,48948,15070],{"class":503},[474,48950,48951,48953,48955],{"class":476,"line":642},[474,48952,15168],{"class":503},[474,48954,22200],{"class":523},[474,48956,15070],{"class":503},[439,48958,48959],{},"If you have a server to deploy your maven artifacts to, now is the time to add it to your config (if you only build and\ndeploy them from your local machine, you don’t need this):",[464,48961,48963],{"className":6253,"code":48962,"language":6255,"meta":469,"style":469},"\n \u003Cdistributionmanagement>\n \u003Crepository>\n \u003Cname>releases-repository\u003C/name>\n \u003Cid>releases.some.address\u003C/id>\n \u003Curl>https://some.address/content/repositories/releases/\u003C/url>\n \u003C/repository>\n \u003Csnapshotrepository>\n \u003Cname>snapshots-repository\u003C/name>\n \u003Cid>snapshots.some.address\u003C/id>\n \u003Curl>https://some.address/content/repositories/snapshots/\u003C/url>\n \u003C/snapshotrepository>\n \u003C/distributionmanagement>\n\n",[471,48964,48965,48969,48974,48979,48984,48989,48994,48999,49004,49009,49014,49019,49024],{"__ignoreMap":469},[474,48966,48967],{"class":476,"line":477},[474,48968,917],{"emptyLinePlaceholder":916},[474,48970,48971],{"class":476,"line":507},[474,48972,48973],{}," \u003Cdistributionmanagement>\n",[474,48975,48976],{"class":476,"line":547},[474,48977,48978],{}," \u003Crepository>\n",[474,48980,48981],{"class":476,"line":584},[474,48982,48983],{}," \u003Cname>releases-repository\u003C/name>\n",[474,48985,48986],{"class":476,"line":607},[474,48987,48988],{}," \u003Cid>releases.some.address\u003C/id>\n",[474,48990,48991],{"class":476,"line":642},[474,48992,48993],{}," \u003Curl>https://some.address/content/repositories/releases/\u003C/url>\n",[474,48995,48996],{"class":476,"line":663},[474,48997,48998],{}," \u003C/repository>\n",[474,49000,49001],{"class":476,"line":694},[474,49002,49003],{}," \u003Csnapshotrepository>\n",[474,49005,49006],{"class":476,"line":700},[474,49007,49008],{}," \u003Cname>snapshots-repository\u003C/name>\n",[474,49010,49011],{"class":476,"line":913},[474,49012,49013],{}," \u003Cid>snapshots.some.address\u003C/id>\n",[474,49015,49016],{"class":476,"line":920},[474,49017,49018],{}," \u003Curl>https://some.address/content/repositories/snapshots/\u003C/url>\n",[474,49020,49021],{"class":476,"line":926},[474,49022,49023],{}," \u003C/snapshotrepository>\n",[474,49025,49026],{"class":476,"line":932},[474,49027,49028],{}," \u003C/distributionmanagement>\n",[3938,49030,49032],{"id":49031},"configure-the-android-manifest-from-maven","Configure the Android Manifest from Maven",[439,49034,49035],{},"To save us some nasty work, we also want to configure the Android manifest with our maven profiles (e.g. setting Debug\nto false for release, automatically update the version on releases, etc.)",[439,49037,49038],{},"For this, we’ll need to filter the manifest in maven:",[439,49040,49041],{},"add following plugins to the parent pom:",[464,49043,49045],{"className":6253,"code":49044,"language":6255,"meta":469,"style":469},"\n \u003Cplugin>\n \u003Cgroupid>org.apache.maven.plugins\u003C/groupid>\n \u003Cartifactid>maven-resources-plugin\u003C/artifactid>\n \u003Cversion>2.6\u003C/version>\n \u003Cconfiguration>\n \u003Cencoding>UTF-8\u003C/encoding>\n \u003C/configuration>\n \u003C/plugin>\n \u003Cplugin>\n \u003Cgroupid>org.codehaus.mojo\u003C/groupid>\n \u003Cartifactid>build-helper-maven-plugin\u003C/artifactid>\n \u003Cversion>1.8\u003C/version>\n \u003C/plugin>\n\n",[471,49046,49047,49051,49055,49060,49065,49070,49074,49079,49083,49087,49091,49096,49101,49106],{"__ignoreMap":469},[474,49048,49049],{"class":476,"line":477},[474,49050,917],{"emptyLinePlaceholder":916},[474,49052,49053],{"class":476,"line":507},[474,49054,22354],{},[474,49056,49057],{"class":476,"line":547},[474,49058,49059],{}," \u003Cgroupid>org.apache.maven.plugins\u003C/groupid>\n",[474,49061,49062],{"class":476,"line":584},[474,49063,49064],{}," \u003Cartifactid>maven-resources-plugin\u003C/artifactid>\n",[474,49066,49067],{"class":476,"line":607},[474,49068,49069],{}," \u003Cversion>2.6\u003C/version>\n",[474,49071,49072],{"class":476,"line":642},[474,49073,16996],{},[474,49075,49076],{"class":476,"line":663},[474,49077,49078],{}," \u003Cencoding>UTF-8\u003C/encoding>\n",[474,49080,49081],{"class":476,"line":694},[474,49082,17046],{},[474,49084,49085],{"class":476,"line":700},[474,49086,22437],{},[474,49088,49089],{"class":476,"line":913},[474,49090,22354],{},[474,49092,49093],{"class":476,"line":920},[474,49094,49095],{}," \u003Cgroupid>org.codehaus.mojo\u003C/groupid>\n",[474,49097,49098],{"class":476,"line":926},[474,49099,49100],{}," \u003Cartifactid>build-helper-maven-plugin\u003C/artifactid>\n",[474,49102,49103],{"class":476,"line":932},[474,49104,49105],{}," \u003Cversion>1.8\u003C/version>\n",[474,49107,49108],{"class":476,"line":938},[474,49109,22437],{},[439,49111,49112],{},"… and the config for them in the app module (and IT module) pom:",[464,49114,49116],{"className":6253,"code":49115,"language":6255,"meta":469,"style":469},"\n \u003Cbuild>\n \u003Cresources>\n \u003Cresource>\n \u003Ctargetpath>${project.basedir}/target/filtered-manifest\u003C/targetpath>\n \u003Cfiltering>true\u003C/filtering>\n \u003Cdirectory>${basedir}\u003C/directory>\n \u003Cincludes>\n \u003Cinclude>AndroidManifest.xml\u003C/include>\n \u003C/includes>\n \u003C/resource>\n \u003C/resources>\n ...\n \u003Cplugins>\n ...\n \u003Cplugin>\n \u003Cgroupid>org.codehaus.mojo\u003C/groupid>\n \u003Cartifactid>build-helper-maven-plugin\u003C/artifactid>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>parse-version\u003C/id>\n \u003Cgoals>\n \u003Cgoal>parse-version\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003Cplugin>\n \u003Cartifactid>maven-resources-plugin\u003C/artifactid>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>initialize\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>resources\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n ...\n \u003C/plugins>\n \u003C/build>\n\n",[471,49117,49118,49122,49127,49132,49137,49142,49147,49152,49157,49162,49167,49172,49177,49182,49187,49192,49197,49202,49207,49212,49217,49222,49227,49232,49237,49242,49247,49252,49257,49262,49266,49270,49275,49279,49284,49288,49292,49296,49300,49304,49309],{"__ignoreMap":469},[474,49119,49120],{"class":476,"line":477},[474,49121,917],{"emptyLinePlaceholder":916},[474,49123,49124],{"class":476,"line":507},[474,49125,49126],{}," \u003Cbuild>\n",[474,49128,49129],{"class":476,"line":547},[474,49130,49131],{}," \u003Cresources>\n",[474,49133,49134],{"class":476,"line":584},[474,49135,49136],{}," \u003Cresource>\n",[474,49138,49139],{"class":476,"line":607},[474,49140,49141],{}," \u003Ctargetpath>${project.basedir}/target/filtered-manifest\u003C/targetpath>\n",[474,49143,49144],{"class":476,"line":642},[474,49145,49146],{}," \u003Cfiltering>true\u003C/filtering>\n",[474,49148,49149],{"class":476,"line":663},[474,49150,49151],{}," \u003Cdirectory>${basedir}\u003C/directory>\n",[474,49153,49154],{"class":476,"line":694},[474,49155,49156],{}," \u003Cincludes>\n",[474,49158,49159],{"class":476,"line":700},[474,49160,49161],{}," \u003Cinclude>AndroidManifest.xml\u003C/include>\n",[474,49163,49164],{"class":476,"line":913},[474,49165,49166],{}," \u003C/includes>\n",[474,49168,49169],{"class":476,"line":920},[474,49170,49171],{}," \u003C/resource>\n",[474,49173,49174],{"class":476,"line":926},[474,49175,49176],{}," \u003C/resources>\n",[474,49178,49179],{"class":476,"line":932},[474,49180,49181],{}," ...\n",[474,49183,49184],{"class":476,"line":938},[474,49185,49186],{}," \u003Cplugins>\n",[474,49188,49189],{"class":476,"line":944},[474,49190,49191],{}," ...\n",[474,49193,49194],{"class":476,"line":950},[474,49195,49196],{}," \u003Cplugin>\n",[474,49198,49199],{"class":476,"line":956},[474,49200,49201],{}," \u003Cgroupid>org.codehaus.mojo\u003C/groupid>\n",[474,49203,49204],{"class":476,"line":962},[474,49205,49206],{}," \u003Cartifactid>build-helper-maven-plugin\u003C/artifactid>\n",[474,49208,49209],{"class":476,"line":4876},[474,49210,49211],{}," \u003Cexecutions>\n",[474,49213,49214],{"class":476,"line":4888},[474,49215,49216],{}," \u003Cexecution>\n",[474,49218,49219],{"class":476,"line":4900},[474,49220,49221],{}," \u003Cid>parse-version\u003C/id>\n",[474,49223,49224],{"class":476,"line":4913},[474,49225,49226],{}," \u003Cgoals>\n",[474,49228,49229],{"class":476,"line":4921},[474,49230,49231],{}," \u003Cgoal>parse-version\u003C/goal>\n",[474,49233,49234],{"class":476,"line":4932},[474,49235,49236],{}," \u003C/goals>\n",[474,49238,49239],{"class":476,"line":4938},[474,49240,49241],{}," \u003C/execution>\n",[474,49243,49244],{"class":476,"line":4946},[474,49245,49246],{}," \u003C/executions>\n",[474,49248,49249],{"class":476,"line":4952},[474,49250,49251],{}," \u003C/plugin>\n",[474,49253,49254],{"class":476,"line":4957},[474,49255,49256],{}," \u003Cplugin>\n",[474,49258,49259],{"class":476,"line":4969},[474,49260,49261],{}," \u003Cartifactid>maven-resources-plugin\u003C/artifactid>\n",[474,49263,49264],{"class":476,"line":4990},[474,49265,49211],{},[474,49267,49268],{"class":476,"line":5001},[474,49269,49216],{},[474,49271,49272],{"class":476,"line":5013},[474,49273,49274],{}," \u003Cphase>initialize\u003C/phase>\n",[474,49276,49277],{"class":476,"line":5024},[474,49278,49226],{},[474,49280,49281],{"class":476,"line":5035},[474,49282,49283],{}," \u003Cgoal>resources\u003C/goal>\n",[474,49285,49286],{"class":476,"line":5047},[474,49287,49236],{},[474,49289,49290],{"class":476,"line":5055},[474,49291,49241],{},[474,49293,49294],{"class":476,"line":5062},[474,49295,49246],{},[474,49297,49298],{"class":476,"line":5067},[474,49299,49251],{},[474,49301,49302],{"class":476,"line":5072},[474,49303,49191],{},[474,49305,49306],{"class":476,"line":5084},[474,49307,49308],{}," \u003C/plugins>\n",[474,49310,49311],{"class":476,"line":5100},[474,49312,49313],{}," \u003C/build>\n",[439,49315,49316],{},"Don’t forget to update the Android manifest path in the android-maven-plugin to the filtered manifest location!",[464,49318,49320],{"className":6253,"code":49319,"language":6255,"meta":469,"style":469},"\n\u003Candroidmanifestfile>${project.build.directory}/filtered-manifest/AndroidManifest.xml\u003C/androidmanifestfile>\n\n",[471,49321,49322,49326],{"__ignoreMap":469},[474,49323,49324],{"class":476,"line":477},[474,49325,917],{"emptyLinePlaceholder":916},[474,49327,49328],{"class":476,"line":507},[474,49329,49330],{},"\u003Candroidmanifestfile>${project.build.directory}/filtered-manifest/AndroidManifest.xml\u003C/androidmanifestfile>\n",[439,49332,49333],{},"In the Android Manifest files of the modules, you can now insert the maven property placeholders for the versionCode and\nversionName",[464,49335,49337],{"className":6253,"code":49336,"language":6255,"meta":469,"style":469},"\n\u003Cmanifest ....=\"\" android:versioncode=\"${parsedVersion.majorVersion}${parsedVersion.minorVersion}${parsedVersion.incrementalVersion}\" android:versionname=\"${project.version}\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\u003C/manifest>\n",[471,49338,49339,49343,49348],{"__ignoreMap":469},[474,49340,49341],{"class":476,"line":477},[474,49342,917],{"emptyLinePlaceholder":916},[474,49344,49345],{"class":476,"line":507},[474,49346,49347],{},"\u003Cmanifest ....=\"\" android:versioncode=\"${parsedVersion.majorVersion}${parsedVersion.minorVersion}${parsedVersion.incrementalVersion}\" android:versionname=\"${project.version}\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n",[474,49349,49350],{"class":476,"line":547},[474,49351,49352],{},"\u003C/manifest>\n",[439,49354,49355],{},"If you now build your project (clean install), the generated Android manifest should include the versionCode “100” and\nthe version “1.0-Snapshot” (If you didn’t change the version in the pom, yet).",[439,49357,49358],{},"Other than that, you have plenty of configuration possibilities of your AndroidManifest within the\nandroid-maven-plugin config, and with this setup you can easily alter the values for the different build profiles.",[3938,49360,49362],{"id":49361},"building-the-release","Building the release",[439,49364,49365],{},"To build the release, use",[464,49367,49369],{"className":16895,"code":49368,"language":16897,"meta":469,"style":469},"mvn release:prepare -Prelease\n",[471,49370,49371],{"__ignoreMap":469},[474,49372,49373],{"class":476,"line":477},[474,49374,49368],{},[439,49376,49377],{},"release:perform is not necessary here!",[439,49379,49380],{},"After the release is done, run",[464,49382,49384],{"className":16895,"code":49383,"language":16897,"meta":469,"style":469},"mvn release:clean\n",[471,49385,49386],{"__ignoreMap":469},[474,49387,49388],{"class":476,"line":477},[474,49389,49383],{},[439,49391,49392],{},"to clean up.",[439,49394,49395],{},[990,49396,49397],{},"Note: you might want to let your scm ignore some files (e.g. release.properties and the target folders) so that you\ndon’t have to further clean up your directories after you run the release.",[439,49399,49400],{},"That’s it, now you are able to build your android app releases with maven!",[439,49402,49403,49404],{},"Here are the sources to check\nagainst: ",[1002,49405,49408],{"href":49406,"rel":49407},"https://media.synyx.de/uploads//2013/09/my-android-project.zip",[1006],"my-android-project sources",[439,49410,49411],{},"My next blog will be about Android & Continuous integration with Jenkins, so check our blog from time to time 🙂",[1024,49413,49414],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":469,"searchDepth":507,"depth":507,"links":49416},[49417,49418,49419,49420],{"id":48776,"depth":507,"text":48777},{"id":48898,"depth":507,"text":48899},{"id":49031,"depth":507,"text":49032},{"id":49361,"depth":507,"text":49362},[1030,11122,1413],"2013-09-18T15:33:14","In my previous post, I showed you the basic setup for android with maven using\\nthe android-maven-plugin. Now I’ll show you how to configure it to\\nmake releases with maven, and how to configure the plugins to save you some work.","https://synyx.de/blog/building-android-projects-with-maven-part-2-releases-with-maven/",{},"/blog/building-android-projects-with-maven-part-2-releases-with-maven",{"title":48753,"description":49428},"In my previous post, I showed you the basic setup for android with maven using\nthe android-maven-plugin. Now I’ll show you how to configure it to\nmake releases with maven, and how to configure the plugins to save you some work.","blog/building-android-projects-with-maven-part-2-releases-with-maven",[11132,48772,22988,49431,22737],"maven-android-plugin","In my previous post, I showed you the basic setup for android with maven using the android-maven-plugin. Now I’ll show you how to configure it to make releases with maven,…","Oemk-uLLhOk07vwymHqwLQY0cgqWKiErUGYoSegG5kA",{"id":49435,"title":49436,"author":49437,"body":49438,"category":49550,"date":49551,"description":49447,"extension":1034,"link":49552,"meta":49553,"navigation":916,"path":49554,"seo":49555,"slug":49556,"stem":49557,"tags":49558,"teaser":49560,"__hash__":49561},"blog/blog/neo4j-jug-karslruhe.md","Neo4J JUG Karlsruhe",[235],{"type":432,"value":49439,"toc":49548},[49440,49443,49448,49451,49454,49459,49462,49465,49468,49471,49474,49477,49480,49485,49488,49491,49512,49515,49518,49521,49526,49529,49532,49541,49545],[435,49441,49436],{"id":49442},"neo4j-jug-karlsruhe",[439,49444,49445],{},[448,49446,49447],{},"Of graphs and relationships – An evening at the JUG Karlsruhe",[439,49449,49450],{},"The Java User Group in Karlsruhe (JUG Karlsruhe) arranges free-for-all presentations about topics around, but not\nlimited to, Java. For the first time in the new location, now at the Karlsruhe ‘Hochschule für Technik&Wirtschaft’ .\nThe talk last Wednesday (September 4th 2013) was dedicated to the beautiful world of graphs. The presentation was given\nby Peter Neubauer, a graph enthusiast. Christian Mennerich and I don’t miss the chance to see this event.",[439,49452,49453],{},"Peter, one of the founder of Neo Technology and the graph database Neo4j, was presenting ‘Neo4j und die wunderbare Welt\nder Graphen’. In a talk that stretched to two and a half hours, without getting boring, Peter presented some graph\ndatabase basics, the Neo4j graph database and lots of applications and code examples. At first, Peter talked a bit about\nthe relational database world and the NoSQL movement, but he skipped most of this part. He introduced the property graph\nmodel and how it is reflected in Neo4j. Graphs can be the data structure of choice for data mining in highly related\ndata sets. But there are lots of other interesting applications for the Neo4j graph database, ranging from social\nnetwork applications and recommendation systems to routing problems.",[439,49455,49456],{},[448,49457,49458],{},"Neo4j and graph-databases",[439,49460,49461],{},"Neo4j is a graph database, what means that the basic data structure is a graph, in opposition to classic relational\ndatabases that deal with relations only. Neo4j is based on so called *property graphs*. A property graph consists of\n*nodes* that are linked to other nodes by *relationships*. Here, as in real live relationships, the direction\nmatters.",[439,49463,49464],{},"*Properties* can be assigned to both, the nodes and the relationships, and more than one relationship between nodes is\npossible. So, a property graph is what you actually call a directed labeled multi graph. Properties are ‘flat data’, no\nlinks to real objects is directly supported (but nobody stops you from putting a serialized object as a property…).",[439,49466,49467],{},"Neo4j is implemented in Java. Nodes are stored as doubly linked lists, making graph traversals possible in the direction\nof ingoing and outgoing relationships. This implementation makes operations concerning neighbored nodes (i.e. nodes\nlinked by a relationship) possible in constant time. Thus, graphs provide what you call ‘index-free adjacency’. At the\nmoment, more than 17 Billion nodes and relationships can be managed by a single instance of Neo4j (enough to represent a\ncontinents data points in Open Street Map). To traverse a path in a graph, you need an entry node in the graph.",[439,49469,49470],{},"Neo4j comes along with full ACID support. The ACID properties, atomicity, consistency, isolation and durability, are\nwell known from relational databases. In short, ACID means that a transaction manipulating data in the databse is\nexecuted as if it had exclusive access, and either all its operations take effect, or none. In the sense of the CAP\ntheorem, Neo4j is on the CA side of the CAP triangle, in opposition to many other NoSQL databases that ‘sacrifice’ A or\nC for P. (CAP stands for consistency, availability, partition tolerance.)",[439,49472,49473],{},"Thus, horizontal scale-out is not an easy problem in Neo4j. To gain high availability, Neo4j offers a master/slave\nreplication strategy, where read performance is ensured by replication of the data. Write operations here become a\nbottleneck, as a dedicated master node is responsible for the write processes. If the master fails (e.g. due to network\nproblems), a new master is automatically elected by a quorum. Sharding in graphs seems to be complicated, as it involves\nthe problem of partitioning a graph.",[439,49475,49476],{},"In addition to the Neo4j database a REST-server and a web front-end are available to maintain the data in the\ndatabase. In the web-frontend, the graph itself can be visualized in a node-and-arrow manner, starting from a\nselected node. Neighboring nodes and relationships can be folded and unfolded to explore the data. Algorithms to arrange\nthe graph clearly on the screen are used. Depending on the size of the graph and the degree of the nodes (i.e. the\nnumber of ingoing and outgoing relationships) the visual exploration can soon lose its fun.",[439,49478,49479],{},"Neo4j comes in of three editions: The Community Edition is for first steps with the graph database, the Advanced and\nEnterprise Edition are for more sophisticated and production set-ups. The community edition is free and might be used\nin a similar way as MySQL might be used. For the usage of the Advanced and Enterprise Edition, licensing is needed. For\nOpen-Source projects, there is the AGPL, to include Neo4j in closed-source business application, the Neo Technology\nCommercial License (NTCL) is available. Actual terms of the license that fits your purposes and business models can be\ndiscussed with Neotechnology directly. But the good thing is: Neo4j is free in all three versions for testing and\nevaluating purposes!",[439,49481,49482],{},[448,49483,49484],{},"Working with graphs",[439,49486,49487],{},"Neo4j offers different possibilities to interact and query the graph. Among others, there are: A REST-interface,\ninterfaces to the query languages Cypher, and the usage of Spring Data Neo4j. The REST interface accepts JSON and can be\nqueried using.e.g. ‘curl’.",[439,49489,49490],{},"Cypher is an easy-to-learn query language, inspired by ASCII-arts: You kind of ‘draw’ your query, using symbols for\nnodes, and the directed relationships ( (…) and -[…]-> ). With that you can ask for the friends of Tobis friends:",[11947,49492,49493,49499,49509],{},[439,49494,49495,49496],{},"START tobi=node:PEOPLE",[471,49497,49498],{},"(name='Tobi'``)",[439,49500,49501,49502,49505,49506,49508],{},"MATCH (tobi)-[",[49503,49504],"friend",{},"]->(friend)-[",[49503,49507],{},"]->(friend2)",[439,49510,49511],{},"RETURN friend2",[439,49513,49514],{},"Hopefully, Tobi will be in the result set. 😉",[439,49516,49517],{},"A different way to work with graphs is using the Spring Data Neo4j API. The object-graph mapping works similar to the\nobjects-relational mapping in Spring Data for relational databases. Using annotations for nodes and relationships,\nobjects are linked to nodes and relations in the database. It is possible to include Cypher queries in annotations, what\nmakes the API flexible to use.",[439,49519,49520],{},"Compared to relational database systems, queries become incredibly fast due the locality and topology of the nodes. As\nthe friends-of-friend query shown above will be answered in a time, almost independent of the number of persons stored\nin the database, as only neighboring nodes are searched what keeps the data local. (Actually, the time for the query\nscales with the number of friends a person has.) In a relational database system, the query time strongly depends on the\nnumber of persons in the system, so that exceeding a certain number of persons, in a relational database system the\nquery becomes unfeasible in an acceptable time.",[439,49522,49523],{},[448,49524,49525],{},"Thinking in graphs",[439,49527,49528],{},"Thinking in graphs needs getting used to. Queries have to be formulated as graph traversals or graph-related problems.\nBut once the thought is accepted and the classical thinking in relations is abandoned, the advantages become clear. Many\nscenarios can be described in the graph world. Social networks and product recommendation systems are an obvious\nexample. But also the problem of dynamically assigning roles and rights in a content management system (CMS), a hard\nproblem, can be formulated and solved as a shortest path problem in a graph. Graphs are often easy to understand, as\nthey are ‘white board friendly’: Once painted to a white board, the data model is there and ready to be implemented. No\nmatter if you draw in an abstract manner, or as a concrete example.",[439,49530,49531],{},"The concept of a graph is powerful, and as general as the concept of relations in relational databases. They are fast to\ntraverse when the data is highly connected. And giving the easy data structure of a graph, queries are often easy to\nformulate. On the other side, sharding can be a real problem in big data scenarios (but it also is in relational\ndatabases), and the shift towards ‘thinking in graphs’ might take some time to adjust to. Give it a try, it is fun! (But\ndon’t make it another all-dominant paradigm!)",[439,49533,49534,49535,49540],{},"Peter showed lots of interesting application of models of real world scenarios with graphs, covering CMS applications,\nstrutr (structr.org), routing (",[1002,49536,49539],{"href":49537,"rel":49538},"http://www.transportdublin.ie",[1006],"www.transportdublin.ie","), gene sequencing and more. On his laptop, he also could show\ncoding examples of programming against the Spring Data Neo4j API, proving the elegance of this approach. In a\nlive-experiment, Peter showed exemplary queries against a database he had populated with data from from MusicBrainz and\nLast.fm, about 15 GBs in size! With queries like ‘What music does A like that B likes’ Peter showed how easy it is to\ncreate a music recommendations in this huge amount of data by exploiting the locality of the data. The Cypher expression\nhe used all just span a few lines.",[439,49542,49543],{},[448,49544,9392],{},[439,49546,49547],{},"Graphs are great, we all knew that before. But there are more applications to graphs than one might have thought of.\nNeo4j comes with solutions for many often occurring problems, such as finding the shortest path between two nodes. After\ndiscussing the importance of self relationships of nodes in a graph database, Peter proudly presented his T-shirt, so\nwe now know: (peter)-[FRIEND]->(peter). And, after paying the beer, he surely has a lot more friends now. 😉 But\nseriously: Thank you Peter for the presentation, the discussion and the interesting insides into the applications of\ngraphs and graph databases! It was a long, informative and fun evening.",{"title":469,"searchDepth":507,"depth":507,"links":49549},[],[1031],"2013-09-16T15:39:00","https://synyx.de/blog/neo4j-jug-karslruhe/",{},"/blog/neo4j-jug-karslruhe",{"title":49436,"description":49447},"neo4j-jug-karslruhe","blog/neo4j-jug-karslruhe",[49559],"neo4j-jug-nosql","Of graphs and relationships – An evening at the JUG Karlsruhe The Java User Group in Karlsruhe (JUG Karlsruhe) arranges free-for-all presentations about topics around, but not limited to, Java.…","WZIKINQdhGvmR4WoqHkrwoNyayKfJ5MDG9ceOjHni5w",{"id":49563,"title":49564,"author":49565,"body":49566,"category":50276,"date":50277,"description":50278,"extension":1034,"link":50279,"meta":50280,"navigation":916,"path":50281,"seo":50282,"slug":49570,"stem":50283,"tags":50284,"teaser":50287,"__hash__":50288},"blog/blog/building-android-projects-with-maven-part-1-setup.md","Building Android projects with Maven – Part 1: Setup",[190],{"type":432,"value":49567,"toc":50266},[49568,49571,49574,49577,49581,49588,49591,49596,49610,49616,49619,49622,49631,49634,49637,49640,49664,49669,49673,49676,49679,49683,49686,49689,49692,49695,49698,49701,49705,49708,49711,49714,49717,49720,49723,49726,49729,49732,49735,49739,49742,49781,49784,49787,49790,49794,49801,49804,49843,49846,49870,49873,49964,49967,49975,49978,49987,49990,49994,49997,50006,50009,50012,50046,50049,50078,50081,50111,50114,50117,50220,50223,50226,50250,50253,50261,50264],[435,49569,49564],{"id":49570},"building-android-projects-with-maven-part-1-setup",[439,49572,49573],{},"Building and managing Android projects with maven is not as easy as it could be. So in this blog, I’ll show you how we\nmanaged to get it work nicely.",[439,49575,49576],{},"In this example, we’ll create a parent project with an app module and a separate instrumentation tests module.",[3938,49578,49580],{"id":49579},"project-setup","Project setup",[439,49582,49583,49584,11288],{},"The quickest approach to create a new Android project with maven is using a maven archetype from\naquinet (",[1002,49585,49586],{"href":49586,"rel":49587},"http://mvnrepository.com/artifact/de.akquinet.android.archetypes",[1006],[439,49589,49590],{},"For this project, we’ll use the android-release archetype, as it creates a skeleton for exactly our case, as we also\nwant to release the app with maven.",[439,49592,49593],{},[990,49594,49595],{},"Hint: be sure to use the latest version of the archetype",[464,49597,49599],{"className":16895,"code":49598,"language":16897,"meta":469,"style":469},"\nmvn archetype:generate -DarchetypeArtifactId=android-release -DarchetypeGroupId=de.akquinet.android.archetypes -DarchetypeVersion=1.0.9 -DgroupId=com.foo.bar -DartifactId=my-android-project -Dpackage=com.foo.bar.android\n",[471,49600,49601,49605],{"__ignoreMap":469},[474,49602,49603],{"class":476,"line":477},[474,49604,917],{"emptyLinePlaceholder":916},[474,49606,49607],{"class":476,"line":507},[474,49608,49609],{},"mvn archetype:generate -DarchetypeArtifactId=android-release -DarchetypeGroupId=de.akquinet.android.archetypes -DarchetypeVersion=1.0.9 -DgroupId=com.foo.bar -DartifactId=my-android-project -Dpackage=com.foo.bar.android\n",[439,49611,49612,49613],{},"With this, a parent project named after the artifactId gets created in your current directory. Inside, you’ll find two\nmodules, one for the app and one for the instrumentation tests, and the pom.xml of the parent is also already\npre-configured with both of them as modules. In the pom, you’ll find several plugins already configured for you, on top\nof all the ",[1002,49614,48772],{"href":48770,"rel":49615},[1006],[439,49617,49618],{},"Now check the pom for the android-maven-plugin version and update it to the latest version (3.6.1 at the moment I\nwrote this blog). It should be at least 3.6.0 – if not, the build will fail, because the aapt tool can’t be found as\nit moved in a previous android release and the plugin only considers this since version 3.6.0.",[439,49620,49621],{},"To test the setup, simply go into the parent project and run",[464,49623,49625],{"className":16895,"code":49624,"language":16897,"meta":469,"style":469},"mvn clean install\n",[471,49626,49627],{"__ignoreMap":469},[474,49628,49629],{"class":476,"line":477},[474,49630,49624],{},[439,49632,49633],{},"Now everything should be compiled and the instrumentation tests should run on any emulator or device that is connected.",[439,49635,49636],{},"If you get an error stating “Platform/API level 16 not available”, install it via the SDK Manager, or replace the sdk\nversion in the pom to an available one.",[439,49638,49639],{},"In the android-maven-plugin config:",[464,49641,49643],{"className":6253,"code":49642,"language":6255,"meta":469,"style":469},"\n \u003Csdk>\n \u003Cplatform>16\u003C/platform>\n \u003C/sdk>\n",[471,49644,49645,49649,49654,49659],{"__ignoreMap":469},[474,49646,49647],{"class":476,"line":477},[474,49648,917],{"emptyLinePlaceholder":916},[474,49650,49651],{"class":476,"line":507},[474,49652,49653],{}," \u003Csdk>\n",[474,49655,49656],{"class":476,"line":547},[474,49657,49658],{}," \u003Cplatform>16\u003C/platform>\n",[474,49660,49661],{"class":476,"line":584},[474,49662,49663],{}," \u003C/sdk>\n",[439,49665,49666],{},[990,49667,49668],{},"Note: the archetypes from aquinet also include their androlog logging framework which is a wrapper around the Android\nlogging that adds the functionality of disabling the logging for releases and provides log4j-like configuration. You\ncould remove it from the poms, or you could give it a try, which I would recommend you to 🙂",[3938,49670,49672],{"id":49671},"setting-up-android-studio-to-work-with-maven","Setting up Android Studio to work with maven",[439,49674,49675],{},"To conveniently work on a maven project with Android studio, we have to set it to automatically import Maven projects,\nso that it notices changes to the pom.xml and updates it’s dependencies.",[439,49677,49678],{},"File -> Settings -> Maven -> Importing -> Import Maven projects automatically",[1065,49680,49682],{"id":49681},"import-the-project","Import the project:",[439,49684,49685],{},"* File -> Import project",[439,49687,49688],{},"* Select your parent project",[439,49690,49691],{},"* Import from external model -> Select Maven",[439,49693,49694],{},"* Check “Import Maven projects automatically” -> Next",[439,49696,49697],{},"* Select the Android platform -> Next",[439,49699,49700],{},"* Finish",[1065,49702,49704],{"id":49703},"configure-android-studio-to-also-build-using-maven","configure Android Studio to also build using Maven:",[439,49706,49707],{},"* Select the project",[439,49709,49710],{},"* Run -> Edit Configurations",[439,49712,49713],{},"* “+” Android Application",[439,49715,49716],{},"* Module: select Android App",[439,49718,49719],{},"* Launch default Activity",[439,49721,49722],{},"* Select default target device",[439,49724,49725],{},"* Before Launch: remove “Make”",[439,49727,49728],{},"* Before Launch: add Maven “clean install”",[439,49730,49731],{},"* OK",[439,49733,49734],{},"Now if you run the project in Android studio, Maven will be used.",[3938,49736,49738],{"id":49737},"running-android-lint","Running Android Lint",[439,49740,49741],{},"It’s recommended to run Android Lint to check warnings and errors. To run it with the android-maven-plugin, simply\ninsert this config to the android-maven-plugin int the pom (and edit the sources path accordingly to your project):",[464,49743,49745],{"className":6253,"code":49744,"language":6255,"meta":469,"style":469},"\n \u003Clint>\n \u003Cskip>false\u003C/skip>\n \u003Csources>${project.basedir}/my-android-project/src/main/java/\u003C/sources>\n \u003Cenablehtml>true\u003C/enablehtml>\n \u003Cenablexml>false\u003C/enablexml>\n \u003C/lint>\n",[471,49746,49747,49751,49756,49761,49766,49771,49776],{"__ignoreMap":469},[474,49748,49749],{"class":476,"line":477},[474,49750,917],{"emptyLinePlaceholder":916},[474,49752,49753],{"class":476,"line":507},[474,49754,49755],{}," \u003Clint>\n",[474,49757,49758],{"class":476,"line":547},[474,49759,49760],{}," \u003Cskip>false\u003C/skip>\n",[474,49762,49763],{"class":476,"line":584},[474,49764,49765],{}," \u003Csources>${project.basedir}/my-android-project/src/main/java/\u003C/sources>\n",[474,49767,49768],{"class":476,"line":607},[474,49769,49770],{}," \u003Cenablehtml>true\u003C/enablehtml>\n",[474,49772,49773],{"class":476,"line":642},[474,49774,49775],{}," \u003Cenablexml>false\u003C/enablexml>\n",[474,49777,49778],{"class":476,"line":663},[474,49779,49780],{}," \u003C/lint>\n",[439,49782,49783],{},"If you want to execute android lint on the build, add android:lint to the maven commands (e.g. “mvn clean install\nandroid:lint”)",[439,49785,49786],{},"Don’t forget to edit the Run config you previously created to include “android:lint”!",[439,49788,49789],{},"The results will be written to /target/lint-results/lint-results-html/. (We disabled the XML output and enabled HTML,\nbecause it just has a better readability and you have a way better overview on the HTML pages)",[3938,49791,49793],{"id":49792},"create-a-separate-instrumentation-test-profile","Create a separate Instrumentation Test Profile",[439,49795,49796,49797,49800],{},"We don’t want to install the app and run the Instrumentation Tests on ",[990,49798,49799],{},"every"," build, because it just takes so long. So\nwe’ll execute the tests only in a separate maven profile.",[439,49802,49803],{},"First, we’ll disable the instrumentation tests for all cases by inserting a configuration for the android-maven-plugin\nin the parent pom:",[464,49805,49807],{"className":6253,"code":49806,"language":6255,"meta":469,"style":469},"\n \u003Cconfiguration>\n \u003Ctest>\n \u003Cskip>true\u003C/skip>\n \u003C/test>\n ...\n \u003C/configuration>\n\n",[471,49808,49809,49813,49818,49823,49828,49833,49838],{"__ignoreMap":469},[474,49810,49811],{"class":476,"line":477},[474,49812,917],{"emptyLinePlaceholder":916},[474,49814,49815],{"class":476,"line":507},[474,49816,49817],{}," \u003Cconfiguration>\n",[474,49819,49820],{"class":476,"line":547},[474,49821,49822],{}," \u003Ctest>\n",[474,49824,49825],{"class":476,"line":584},[474,49826,49827],{}," \u003Cskip>true\u003C/skip>\n",[474,49829,49830],{"class":476,"line":607},[474,49831,49832],{}," \u003C/test>\n",[474,49834,49835],{"class":476,"line":642},[474,49836,49837],{}," ...\n",[474,49839,49840],{"class":476,"line":663},[474,49841,49842],{}," \u003C/configuration>\n",[439,49844,49845],{},"In the parent pom, add the new profile “IT”:",[464,49847,49849],{"className":6253,"code":49848,"language":6255,"meta":469,"style":469},"\n \u003Cprofile>\n \u003Cid>IT\u003C/id>\n \u003C/profile>\n",[471,49850,49851,49855,49860,49865],{"__ignoreMap":469},[474,49852,49853],{"class":476,"line":477},[474,49854,917],{"emptyLinePlaceholder":916},[474,49856,49857],{"class":476,"line":507},[474,49858,49859],{}," \u003Cprofile>\n",[474,49861,49862],{"class":476,"line":547},[474,49863,49864],{}," \u003Cid>IT\u003C/id>\n",[474,49866,49867],{"class":476,"line":584},[474,49868,49869],{}," \u003C/profile>\n",[439,49871,49872],{},"And in the pom of the IT module, we also add the IT profile and enable the tests for it again:",[464,49874,49876],{"className":6253,"code":49875,"language":6255,"meta":469,"style":469},"\n \u003Cprofile>\n \u003Cid>IT\u003C/id>\n \u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003Cgroupid>com.jayway.maven.plugins.android.generation2\u003C/groupid>\n \u003Cartifactid>android-maven-plugin\u003C/artifactid>\n \u003Cinherited>true\u003C/inherited>\n \u003Cconfiguration>\n \u003Ctest>\n \u003Cskip>false\u003C/skip>\n \u003C/test>\n \u003C/configuration>\n \u003C/plugin>\n \u003C/plugins>\n \u003C/build>\n \u003C/profile>\n\n",[471,49877,49878,49882,49886,49890,49895,49900,49905,49910,49915,49920,49925,49930,49935,49940,49945,49950,49955,49960],{"__ignoreMap":469},[474,49879,49880],{"class":476,"line":477},[474,49881,917],{"emptyLinePlaceholder":916},[474,49883,49884],{"class":476,"line":507},[474,49885,49859],{},[474,49887,49888],{"class":476,"line":547},[474,49889,49864],{},[474,49891,49892],{"class":476,"line":584},[474,49893,49894],{}," \u003Cbuild>\n",[474,49896,49897],{"class":476,"line":607},[474,49898,49899],{}," \u003Cplugins>\n",[474,49901,49902],{"class":476,"line":642},[474,49903,49904],{}," \u003Cplugin>\n",[474,49906,49907],{"class":476,"line":663},[474,49908,49909],{}," \u003Cgroupid>com.jayway.maven.plugins.android.generation2\u003C/groupid>\n",[474,49911,49912],{"class":476,"line":694},[474,49913,49914],{}," \u003Cartifactid>android-maven-plugin\u003C/artifactid>\n",[474,49916,49917],{"class":476,"line":700},[474,49918,49919],{}," \u003Cinherited>true\u003C/inherited>\n",[474,49921,49922],{"class":476,"line":913},[474,49923,49924],{}," \u003Cconfiguration>\n",[474,49926,49927],{"class":476,"line":920},[474,49928,49929],{}," \u003Ctest>\n",[474,49931,49932],{"class":476,"line":926},[474,49933,49934],{}," \u003Cskip>false\u003C/skip>\n",[474,49936,49937],{"class":476,"line":932},[474,49938,49939],{}," \u003C/test>\n",[474,49941,49942],{"class":476,"line":938},[474,49943,49944],{}," \u003C/configuration>\n",[474,49946,49947],{"class":476,"line":944},[474,49948,49949],{}," \u003C/plugin>\n",[474,49951,49952],{"class":476,"line":950},[474,49953,49954],{}," \u003C/plugins>\n",[474,49956,49957],{"class":476,"line":956},[474,49958,49959],{}," \u003C/build>\n",[474,49961,49962],{"class":476,"line":962},[474,49963,49869],{},[439,49965,49966],{},"To test it, run",[464,49968,49969],{"className":16895,"code":49624,"language":16897,"meta":469,"style":469},[471,49970,49971],{"__ignoreMap":469},[474,49972,49973],{"class":476,"line":477},[474,49974,49624],{},[439,49976,49977],{},"The Instrumentation Tests should not run. And then",[464,49979,49981],{"className":16895,"code":49980,"language":16897,"meta":469,"style":469},"mvn clean install -PIT\n",[471,49982,49983],{"__ignoreMap":469},[474,49984,49985],{"class":476,"line":477},[474,49986,49980],{},[439,49988,49989],{},"Now the tests should run.",[3938,49991,49993],{"id":49992},"dependencies-on-android-libraries","Dependencies on android libraries",[439,49995,49996],{},"The dependencies on the android libraries are some kind of problem, because not all of them are published in maven\ncentral (e.g. only up to Android 4.1.1.4 right now, and only the support-v4 library, not the -v7 one…)",[439,49998,49999,50000,50005],{},"To compensate this, there is the ",[1002,50001,50004],{"href":50002,"rel":50003},"https://github.com/mosabua/maven-android-sdk-deployer",[1006],"maven-android-sdk-deployer","\nproject that lets you deploy your local sdk components into a maven repository of your choice (defaults to your local\none).",[439,50007,50008],{},"For the tutorial on how to use it, please head over to their github page, everything is explained nicely detailed there.",[439,50010,50011],{},"Just some small pointers for this this setup: if you have a dependency on a apklib (except from the android framework\nitself), define the dependency in the parent project to define the version:",[464,50013,50015],{"className":6253,"code":50014,"language":6255,"meta":469,"style":469},"\n \u003Cdependency>\n \u003Cgroupid>android.support\u003C/groupid>\n \u003Cartifactid>compatibility-v4\u003C/artifactid>\n \u003Cversion>18\u003C/version>\n \u003C/dependency>\n",[471,50016,50017,50021,50026,50031,50036,50041],{"__ignoreMap":469},[474,50018,50019],{"class":476,"line":477},[474,50020,917],{"emptyLinePlaceholder":916},[474,50022,50023],{"class":476,"line":507},[474,50024,50025],{}," \u003Cdependency>\n",[474,50027,50028],{"class":476,"line":547},[474,50029,50030],{}," \u003Cgroupid>android.support\u003C/groupid>\n",[474,50032,50033],{"class":476,"line":584},[474,50034,50035],{}," \u003Cartifactid>compatibility-v4\u003C/artifactid>\n",[474,50037,50038],{"class":476,"line":607},[474,50039,50040],{}," \u003Cversion>18\u003C/version>\n",[474,50042,50043],{"class":476,"line":642},[474,50044,50045],{}," \u003C/dependency>\n",[439,50047,50048],{},"In the app,",[464,50050,50052],{"className":6253,"code":50051,"language":6255,"meta":469,"style":469},"\n \u003Cdependency>\n \u003Cgroupid>android.support\u003C/groupid>\n \u003Cartifactid>compatibility-v4\u003C/artifactid>\n \u003C/dependency>\n",[471,50053,50054,50058,50063,50068,50073],{"__ignoreMap":469},[474,50055,50056],{"class":476,"line":477},[474,50057,917],{"emptyLinePlaceholder":916},[474,50059,50060],{"class":476,"line":507},[474,50061,50062],{}," \u003Cdependency>\n",[474,50064,50065],{"class":476,"line":547},[474,50066,50067],{}," \u003Cgroupid>android.support\u003C/groupid>\n",[474,50069,50070],{"class":476,"line":584},[474,50071,50072],{}," \u003Cartifactid>compatibility-v4\u003C/artifactid>\n",[474,50074,50075],{"class":476,"line":607},[474,50076,50077],{}," \u003C/dependency>\n",[439,50079,50080],{},"And in the IT module, set it as provided!",[464,50082,50084],{"className":6253,"code":50083,"language":6255,"meta":469,"style":469},"\n \u003Cdependency>\n \u003Cgroupid>android.support\u003C/groupid>\n \u003Cartifactid>compatibility-v4\u003C/artifactid>\n \u003Cscope>provided\u003C/scope>\n \u003C/dependency>\n",[471,50085,50086,50090,50094,50098,50102,50107],{"__ignoreMap":469},[474,50087,50088],{"class":476,"line":477},[474,50089,917],{"emptyLinePlaceholder":916},[474,50091,50092],{"class":476,"line":507},[474,50093,50062],{},[474,50095,50096],{"class":476,"line":547},[474,50097,50067],{},[474,50099,50100],{"class":476,"line":584},[474,50101,50072],{},[474,50103,50104],{"class":476,"line":607},[474,50105,50106],{}," \u003Cscope>provided\u003C/scope>\n",[474,50108,50109],{"class":476,"line":642},[474,50110,50077],{},[439,50112,50113],{},"If you want to use newer versions than the preconfigured 4.1.1.4, you have to deploy them yourself and also use\nanother group id for android, simply “android” instead of “com.google.android”. For the exact versions, deploy them with\nthe maven-android-deployer and look into your repository, or simply look at the readme at the github page of the\ndeployer.",[439,50115,50116],{},"Here’s an example config of the dependencies deployed with the maven-android-deployer:",[464,50118,50120],{"className":6253,"code":50119,"language":6255,"meta":469,"style":469},"\n \u003Cproperties>\n \u003Cplatform.version>4.2.2_r2\u003C/platform.version>\n \u003Candroid.platform>17\u003C/android.platform>\n \u003C/properties>\n \u003Cdependencymanagement>\n \u003Cdependencies>\n \u003Cdependency>\n \u003Cgroupid>android\u003C/groupid>\n \u003Cartifactid>android\u003C/artifactid>\n \u003Cversion>${platform.version}\u003C/version>\n \u003Cscope>provided\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003Cgroupid>android.test.uiautomator\u003C/groupid>\n \u003Cartifactid>uiautomator\u003C/artifactid>\n \u003Cversion>${platform.version}\u003C/version>\n \u003Cscope>provided\u003C/scope>\n \u003C/dependency>\n \u003C/dependencies>\n \u003C/dependencymanagement>\n\n",[471,50121,50122,50126,50131,50136,50141,50146,50151,50156,50160,50165,50170,50175,50180,50184,50188,50193,50198,50202,50206,50210,50215],{"__ignoreMap":469},[474,50123,50124],{"class":476,"line":477},[474,50125,917],{"emptyLinePlaceholder":916},[474,50127,50128],{"class":476,"line":507},[474,50129,50130],{}," \u003Cproperties>\n",[474,50132,50133],{"class":476,"line":547},[474,50134,50135],{}," \u003Cplatform.version>4.2.2_r2\u003C/platform.version>\n",[474,50137,50138],{"class":476,"line":584},[474,50139,50140],{}," \u003Candroid.platform>17\u003C/android.platform>\n",[474,50142,50143],{"class":476,"line":607},[474,50144,50145],{}," \u003C/properties>\n",[474,50147,50148],{"class":476,"line":642},[474,50149,50150],{}," \u003Cdependencymanagement>\n",[474,50152,50153],{"class":476,"line":663},[474,50154,50155],{}," \u003Cdependencies>\n",[474,50157,50158],{"class":476,"line":694},[474,50159,50025],{},[474,50161,50162],{"class":476,"line":700},[474,50163,50164],{}," \u003Cgroupid>android\u003C/groupid>\n",[474,50166,50167],{"class":476,"line":913},[474,50168,50169],{}," \u003Cartifactid>android\u003C/artifactid>\n",[474,50171,50172],{"class":476,"line":920},[474,50173,50174],{}," \u003Cversion>${platform.version}\u003C/version>\n",[474,50176,50177],{"class":476,"line":926},[474,50178,50179],{}," \u003Cscope>provided\u003C/scope>\n",[474,50181,50182],{"class":476,"line":932},[474,50183,50045],{},[474,50185,50186],{"class":476,"line":938},[474,50187,50025],{},[474,50189,50190],{"class":476,"line":944},[474,50191,50192],{}," \u003Cgroupid>android.test.uiautomator\u003C/groupid>\n",[474,50194,50195],{"class":476,"line":950},[474,50196,50197],{}," \u003Cartifactid>uiautomator\u003C/artifactid>\n",[474,50199,50200],{"class":476,"line":956},[474,50201,50174],{},[474,50203,50204],{"class":476,"line":962},[474,50205,50179],{},[474,50207,50208],{"class":476,"line":4876},[474,50209,50045],{},[474,50211,50212],{"class":476,"line":4888},[474,50213,50214],{}," \u003C/dependencies>\n",[474,50216,50217],{"class":476,"line":4900},[474,50218,50219],{}," \u003C/dependencymanagement>\n",[439,50221,50222],{},"Don’t forget to update the modules dependencies accordingly!",[439,50224,50225],{},"As you can probably see, we also replaced the sdk platform version config with a parameter, in the android maven plugin\nconfig:",[464,50227,50229],{"className":6253,"code":50228,"language":6255,"meta":469,"style":469},"\n \u003Csdk>\n \u003Cplatform>${android.platform}\u003C/platform>\n \u003C/sdk>\n\n",[471,50230,50231,50235,50240,50245],{"__ignoreMap":469},[474,50232,50233],{"class":476,"line":477},[474,50234,917],{"emptyLinePlaceholder":916},[474,50236,50237],{"class":476,"line":507},[474,50238,50239],{}," \u003Csdk>\n",[474,50241,50242],{"class":476,"line":547},[474,50243,50244],{}," \u003Cplatform>${android.platform}\u003C/platform>\n",[474,50246,50247],{"class":476,"line":584},[474,50248,50249],{}," \u003C/sdk>\n",[439,50251,50252],{},"With this you should be able to develop Android (more or less comfortable) using maven and Android studio.",[439,50254,50255,50260],{},[1002,50256,50259],{"href":50257,"rel":50258},"http://blog.synyx.de/2013/09/building-android-projects-with-maven-part-2-releases-with-maven/",[1006],"In the next blog",", I’ll\nshow you how to configure the plugins to build releases of Android apps!",[439,50262,50263],{},"I’ll also post the sources for the project at the end of the next blogpost, so you can check against it, if you get some\nerrors.",[1024,50265,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":50267},[50268,50269,50273,50274,50275],{"id":49579,"depth":507,"text":49580},{"id":49671,"depth":507,"text":49672,"children":50270},[50271,50272],{"id":49681,"depth":547,"text":49682},{"id":49703,"depth":547,"text":49704},{"id":49737,"depth":507,"text":49738},{"id":49792,"depth":507,"text":49793},{"id":49992,"depth":507,"text":49993},[1030,11122,1413],"2013-09-13T14:57:30","Building and managing Android projects with maven is not as easy as it could be. So in this blog, I’ll show you how we\\nmanaged to get it work nicely.","https://synyx.de/blog/building-android-projects-with-maven-part-1-setup/",{},"/blog/building-android-projects-with-maven-part-1-setup",{"title":49564,"description":49573},"blog/building-android-projects-with-maven-part-1-setup",[11132,48772,50285,22988,49431,50286],"build","setup","Building and managing Android projects with maven is not as easy as it could be. So in this blog, I’ll show you how we managed to get it work nicely.…","UiE3YMoUCyJfKt7af3phI1JVhLV8Qa_ynuSw39WP0Dk",{"id":50290,"title":50291,"author":50292,"body":50293,"category":50687,"date":50688,"description":50689,"extension":1034,"link":50690,"meta":50691,"navigation":916,"path":50692,"seo":50693,"slug":50297,"stem":50694,"tags":50695,"teaser":50696,"__hash__":50697},"blog/blog/yammer-metrics-made-easy-part-ii.md","yammer – Metrics made easy – Part II",[15],{"type":432,"value":50294,"toc":50685},[50295,50298,50301,50304,50307,50310,50313,50316,50319,50322,50325,50328,50331,50334,50382,50385,50486,50489,50586,50589,50592,50595,50598,50601,50633,50636,50674,50677,50680,50683],[435,50296,50291],{"id":50297},"yammer-metrics-made-easy-part-ii",[439,50299,50300],{},"Last time i explained a little example using yammer metrics to inspect runtime metrics of a standalone client\napplication. This time we want to go a step further and instrument a web application and expose some metrics around\nrequest/response cycles.",[439,50302,50303],{},"Important Note:",[439,50305,50306],{},"For this example we need a jmx-enabled container like a Tomcat or a Glassfish. If you want to use Jetty or similar\ncontainers please provide a JMX-Registry within them. For example you can use JMinix which is quite easy (add\ndependency and add the JMinix servlet to your web.xml)",[439,50308,50309],{},"So, lets get started with yammer metrics for web apps. What provides metrics out of the box for your project:",[439,50311,50312],{},"* DefaultWebappMetricsFilter for request / response cycle metrics",[439,50314,50315],{},"* Little Administration Servlet with links for further inspections",[439,50317,50318],{},"* Ping Servlet for isAlive checks",[439,50320,50321],{},"* ThreadDump Servlet to inspect containers threads",[439,50323,50324],{},"* Metrics Servlet which exposes all your registered metrics as JSON",[439,50326,50327],{},"* HealthCheck Servlet which exposes all your registered HealthChecks as JSON",[439,50329,50330],{},"For todays blog we dig a bit deeper into enabling all these Servlets as well as exposing them to a JmxRegistry. In a\nlater article we will dig deeper into health checks and standard metrics.",[439,50332,50333],{},"Now, where to start? Add metrics-core, metrics-servlet and metrics-servlets (bad naming, isn’t it?) to your pom.\nAfter that we are able to add the DefaultWebappMetricsFilter to our web.xml",[464,50335,50337],{"className":6253,"code":50336,"language":6255,"meta":469,"style":469},"\n \u003Cfilter>\n \u003Cfilter-name>webappMetricsFilter\u003C/filter-name>\n \u003Cfilter-class>com.yammer.metrics.servlet.DefaultWebappMetricsFilter\u003C/filter-class>\n \u003C/filter>\n \u003Cfilter-mapping>\n \u003Cfilter-name>webappMetricsFilter\u003C/filter-name>\n \u003Curl-pattern>/*\u003C/url-pattern>\n \u003C/filter-mapping>\n\n",[471,50338,50339,50343,50348,50353,50358,50363,50368,50372,50377],{"__ignoreMap":469},[474,50340,50341],{"class":476,"line":477},[474,50342,917],{"emptyLinePlaceholder":916},[474,50344,50345],{"class":476,"line":507},[474,50346,50347],{}," \u003Cfilter>\n",[474,50349,50350],{"class":476,"line":547},[474,50351,50352],{}," \u003Cfilter-name>webappMetricsFilter\u003C/filter-name>\n",[474,50354,50355],{"class":476,"line":584},[474,50356,50357],{}," \u003Cfilter-class>com.yammer.metrics.servlet.DefaultWebappMetricsFilter\u003C/filter-class>\n",[474,50359,50360],{"class":476,"line":607},[474,50361,50362],{}," \u003C/filter>\n",[474,50364,50365],{"class":476,"line":642},[474,50366,50367],{}," \u003Cfilter-mapping>\n",[474,50369,50370],{"class":476,"line":663},[474,50371,50352],{},[474,50373,50374],{"class":476,"line":694},[474,50375,50376],{}," \u003Curl-pattern>/*\u003C/url-pattern>\n",[474,50378,50379],{"class":476,"line":700},[474,50380,50381],{}," \u003C/filter-mapping>\n",[439,50383,50384],{},"Then we need to add all within the distribution shipped helper-servlets",[464,50386,50388],{"className":6253,"code":50387,"language":6255,"meta":469,"style":469},"\n \u003Cservlet>\n \u003Cservlet-name>MetricsAdmin\u003C/servlet-name>\n \u003Cservlet-class>com.yammer.metrics.servlets.AdminServlet\u003C/servlet-class>\n \u003C/servlet>\n \u003Cservlet>\n \u003Cservlet-name>MetricsPing\u003C/servlet-name>\n \u003Cservlet-class>com.yammer.metrics.servlets.PingServlet\u003C/servlet-class>\n \u003C/servlet>\n \u003Cservlet>\n \u003Cservlet-name>MetricsThread\u003C/servlet-name>\n \u003Cservlet-class>com.yammer.metrics.servlets.ThreadDumpServlet\u003C/servlet-class>\n \u003C/servlet>\n \u003Cservlet>\n \u003Cservlet-name>Metrics\u003C/servlet-name>\n \u003Cservlet-class>com.yammer.metrics.servlets.MetricsServlet\u003C/servlet-class>\n \u003C/servlet>\n \u003Cservlet>\n \u003Cservlet-name>MetricsHealthCheck\u003C/servlet-name>\n \u003Cservlet-class>com.yammer.metrics.servlets.HealthCheckServlet\u003C/servlet-class>\n \u003C/servlet>\n\n",[471,50389,50390,50394,50399,50404,50409,50414,50418,50423,50428,50432,50436,50441,50446,50450,50454,50459,50464,50468,50472,50477,50482],{"__ignoreMap":469},[474,50391,50392],{"class":476,"line":477},[474,50393,917],{"emptyLinePlaceholder":916},[474,50395,50396],{"class":476,"line":507},[474,50397,50398],{}," \u003Cservlet>\n",[474,50400,50401],{"class":476,"line":547},[474,50402,50403],{}," \u003Cservlet-name>MetricsAdmin\u003C/servlet-name>\n",[474,50405,50406],{"class":476,"line":584},[474,50407,50408],{}," \u003Cservlet-class>com.yammer.metrics.servlets.AdminServlet\u003C/servlet-class>\n",[474,50410,50411],{"class":476,"line":607},[474,50412,50413],{}," \u003C/servlet>\n",[474,50415,50416],{"class":476,"line":642},[474,50417,50398],{},[474,50419,50420],{"class":476,"line":663},[474,50421,50422],{}," \u003Cservlet-name>MetricsPing\u003C/servlet-name>\n",[474,50424,50425],{"class":476,"line":694},[474,50426,50427],{}," \u003Cservlet-class>com.yammer.metrics.servlets.PingServlet\u003C/servlet-class>\n",[474,50429,50430],{"class":476,"line":700},[474,50431,50413],{},[474,50433,50434],{"class":476,"line":913},[474,50435,50398],{},[474,50437,50438],{"class":476,"line":920},[474,50439,50440],{}," \u003Cservlet-name>MetricsThread\u003C/servlet-name>\n",[474,50442,50443],{"class":476,"line":926},[474,50444,50445],{}," \u003Cservlet-class>com.yammer.metrics.servlets.ThreadDumpServlet\u003C/servlet-class>\n",[474,50447,50448],{"class":476,"line":932},[474,50449,50413],{},[474,50451,50452],{"class":476,"line":938},[474,50453,50398],{},[474,50455,50456],{"class":476,"line":944},[474,50457,50458],{}," \u003Cservlet-name>Metrics\u003C/servlet-name>\n",[474,50460,50461],{"class":476,"line":950},[474,50462,50463],{}," \u003Cservlet-class>com.yammer.metrics.servlets.MetricsServlet\u003C/servlet-class>\n",[474,50465,50466],{"class":476,"line":956},[474,50467,50413],{},[474,50469,50470],{"class":476,"line":962},[474,50471,50398],{},[474,50473,50474],{"class":476,"line":4876},[474,50475,50476],{}," \u003Cservlet-name>MetricsHealthCheck\u003C/servlet-name>\n",[474,50478,50479],{"class":476,"line":4888},[474,50480,50481],{}," \u003Cservlet-class>com.yammer.metrics.servlets.HealthCheckServlet\u003C/servlet-class>\n",[474,50483,50484],{"class":476,"line":4900},[474,50485,50413],{},[439,50487,50488],{},"and then expose them to an url:",[464,50490,50492],{"className":6253,"code":50491,"language":6255,"meta":469,"style":469},"\n \u003Cservlet-mapping>\n \u003Cservlet-name>MetricsAdmin\u003C/servlet-name>\n \u003Curl-pattern>/admin\u003C/url-pattern>\n \u003C/servlet-mapping>\n \u003Cservlet-mapping>\n \u003Cservlet-name>MetricsPing\u003C/servlet-name>\n \u003Curl-pattern>/admin/ping\u003C/url-pattern>\n \u003C/servlet-mapping>\n \u003Cservlet-mapping>\n \u003Cservlet-name>MetricsThread\u003C/servlet-name>\n \u003Curl-pattern>/admin/threads\u003C/url-pattern>\n \u003C/servlet-mapping>\n \u003Cservlet-mapping>\n \u003Cservlet-name>Metrics\u003C/servlet-name>\n \u003Curl-pattern>/admin/metrics\u003C/url-pattern>\n \u003C/servlet-mapping>\n \u003Cservlet-mapping>\n \u003Cservlet-name>MetricsHealthCheck\u003C/servlet-name>\n \u003Curl-pattern>/admin/healthcheck\u003C/url-pattern>\n \u003C/servlet-mapping>\n\n",[471,50493,50494,50498,50503,50507,50512,50517,50522,50526,50531,50535,50539,50543,50548,50552,50556,50560,50565,50569,50573,50577,50582],{"__ignoreMap":469},[474,50495,50496],{"class":476,"line":477},[474,50497,917],{"emptyLinePlaceholder":916},[474,50499,50500],{"class":476,"line":507},[474,50501,50502],{}," \u003Cservlet-mapping>\n",[474,50504,50505],{"class":476,"line":547},[474,50506,50403],{},[474,50508,50509],{"class":476,"line":584},[474,50510,50511],{}," \u003Curl-pattern>/admin\u003C/url-pattern>\n",[474,50513,50514],{"class":476,"line":607},[474,50515,50516],{}," \u003C/servlet-mapping>\n",[474,50518,50519],{"class":476,"line":642},[474,50520,50521],{}," \u003Cservlet-mapping>\n",[474,50523,50524],{"class":476,"line":663},[474,50525,50422],{},[474,50527,50528],{"class":476,"line":694},[474,50529,50530],{}," \u003Curl-pattern>/admin/ping\u003C/url-pattern>\n",[474,50532,50533],{"class":476,"line":700},[474,50534,50516],{},[474,50536,50537],{"class":476,"line":913},[474,50538,50521],{},[474,50540,50541],{"class":476,"line":920},[474,50542,50440],{},[474,50544,50545],{"class":476,"line":926},[474,50546,50547],{}," \u003Curl-pattern>/admin/threads\u003C/url-pattern>\n",[474,50549,50550],{"class":476,"line":932},[474,50551,50516],{},[474,50553,50554],{"class":476,"line":938},[474,50555,50521],{},[474,50557,50558],{"class":476,"line":944},[474,50559,50458],{},[474,50561,50562],{"class":476,"line":950},[474,50563,50564],{}," \u003Curl-pattern>/admin/metrics\u003C/url-pattern>\n",[474,50566,50567],{"class":476,"line":956},[474,50568,50516],{},[474,50570,50571],{"class":476,"line":962},[474,50572,50521],{},[474,50574,50575],{"class":476,"line":4876},[474,50576,50476],{},[474,50578,50579],{"class":476,"line":4888},[474,50580,50581],{}," \u003Curl-pattern>/admin/healthcheck\u003C/url-pattern>\n",[474,50583,50584],{"class":476,"line":4900},[474,50585,50516],{},[439,50587,50588],{},"Finished?",[439,50590,50591],{},"Not yet .. nothing will work for now. yammer Metrics need some “bootstrapping” which cannot be done too easy with DI\nFrameworks like Spring. It’s a little hazzle /o\\",[439,50593,50594],{},"For our little example it is enough to enable the required bootstrapping with a ServletContextListener and it’s provided\nmethods contextInitialized and contextDestroyed.",[439,50596,50597],{},"Why do we need a programmatic bootstrapping? Because metrics searches for it’s MetricRegistries in servletcontext. It is\npossible doing this by hand but this won’t work with the current 3.0.0-beta1 version so i decided using this way.\nFurthermore it seems the easiest way enabling all the needed stuff and registries.",[439,50599,50600],{},"An apropriate contextInitialized method could look like this:",[464,50602,50604],{"className":709,"code":50603,"language":711,"meta":469,"style":469},"\n @Override\n public void contextInitialized(ServletContextEvent sce) {\n createAndBuildRegistries(sce);\n registerHealthChecks();\n }\n",[471,50605,50606,50610,50614,50619,50624,50629],{"__ignoreMap":469},[474,50607,50608],{"class":476,"line":477},[474,50609,917],{"emptyLinePlaceholder":916},[474,50611,50612],{"class":476,"line":507},[474,50613,21043],{},[474,50615,50616],{"class":476,"line":547},[474,50617,50618],{}," public void contextInitialized(ServletContextEvent sce) {\n",[474,50620,50621],{"class":476,"line":584},[474,50622,50623],{}," createAndBuildRegistries(sce);\n",[474,50625,50626],{"class":476,"line":607},[474,50627,50628],{}," registerHealthChecks();\n",[474,50630,50631],{"class":476,"line":642},[474,50632,1276],{},[439,50634,50635],{},"The corresponding createAndBuildRegistries Method could look like this:",[464,50637,50639],{"className":709,"code":50638,"language":711,"meta":469,"style":469},"\n private void createAndBuildRegistries(ServletContextEvent sce) {\n metricsForWebapp = new MetricRegistry(\"DemonstrationWebapp\");\n jmxForWebapp = JmxReporter.forRegistry(metricsForWebapp).build();\n sce.getServletContext().setAttribute(DefaultWebappMetricsFilter.class.getName() + \".registry\", metricsForWebapp);\n jmxForWebapp.start();\n }\n",[471,50640,50641,50645,50650,50655,50660,50665,50670],{"__ignoreMap":469},[474,50642,50643],{"class":476,"line":477},[474,50644,917],{"emptyLinePlaceholder":916},[474,50646,50647],{"class":476,"line":507},[474,50648,50649],{}," private void createAndBuildRegistries(ServletContextEvent sce) {\n",[474,50651,50652],{"class":476,"line":547},[474,50653,50654],{}," metricsForWebapp = new MetricRegistry(\"DemonstrationWebapp\");\n",[474,50656,50657],{"class":476,"line":584},[474,50658,50659],{}," jmxForWebapp = JmxReporter.forRegistry(metricsForWebapp).build();\n",[474,50661,50662],{"class":476,"line":607},[474,50663,50664],{}," sce.getServletContext().setAttribute(DefaultWebappMetricsFilter.class.getName() + \".registry\", metricsForWebapp);\n",[474,50666,50667],{"class":476,"line":642},[474,50668,50669],{}," jmxForWebapp.start();\n",[474,50671,50672],{"class":476,"line":663},[474,50673,1276],{},[439,50675,50676],{},"and the method registerHealthChecks is used to register all developed HealthChecks to the appropriate Registry we want\nto expose them. Currently we implemented no HealthChecks so the HealthCheckRegistry should just expose nothing to JMX\nand send back a 400 via REST.",[439,50678,50679],{},"So that’s it for today. We showed you how easy it is to enhance a web-application with runtime metrics for it’s\nrequest/response cycles. In the upcoming port we will show you hw to implement some HealthChecks as well as some\nmeasurements around business-interactions.",[439,50681,50682],{},"So stay tuned …",[1024,50684,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":50686},[],[1031],"2013-09-09T14:09:47","Last time i explained a little example using yammer metrics to inspect runtime metrics of a standalone client\\napplication. This time we want to go a step further and instrument a web application and expose some metrics around\\nrequest/response cycles.","https://synyx.de/blog/yammer-metrics-made-easy-part-ii/",{},"/blog/yammer-metrics-made-easy-part-ii",{"title":50291,"description":50300},"blog/yammer-metrics-made-easy-part-ii",[],"Last time i explained a little example using yammer metrics to inspect runtime metrics of a standalone client application. This time we want to go a step further and instrument…","rHywEJRutLZjs0z2ZFb6rrYa7moeVd_9TwQVULc_JXg",{"id":50699,"title":50700,"author":50701,"body":50702,"category":51143,"date":51144,"description":51145,"extension":1034,"link":51146,"meta":51147,"navigation":916,"path":51148,"seo":51149,"slug":50706,"stem":51150,"tags":51151,"teaser":51155,"__hash__":51156},"blog/blog/yammer-metrics-made-easy-part-i.md","yammer – Metrics made easy – Part I",[15],{"type":432,"value":50703,"toc":51141},[50704,50707,50710,50713,50745,50748,50762,50765,50784,50787,50790,50793,50796,50813,50816,50823,50826,50829,50832,50835,50838,50841,50844,50847,50850,50853,50856,50859,50862,50865,50868,50871,50915,50918,51120,51123,51133,51136,51139],[435,50705,50700],{"id":50706},"yammer-metrics-made-easy-part-i",[439,50708,50709],{},"Metrics by yammer provides runtime metrics and statistics for all kind of apps you can imagine. A lot of stuff is\ndirectly useable out of the box, for example measuring request/response cycles of webapps and provide histograms of the\nmeasured values. So, lets try enabling a simple Java-Application built by maven.",[439,50711,50712],{},"First we add needed dependencies into our pom:",[464,50714,50716],{"className":6253,"code":50715,"language":6255,"meta":469,"style":469},"\n \u003Cdependency>\n \u003Cgroupid>com.yammer.metrics\u003C/groupid>\n \u003Cartifactid>metrics-core\u003C/artifactid>\n \u003Cversion>3.0.0-BETA1\u003C/version>\n \u003C/dependency>\n\n",[471,50717,50718,50722,50726,50731,50736,50741],{"__ignoreMap":469},[474,50719,50720],{"class":476,"line":477},[474,50721,917],{"emptyLinePlaceholder":916},[474,50723,50724],{"class":476,"line":507},[474,50725,50062],{},[474,50727,50728],{"class":476,"line":547},[474,50729,50730],{}," \u003Cgroupid>com.yammer.metrics\u003C/groupid>\n",[474,50732,50733],{"class":476,"line":584},[474,50734,50735],{}," \u003Cartifactid>metrics-core\u003C/artifactid>\n",[474,50737,50738],{"class":476,"line":607},[474,50739,50740],{}," \u003Cversion>3.0.0-BETA1\u003C/version>\n",[474,50742,50743],{"class":476,"line":642},[474,50744,50077],{},[439,50746,50747],{},"After providing this, we are able to do something like that in our code:",[464,50749,50751],{"className":709,"code":50750,"language":711,"meta":469,"style":469},"\nstatic final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n\n",[471,50752,50753,50757],{"__ignoreMap":469},[474,50754,50755],{"class":476,"line":477},[474,50756,917],{"emptyLinePlaceholder":916},[474,50758,50759],{"class":476,"line":507},[474,50760,50761],{},"static final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n",[439,50763,50764],{},"The MetricRegistry is not more and not less than a structural component for a couple of Metrics in you Application.\nLet’s imagine you’ve developed an application for remote number crunching, then it would be a good idea creating 2\nMetricRegistry Instances like this:",[464,50766,50768],{"className":709,"code":50767,"language":711,"meta":469,"style":469},"\nstatic final MetricRegistry crunchMetrics = new MetricRegistry(\"CrunchMeasurement\");\nstatic final MetricRegistry requestMetrics = new MetricRegistry(\"RequestMeasurement\");\n\n",[471,50769,50770,50774,50779],{"__ignoreMap":469},[474,50771,50772],{"class":476,"line":477},[474,50773,917],{"emptyLinePlaceholder":916},[474,50775,50776],{"class":476,"line":507},[474,50777,50778],{},"static final MetricRegistry crunchMetrics = new MetricRegistry(\"CrunchMeasurement\");\n",[474,50780,50781],{"class":476,"line":547},[474,50782,50783],{},"static final MetricRegistry requestMetrics = new MetricRegistry(\"RequestMeasurement\");\n",[439,50785,50786],{},"You would use one of them for all measurement of the crunching component itself and the other for the little server you\nincluded to access your numbercruncher (possibly to measure request/response cycles too).",[439,50788,50789],{},"First step is done. We are now able to add some Metrics to a registry which is needed to expose them. But wait … what we\nshould expose now?",[439,50791,50792],{},"Which possibilities do we have with metrics?",[439,50794,50795],{},"Well, there are 5 types of measurements included",[994,50797,50798,50801,50804,50807,50810],{},[997,50799,50800],{},"Gauge (instantaneous measurement of one value)",[997,50802,50803],{},"Histogram (measurement of value variants)",[997,50805,50806],{},"Timer (measurement of timings)",[997,50808,50809],{},"Counter (measurement of atomic longs)",[997,50811,50812],{},"Meter (measurement of ticks in a time range)",[439,50814,50815],{},"as well as some typically needed ones for special purposes like the",[439,50817,50818],{},[448,50819,50820],{},[990,50821,50822],{},"com.yammer.metrics.servlet.DefaultWebappMetricsFilter (we will discuss this later in the blog)",[439,50824,50825],{},"So for our example we should take two types of measurements: a Timer for measurement of request/response cycles and a\nsecond timer for measurement of the number crunching calculation.",[439,50827,50828],{},"Next step is to expose the measured values to a format you can use it. Metrics provides a lot of default exposements\nlike:",[439,50830,50831],{},"* JMX",[439,50833,50834],{},"* JSON",[439,50836,50837],{},"* CSV",[439,50839,50840],{},"* log4j / slf4j",[439,50842,50843],{},"* logback",[439,50845,50846],{},"* ganglia",[439,50848,50849],{},"* graphite",[439,50851,50852],{},"* console",[439,50854,50855],{},"Of course you are able to create your own Reporters ’cause its open source software 🙂 For our example it’s enough using\none of the bundled reporters, e.G. the ConsoleReporter.",[439,50857,50858],{},"So, at a glance we need to do the following steps to enable a Java-Application with Metrics:",[439,50860,50861],{},"1.) Create and instantiate a MetricRegistry (i highly encourage you to inject them into your productive code!)",[439,50863,50864],{},"2.) Create Measurements to your needs (in our example the mentioned 2 timers)",[439,50866,50867],{},"3.) Create and instantiate a Reporter to your needs ( i highly encourage you to inject them into your productive code\n🙂",[439,50869,50870],{},"Let’s show this with a very straightforward coded application",[464,50872,50874],{"className":709,"code":50873,"language":711,"meta":469,"style":469},"\n final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n evictions = metrics.counter(MetricRegistry.name(HealthCheckDemo.class, \"cache-evictions\"));\n request = metrics.timer(MetricRegistry.name(ArithmeticDemoOperation.class, \"calculation-duration\"));\n reporter = ConsoleReporter.forRegistry(metrics).build();\n jmxReporter = JmxReporter.forRegistry(metrics).build();\n reporter.start(1, TimeUnit.MINUTES); // should expose values every minute\n jmxReporter.start();\n\n",[471,50875,50876,50880,50885,50890,50895,50900,50905,50910],{"__ignoreMap":469},[474,50877,50878],{"class":476,"line":477},[474,50879,917],{"emptyLinePlaceholder":916},[474,50881,50882],{"class":476,"line":507},[474,50883,50884],{}," final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n",[474,50886,50887],{"class":476,"line":547},[474,50888,50889],{}," evictions = metrics.counter(MetricRegistry.name(HealthCheckDemo.class, \"cache-evictions\"));\n",[474,50891,50892],{"class":476,"line":584},[474,50893,50894],{}," request = metrics.timer(MetricRegistry.name(ArithmeticDemoOperation.class, \"calculation-duration\"));\n",[474,50896,50897],{"class":476,"line":607},[474,50898,50899],{}," reporter = ConsoleReporter.forRegistry(metrics).build();\n",[474,50901,50902],{"class":476,"line":642},[474,50903,50904],{}," jmxReporter = JmxReporter.forRegistry(metrics).build();\n",[474,50906,50907],{"class":476,"line":663},[474,50908,50909],{}," reporter.start(1, TimeUnit.MINUTES); // should expose values every minute\n",[474,50911,50912],{"class":476,"line":694},[474,50913,50914],{}," jmxReporter.start();\n",[439,50916,50917],{},"After running this application you should see a console output like this:",[464,50919,50921],{"className":16895,"code":50920,"language":16897,"meta":469,"style":469},"\n05.05.13 08:22:03 ==============================================================\n-- Counters --------------------------------------------------------------------\norg.synyx.demos.HealthCheckDemo.cache-evictions\ncount = 1\n-- Timers ----------------------------------------------------------------------\norg.synyx.demos.ArithmeticDemoOperation.calculation-duration\ncount = 1\nmean rate = 0,02 calls/second\n1-minute rate = 0,09 calls/second\n5-minute rate = 0,17 calls/second\n15-minute rate = 0,19 calls/second\nmin = 1250,28 milliseconds\nmax = 1250,28 milliseconds\nmean = 1250,28 milliseconds\nstddev = 0,00 milliseconds\nmedian = 1250,28 milliseconds\n75% \u003C= 1250,28 milliseconds\n95% \u003C= 1250,28 milliseconds\n98% \u003C= 1250,28 milliseconds\n99% \u003C= 1250,28 milliseconds\n99.9% \u003C= 1250,28 milliseconds\n05.05.13 08:23:03 ==============================================================\n-- Counters --------------------------------------------------------------------\norg.synyx.demos.HealthCheckDemo.cache-evictions\ncount = 1\n-- Timers ----------------------------------------------------------------------\norg.synyx.demos.ArithmeticDemoOperation.calculation-duration\ncount = 1\nmean rate = 0,01 calls/second\n1-minute rate = 0,03 calls/second\n5-minute rate = 0,14 calls/second\n15-minute rate = 0,18 calls/second\nmin = 1250,28 milliseconds\nmax = 1250,28 milliseconds\nmean = 1250,28 milliseconds\nstddev = 0,00 milliseconds\nmedian = 1250,28 milliseconds\n75% \u003C= 1250,28 milliseconds\n95% \u003C= 1250,28 milliseconds\n98% \u003C= 1250,28 milliseconds\n99% \u003C= 1250,28 milliseconds\n99.9% \u003C= 1250,28 milliseconds\n\n",[471,50922,50923,50927,50932,50937,50942,50947,50952,50957,50961,50966,50971,50976,50981,50986,50991,50996,51001,51006,51011,51016,51021,51026,51031,51036,51040,51044,51048,51052,51056,51060,51065,51070,51075,51080,51084,51088,51092,51096,51100,51104,51108,51112,51116],{"__ignoreMap":469},[474,50924,50925],{"class":476,"line":477},[474,50926,917],{"emptyLinePlaceholder":916},[474,50928,50929],{"class":476,"line":507},[474,50930,50931],{},"05.05.13 08:22:03 ==============================================================\n",[474,50933,50934],{"class":476,"line":547},[474,50935,50936],{},"-- Counters --------------------------------------------------------------------\n",[474,50938,50939],{"class":476,"line":584},[474,50940,50941],{},"org.synyx.demos.HealthCheckDemo.cache-evictions\n",[474,50943,50944],{"class":476,"line":607},[474,50945,50946],{},"count = 1\n",[474,50948,50949],{"class":476,"line":642},[474,50950,50951],{},"-- Timers ----------------------------------------------------------------------\n",[474,50953,50954],{"class":476,"line":663},[474,50955,50956],{},"org.synyx.demos.ArithmeticDemoOperation.calculation-duration\n",[474,50958,50959],{"class":476,"line":694},[474,50960,50946],{},[474,50962,50963],{"class":476,"line":700},[474,50964,50965],{},"mean rate = 0,02 calls/second\n",[474,50967,50968],{"class":476,"line":913},[474,50969,50970],{},"1-minute rate = 0,09 calls/second\n",[474,50972,50973],{"class":476,"line":920},[474,50974,50975],{},"5-minute rate = 0,17 calls/second\n",[474,50977,50978],{"class":476,"line":926},[474,50979,50980],{},"15-minute rate = 0,19 calls/second\n",[474,50982,50983],{"class":476,"line":932},[474,50984,50985],{},"min = 1250,28 milliseconds\n",[474,50987,50988],{"class":476,"line":938},[474,50989,50990],{},"max = 1250,28 milliseconds\n",[474,50992,50993],{"class":476,"line":944},[474,50994,50995],{},"mean = 1250,28 milliseconds\n",[474,50997,50998],{"class":476,"line":950},[474,50999,51000],{},"stddev = 0,00 milliseconds\n",[474,51002,51003],{"class":476,"line":956},[474,51004,51005],{},"median = 1250,28 milliseconds\n",[474,51007,51008],{"class":476,"line":962},[474,51009,51010],{},"75% \u003C= 1250,28 milliseconds\n",[474,51012,51013],{"class":476,"line":4876},[474,51014,51015],{},"95% \u003C= 1250,28 milliseconds\n",[474,51017,51018],{"class":476,"line":4888},[474,51019,51020],{},"98% \u003C= 1250,28 milliseconds\n",[474,51022,51023],{"class":476,"line":4900},[474,51024,51025],{},"99% \u003C= 1250,28 milliseconds\n",[474,51027,51028],{"class":476,"line":4913},[474,51029,51030],{},"99.9% \u003C= 1250,28 milliseconds\n",[474,51032,51033],{"class":476,"line":4921},[474,51034,51035],{},"05.05.13 08:23:03 ==============================================================\n",[474,51037,51038],{"class":476,"line":4932},[474,51039,50936],{},[474,51041,51042],{"class":476,"line":4938},[474,51043,50941],{},[474,51045,51046],{"class":476,"line":4946},[474,51047,50946],{},[474,51049,51050],{"class":476,"line":4952},[474,51051,50951],{},[474,51053,51054],{"class":476,"line":4957},[474,51055,50956],{},[474,51057,51058],{"class":476,"line":4969},[474,51059,50946],{},[474,51061,51062],{"class":476,"line":4990},[474,51063,51064],{},"mean rate = 0,01 calls/second\n",[474,51066,51067],{"class":476,"line":5001},[474,51068,51069],{},"1-minute rate = 0,03 calls/second\n",[474,51071,51072],{"class":476,"line":5013},[474,51073,51074],{},"5-minute rate = 0,14 calls/second\n",[474,51076,51077],{"class":476,"line":5024},[474,51078,51079],{},"15-minute rate = 0,18 calls/second\n",[474,51081,51082],{"class":476,"line":5035},[474,51083,50985],{},[474,51085,51086],{"class":476,"line":5047},[474,51087,50990],{},[474,51089,51090],{"class":476,"line":5055},[474,51091,50995],{},[474,51093,51094],{"class":476,"line":5062},[474,51095,51000],{},[474,51097,51098],{"class":476,"line":5067},[474,51099,51005],{},[474,51101,51102],{"class":476,"line":5072},[474,51103,51010],{},[474,51105,51106],{"class":476,"line":5084},[474,51107,51015],{},[474,51109,51110],{"class":476,"line":5100},[474,51111,51020],{},[474,51113,51114],{"class":476,"line":5111},[474,51115,51025],{},[474,51117,51118],{"class":476,"line":5122},[474,51119,51030],{},[439,51121,51122],{},"Furthermore, if you debug the demo application you are able to inspect our exposements via a jmx-client like jVisualVM\nof jConsole after connecting.",[439,51124,51125],{},[1002,51126,51129],{"href":51127,"rel":51128},"https://media.synyx.de/uploads//2013/06/Bildschirmfoto-2013-09-02-um-12.08.02.png",[1006],[2205,51130],{"alt":51131,"src":51132},"Bildschirmfoto 2013-09-02 um 12.08.02","https://media.synyx.de/uploads//2013/06/Bildschirmfoto-2013-09-02-um-12.08.02-300x250.png",[439,51134,51135],{},"SUCCESS!! \\o/ As you can see you are able to expose the same values to different reporters if you want to. Isn’t that\nnice? Yes it is!",[439,51137,51138],{},"Next time we will enable a java-webapplication with some measurements, so stay tuned!",[1024,51140,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":51142},[],[1030,1412],"2013-09-02T11:50:58","Metrics by yammer provides runtime metrics and statistics for all kind of apps you can imagine. A lot of stuff is\\ndirectly useable out of the box, for example measuring request/response cycles of webapps and provide histograms of the\\nmeasured values. So, lets try enabling a simple Java-Application built by maven.","https://synyx.de/blog/yammer-metrics-made-easy-part-i/",{},"/blog/yammer-metrics-made-easy-part-i",{"title":50700,"description":50709},"blog/yammer-metrics-made-easy-part-i",[711,51152,45218,51153,37372,35140,51154],"javaee","measurement","tomcat","Metrics by yammer provides runtime metrics and statistics for all kind of apps you can imagine. A lot of stuff is directly useable out of the box, for example measuring…","l7bmMSJcqQbjJtpxQuROOnmXiVhqqittUDSePSHo02c",{"id":51158,"title":51159,"author":51160,"body":51161,"category":51438,"date":51439,"description":51170,"extension":1034,"link":17565,"meta":51440,"navigation":916,"path":51441,"seo":51442,"slug":51165,"stem":51443,"tags":51444,"teaser":51449,"__hash__":51450},"blog/blog/devoooops-azubitausch-bei-synyx.md","DevOooops – Azubitausch bei synyx",[30],{"type":432,"value":51162,"toc":51436},[51163,51166,51171,51174,51184,51189,51199,51209,51214,51217,51222,51230,51235,51244,51249,51252,51257,51270,51273,51278,51288,51293,51296,51299,51302,51307,51310,51315,51342,51347,51357,51362,51365,51370,51373,51378,51381,51386,51395,51400,51407,51413,51416,51421,51424],[435,51164,51159],{"id":51165},"devoooops-azubitausch-bei-synyx",[439,51167,51168],{},[448,51169,51170],{},"03-04-2013",[439,51172,51173],{},"Es ist kein verspäteter Aprilscherz. Um über den eigenen Tellerrand zu schauen, verlasse ich die gewohnte Java Welt und\nbreche meine Zelte im Individualsoftware Team ab. Der DevOp-Azubitausch beginnt. Während ich mich unter lauter bärtigen\nMenschen in der Adminstube heimelig einrichte, darf sich unser Systemintegrator-Azubi Matze, dessen Arbeitsplatz ich\neingenommen habe, im vorderen Entwicklerzimmer breit machen.",[439,51175,51176,51177,51180,51181,15931],{},"Dank ",[471,51178,51179],{},"$HOME/.ssh/authorized_keys"," habe ich plötzlich die Macht, auf allen Rechnern Unsinn zu treiben, wenn mir der Sinn\ndanach steht. (muahahaha) Aber da aus großer Macht große Verantwortung folgt, beschränke ich mich weiterhin darauf, mit\ngrößter Mühe nur meinen eigenen Rechner kaputt zu machen. [",[990,51182,51183],{},"Anmerkung der Redaktion: man beachte die Einträge vom\n29-04-2013 und 17-06-2013",[439,51185,51186],{},[448,51187,51188],{},"10-04-2013",[439,51190,51191,51192,51198],{},"Mein erstes richtiges Shell-Skript ist fertig und der\ndazugehörige ",[1002,51193,51197],{"href":51194,"rel":51195,"title":51196},"http://blog.synyx.de/2013/04/continuous-deployment-automatic-backup-script/",[1006],"Automatisiertes Backup Skript","Blogpost","\nauch. Das bisher manuell ablaufende Backup vor Deployments ist automatisiert, yeah.",[439,51200,51201,51202,51208],{},"Damit dieses Skript auf den Servern genauso wie das bereits vorhandene Continuous Deplyoment Skript automatisch\nausgerollt wird, bekomme ich einen ersten Einblick in die Arbeit mit ",[1002,51203,51207],{"href":51204,"rel":51205,"title":51206},"https://puppetlabs.com/",[1006],"puppetlabs","Puppet",". Das\nsieht ja ganz nett aus. Mir gefällt der “declare how the world should be” Ansatz.",[439,51210,51211],{},[448,51212,51213],{},"15-04-2013",[439,51215,51216],{},"Verschdl bekommt einen kleinen Tobsuchtsanfall wegen Logging Dateien mit fehlenden Datum-/Zeitangaben und mangelnder\nLog Rotation Einstellung. Ich nehme mir vor, auf solche Dinge künftig penibel zu achten….",[439,51218,51219],{},[448,51220,51221],{},"16-04-2013",[439,51223,51224,51225,51229],{},"Ich möchte, dass\ndie ",[1002,51226,51228],{"href":41811,"rel":51227,"title":51228},[1006],"Urlaubsverwaltung"," von\naußen erreichbar ist. Prompt bekomme ich Schlagwörter wie DNS, Ports, Reverse Proxy, Vhosts, Rewrite Rules um die Ohren\ngehauen. Ich male ein chaotisches Bildchen mit vielen Pfeilen, während Zivi mir erklärt, wie das denn alles so\nzusammenspielt. Am Ende bilde ich mir ein, es tatsächlich halbwegs verstanden zu haben 🙂 und Urlaub lässt sich nun auch\nvon zuhause aus beantragen.",[439,51231,51232],{},[448,51233,51234],{},"17-04-2013",[439,51236,51237,51238,51243],{},"Ich versuche herumfliegende Kabel im ",[1002,51239,51242],{"href":51240,"rel":51241,"title":51240},"http://partkeepr.org/",[1006],"Partkeepr"," zu inventarisieren. Die\nBetonung liegt bei “versuchen”… (“Was ist das denn?! Hab ich ja noch nie gesehen…”)",[439,51245,51246],{},[448,51247,51248],{},"22-04-2013",[439,51250,51251],{},"Verschdl gibt seit Wochen wirre Dinge von sich. Irgendwas mit “AWS” und “Cloud”. Dann schaue ich mir das halt mal an…",[439,51253,51254],{},[448,51255,51256],{},"23-04-2013",[439,51258,51259,51260,18762,51265],{},"Matze und ich haben die ehrenwerte Aufgabe, eine Debian VM zu installieren und dort einen neuen Puppetmaster\naufzusetzen. Eine Umstrukturierung ist im Gange. Puppet Module sollen künftig in eigenen Git Repos versioniert werden,\nstatt in einem einzigen großen Puppet Git Repo. Neue Wege sollen ausprobiert werden mit diesem schicken, neuen\nPuppetmaster wie bspw. ",[1002,51261,51264],{"href":51262,"rel":51263,"title":51262},"http://theforeman.org/",[1006],"Foreman",[1002,51266,51269],{"href":51267,"rel":51268,"title":51269},"https://github.com/rodjek/librarian-puppet",[1006],"librarian-puppet",[439,51271,51272],{},"…und wir sind als Pioniere mit dabei.",[439,51274,51275],{},[448,51276,51277],{},"29-04-2013",[439,51279,51280,51281,30175,51284,51287],{},"Aus einer Laune heraus führe ich auf meinem Rechner ",[471,51282,51283],{},"mv sudoers sudoers.bak",[471,51285,51286],{},"cp sudoers sudoers.bak"," aus und kann\nprompt nicht mehr allzu viel tun. “Toll” 🙂 Zum Glück habe ich die glänzende Idee, meinen Rechner im Recovery Mode zu\nbooten und kann das Problem beheben. Ist mir das Ganze vielleicht peinlich…",[439,51289,51290],{},[448,51291,51292],{},"30-04-2013",[439,51294,51295],{},"Heute darf ich mit htaccess spielen. Zivi und ich richten zum Einen eine LDAP-Authentifizierung, zum Anderen eine\nAuthentifizierung mittels htpasswd File ein.",[439,51297,51298],{},"Cool, ist ja gar nicht sooo kompliziert.",[439,51300,51301],{},"Da das Produktivsystem der Urlaubsverwaltung inzwischen von außen erreichbar ist, möchte ich dies auch für das\nTestsystem durchführen – und zwar selbst. DNS Eintrag, vhosts Config, Rewrite Rules – ajo, kriege ich bestimmt auch\nalleine hin. Verschdl warnt mich “Ja, kannst du schon selber machen. Aber wenn du irgendwas falsch machst, ist halt\nalles kaputt”. Ich mach’s trotzdem. Alleine um 17 Uhr. Und nichts geht kaputt juhu 🙂",[439,51303,51304],{},[448,51305,51306],{},"08-05-2013",[439,51308,51309],{},"Matze und ich haben für unseren firmeninternen IRC-Bot leo ein eigenes Puppet Modul zusammengeschustert. Dieses stellt\ndie für leo benötigte Infrastruktur zur Verfügung. Außerdem deployt sich leo nun dank Puppet und einem\nVersionschecker-Skript automatisch selbst, wenn es eine neue Version gibt. Ziemlich nice, finde ich.",[439,51311,51312],{},[448,51313,51314],{},"14-05-2013",[439,51316,51317,51318,51324,51325,51330,51331,51337,51338,51341],{},"Ich stelle fest: ",[1002,51319,51323],{"href":51320,"rel":51321,"title":51322},"http://aws.amazon.com/de/",[1006],"AWS","Amazon Web Services"," EC2 Instanzen\nper ",[1002,51326,51329],{"href":51327,"rel":51328,"title":51329},"http://www.vagrantup.com/",[1006],"Vagrant"," hochzuziehen ist trotz\nvorhandenem ",[1002,51332,51336],{"href":51333,"rel":51334,"title":51335},"https://github.com/mitchellh/vagrant-aws",[1006],"vagrant-aws","AWS-Plugin"," nicht so ganz ausgereift. AWS EC2\nInstanzen über ",[1002,51339,51264],{"href":51262,"rel":51340,"title":51262},[1006]," zu pflegen ist allerdings einfach nur\nriesengroßer Pain argh…",[439,51343,51344],{},[448,51345,51346],{},"22-05-2013",[439,51348,51349,51352,51353,51356],{},[471,51350,51351],{},"librarian-puppet outdated"," ist nicht wirklich brauchbar. Ich will etwas, das wirklich prüft, ob Puppet Module sich\nverändert haben und es nötig ist, ein ",[471,51354,51355],{},"librarian-puppet update"," auszuführen. Naja, dann skripte ich halt selber was.",[439,51358,51359],{},[448,51360,51361],{},"17-06-2013",[439,51363,51364],{},"17 Uhr, mein Rechner kommt plötzlich nicht mehr ins Internet und Intranet. Was tut man? Netzwerkkabel tauschen, neue\nNetzwerkkarte einsetzen, mit dhcp rumspielen, geht trotzdem nicht. Verdammt, mache ich halt Feierabend, hilft ja alles\nnix.",[439,51366,51367],{},[448,51368,51369],{},"18-06-2013",[439,51371,51372],{},"“Aljona, das Problem gestern lag gar nicht an deinem Rechner. Unser Netzwerk war kaputt.” Argh….",[439,51374,51375],{},[448,51376,51377],{},"02-07-2013",[439,51379,51380],{},"Ich verwirre meine Kollegen bei schule@synyx mit dem Thema “Einführung in Puppet”. Ich glaube, ich bin schon ein\nbisschen zu sehr involviert in das Thema…",[439,51382,51383],{},[448,51384,51385],{},"03-07-2013",[439,51387,51388,51389,51394],{},"Ich erfreue mich mal wieder daran ",[1002,51390,51393],{"href":51391,"rel":51392,"title":51393},"http://gitolite.com/gitolite/admin.html",[1006],"gitolite-admin"," nutzen zu\nkönnen. Da gibt man sich selbst einfach ein paar Rechte mehr für ein Repo und kann prompt versehentlich gepushte\nPasswörter verschwinden lassen, hehe.",[439,51396,51397],{},[448,51398,51399],{},"09-07-2013",[439,51401,51402,51403,51406],{},"Kein “works on my machine” mehr für unser synyx Dashboard. Ich habe eine ",[1002,51404,51329],{"href":51327,"rel":51405,"title":51329},[1006],"\nUmgebung gebaut, die via Puppet provisioniert wird, und ein Puppet Modul erstellt, das die nötige Infrastruktur für\nunser polyglottes synyx Dashboard bereitstellt. Nun muss man sich nicht mehr darum kümmern, dass auf dem eigenen Rechner\nbestimmte Versionen von Ruby, Perl, Groovy, Java und Co. installiert sind, wenn man am synyx Dashboard entwickeln\nmöchte.",[439,51408,51409,51410],{},"Nun reicht ein simples ",[471,51411,51412],{},"vagrant up",[439,51414,51415],{},"Automatisierung++ 🙂",[439,51417,51418],{},[448,51419,51420],{},"…und die Moral von der Geschicht’?",[439,51422,51423],{},"Den Einblick in die dunkle Seite der Macht kann ich künftigen synyx Azubis nur empfehlen. Natürlich war es für mich\ntrotz allem nur ein kleiner Einblick in das große Ganze und ich denke mir nach wie vor oft genug “Häääää?!”, wenn ich\nAdmin-Gesprächen lausche. Aber ich habe doch sehr vieles mitnehmen können aus dieser Zeit. Ich kann mir nun unter den\nAdmin-Fremdwörtern Puppet, librarian-puppet, Foreman, Nagios/Icinga, gitolite-admin, VLAN, Reverse-Proxy, DNS,\nvhost, htaccess, AWS, EC2, Vagrant, lvm, usw. tatsächlich konkret etwas vorstellen.",[439,51425,51426,51427,51432,51433,51435],{},"Aber Vorsicht, es gibt auch Nebenwirkungen! Die inflationäre Benutzung von ",[1002,51428,51431],{"href":51429,"rel":51430,"title":51431},"http://www.vim.org/",[1006],"vim"," kann dazu\nführen, dass man automatisch versucht, vim keys auch in anderen Editoren einzusetzen. Mir ist es inzwischen schon einige\nMale passiert, dass ich versucht habe in anderen Editoren mittels ",[471,51434,6875],{}," zu suchen….",{"title":469,"searchDepth":507,"depth":507,"links":51437},[],[13208],"2013-07-10T12:39:00",{},"/blog/devoooops-azubitausch-bei-synyx",{"title":51159,"description":51170},"blog/devoooops-azubitausch-bei-synyx",[51445,51446,51447,51448,14464,389],"admin","azubitausch","devop","foreman","03-04-2013 Es ist kein verspäteter Aprilscherz. Um über den eigenen Tellerrand zu schauen, verlasse ich die gewohnte Java Welt und breche meine Zelte im Individualsoftware Team ab. Der DevOp-Azubitausch beginnt.…","D3gvsbWqzlBAaOEi1LnEt9R4i7kFlaJ0N6kkHOxjhF4",{"id":51452,"title":51453,"author":51454,"body":51455,"category":51617,"date":51618,"description":51619,"extension":1034,"link":51620,"meta":51621,"navigation":916,"path":51622,"seo":51623,"slug":51459,"stem":51624,"tags":51625,"teaser":51629,"__hash__":51630},"blog/blog/usefulness-ranking-of-code-metrics.md","Usefulness Ranking of Code Metrics",[97],{"type":432,"value":51456,"toc":51606},[51457,51460,51463,51471,51474,51477,51491,51494,51498,51507,51511,51514,51518,51521,51524,51527,51531,51534,51538,51547,51551,51554,51560,51564,51573,51577,51584,51588,51597,51600,51603],[435,51458,51453],{"id":51459},"usefulness-ranking-of-code-metrics",[439,51461,51462],{},"Static code analysis is one of the more controversial fields of software engineering. “Misleading Bogus!” screamers and\n“Must not work without it!” pleaders are bashing their heads in like survivors of a zombie war. My contribution to this\nargument is an attempt to evaluate the usefulness of different code analysis figures.",[439,51464,51465,51466,51470],{},"Since I mostly work in Java projects with ",[1002,51467,51469],{"href":43582,"rel":51468,"title":51469},[1006],"Sonar"," as main analysis tool, my ranking\nis centered on this environment. Some of the mentioned metrics don’t even exist outside of Sonar. Nonetheless much of it\nshould be easily transferable to other programming languages or software design in general.",[439,51472,51473],{},"Of course this ranking is highly subjective on my personal experience and only partly informational, since most\ndevelopers already know the meaning of all the code metrics. More than that I hope to trigger a discussion about their\nusefulness and importance.",[439,51475,51476],{},"First things first: What makes a code metric useful?",[994,51478,51479,51482,51485,51488],{},[997,51480,51481],{},"It outright shows a violation of or deviation from the defined best practice.",[997,51483,51484],{},"It hints to a place in your source code that has design flaws.",[997,51486,51487],{},"It shows that a certain aspect in your project is highly neglected and needs to be worked on.",[997,51489,51490],{},"You can quantify it by telling a “normal” or “good” value and react on it when this norm is violated",[439,51492,51493],{},"If any of these is true, a metric can be considered somewhat useful. So let’s dive into it and have a look at the\ndifferent metrics, starting with the most useful ones:",[3938,51495,51497],{"id":51496},"_1-cyclomatic-complexity","1. Cyclomatic Complexity",[439,51499,22944,51500,51506],{},[1002,51501,51505],{"href":51502,"rel":51503,"title":51504},"http://en.wikipedia.org/wiki/Cyclomatic_complexity",[1006],"Cyclomatic complexity","cyclomatic complexity"," of classes and\nmethods turns out to be my favorite code metric. It almost always hints to flawed design because too much decision logic\nis crammed into one method or class. The code often is not unit tested properly because complex units are very difficult\nto test – every different execution path in the code flow should have its own unit test. In general you should strive\nfor low complexity in every global and local scope of your project, which makes the cyclomatic complexity a very\nimportant and useful measurement.",[3938,51508,51510],{"id":51509},"_2-duplications","2. Duplications",[439,51512,51513],{},"Avoiding code duplication is a major topic of every code design author and that is for good reason. The corresponding\nmetric plain and simple points out your duplicated code and by doing so gives you opportunities to get rid of serious\nerror sources and to reduce future work. Often it tells you that a new layer of abstraction is needed or that you have\nto rethink your module/package/class structure to centralize the duplications. Without the duplication metric these\nplaces are very difficult to find.",[3938,51515,51517],{"id":51516},"_3-rules-compliance-sonar","3. Rules Compliance (Sonar)",[439,51519,51520],{},"The Sonar Rules Compliance (RC) shows the relative amount of coding rules violations in your project. Basically it runs\nstatic code analysis with PMD, Checkstyle, Findbugs etc.",[439,51522,51523],{},"Improving the RC forces the developers to learn how to avoid rules violations, thus improving the code quality. As a\nside effect different developers are forced to use the same coding style by following the same rules. Other than that,\nthe RC is a good measurement to get a rough impression of the overall code quality of a project because so many\ndifferent violations contribute to it. This also makes it a good instrument of comparison. You can compare the RC of\ndifferent projects to get a rough idea of their relative code quality.",[439,51525,51526],{},"Drilling down the specific violations sometimes hints to design flaws, although often the interesting violations are not\neasily identifiable in the mass of unimportant ones.",[3938,51528,51530],{"id":51529},"_4-package-tangling","4. Package Tangling",[439,51532,51533],{},"In the first place the package tangle index and similar metrics show you cyclic dependencies, which are always bad. In\naddition it can identify dependency magnets like util packages that are used all over the project, which makes changes\non them quite difficult.",[3938,51535,51537],{"id":51536},"_5-lcom4","5. LCOM4",[439,51539,22944,51540,51546],{},[1002,51541,51545],{"href":51542,"rel":51543,"title":51544},"https://web.archive.org/web/20131129081756/http://docs.codehaus.org:80/display/SONAR/LCOM4+-+Lack+of+Cohesion+of+Methods",[1006],"LCOM4","Lack of Cohesion of Methods","\ntells you how much the methods inside a class belong together by measuring if they use the same members of the class. An\nLCOM higher than one often leads you to a violation of the Single Responsibility Principle.",[3938,51548,51550],{"id":51549},"_6-lines-of-code","6. Lines of Code",[439,51552,51553],{},"Wait, what? Lines of code is not at the end of the list? Isn’t that just that bogus number, which tells us absolutely\nnothing and encouraged developers in the past to produce crap because they were paid by lines of code?",[439,51555,51556,51557,51559],{},"Well, on the one hand this is true – on the other hand it isn’t ",[990,51558,36769],{}," useless in my opinion. If you have a look at the\namount of lines of code broken down by class or method you will almost always find a badly designed piece of code at the\ntop of the list. Most of the times the largest class in a project is the “black sheep”, which every developer fears\nchanging and where redesign is needed the most. Lines of Code per class helps you identifying it.",[3938,51561,51563],{"id":51562},"_7-sonar-quality-index","7. Sonar Quality Index",[439,51565,51566,51567,51572],{},"Sonar’s ",[1002,51568,51571],{"href":51569,"rel":51570,"title":51571},"https://web.archive.org/web/20150118055327/http://docs.codehaus.org:80/display/SONAR/Quality+Index+Plugin",[1006],"Quality Index","\ntries to merge several other Indexes into one number to give an overall indicator for code quality. It doesn’t do a very\ngood job though, because the formulas and weightings behind it are not really intuitive, which makes it a pretty\nintransparent and meaningless measurement. You can use it to roughly compare projects with one another but it won’t\nreally help to increase your code quality.",[3938,51574,51576],{"id":51575},"_8-sonar-complexity-factor","8. Sonar Complexity Factor",[439,51578,51579,51580,51583],{},"The Sonar Complexity Factor is so far down the list because it is always zero. Always! You say I am lying and you have\nseen it above zero? Well, then measuring the code quality of your project is one of your lesser problems. The Complexity\nFactor only rises above zero, when you have a cyclomatic complexity of 31 or more somewhere in your code. That means a\nmethod with ",[990,51581,51582],{},"31 or more different execution paths",". That’s the kind of code you don’t want to change anymore, let alone\nfix it. You just wanna release it from its pain and throw it away. And a metric that only shows something, when the game\nover screen is already flashing in front of you, doesn’t help at all.",[3938,51585,51587],{"id":51586},"_9-lines-of-comments","9. Lines of Comments",[439,51589,51590,51591,51596],{},"Counting lines of comments to evaluate your code is one of the worst ideas I’ve heard. Sonar for example even tells you\nthat it is good to have more lines of comments – whaaaat? Didn’t we learn in Uncle\nBob’s ",[1002,51592,51595],{"href":51593,"rel":51594,"title":51595},"http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882",[1006],"Clean Code"," that\n“comments are not like Schindler’s list, they are not pure good”?",[439,51598,51599],{},"Yes, you should describe your API properly with Javadoc. But you should try to reduce the comments describing your code,\nthe code should describe itself. These opposing goals make it impossible to tell a “good” amount of comments, thus\nmaking this metric completely useless.",[439,51601,51602],{},"OK, that’s it. I hope I could give you a little insight to the usefulness of some code metric numbers or at least got a\n“bullshit, that guy has no clue” out of you to animate you for discussion.",[439,51604,51605],{},"One more thing: In my opinion a newly started greenfield project should try to keep the first five of the list at\noptimum (duplication and dependency cycles at 0, LCOM at 1, complexity near 1, Rules Compliance at 100%), which is not\nimpossible and not only gives you a very good feeling about your code but also saves you a lot of work in the long run.",{"title":469,"searchDepth":507,"depth":507,"links":51607},[51608,51609,51610,51611,51612,51613,51614,51615,51616],{"id":51496,"depth":507,"text":51497},{"id":51509,"depth":507,"text":51510},{"id":51516,"depth":507,"text":51517},{"id":51529,"depth":507,"text":51530},{"id":51536,"depth":507,"text":51537},{"id":51549,"depth":507,"text":51550},{"id":51562,"depth":507,"text":51563},{"id":51575,"depth":507,"text":51576},{"id":51586,"depth":507,"text":51587},[1030],"2013-07-01T10:53:44","Static code analysis is one of the more controversial fields of software engineering. “Misleading Bogus!” screamers and\\n“Must not work without it!” pleaders are bashing their heads in like survivors of a zombie war. My contribution to this\\nargument is an attempt to evaluate the usefulness of different code analysis figures.","https://synyx.de/blog/usefulness-ranking-of-code-metrics/",{},"/blog/usefulness-ranking-of-code-metrics",{"title":51453,"description":51462},"blog/usefulness-ranking-of-code-metrics",[51626,44009,51627,51628],"code-metrics","sonar","static-code-analysis","Static code analysis is one of the more controversial fields of software engineering. “Misleading Bogus!” screamers and “Must not work without it!” pleaders are bashing their heads in like survivors…","wenHIR1volcE7QrfG0x3m6zQmC8Efe8TxqlNM0COPrA",{"id":51632,"title":51633,"author":51634,"body":51635,"category":52273,"date":52274,"description":51642,"extension":1034,"link":52275,"meta":52276,"navigation":916,"path":52277,"seo":52278,"slug":51639,"stem":52279,"tags":52280,"teaser":52282,"__hash__":52283},"blog/blog/awesome-css-3-layouting.md","Awesome CSS 3 Layouting",[335],{"type":432,"value":51636,"toc":52268},[51637,51640,51643,51657,51672,51684,51687,51690,51696,51866,51892,51912,51915,51919,51933,51939,52217,52223,52234,52237,52240,52243,52254,52263,52266],[435,51638,51633],{"id":51639},"awesome-css-3-layouting",[439,51641,51642],{},"At first let me ask you a few questions about developing web applications:",[994,51644,51645,51648,51651,51654],{},[997,51646,51647],{},"How do you create multiple column layouts?",[997,51649,51650],{},"How do you make it flexible?",[997,51652,51653],{},"How do you solve the 100% height problem?",[997,51655,51656],{},"How do you make it responsiveness for Desktop vs Mobile?",[439,51658,51659,51660,51663,51664,51667,51668,51671],{},"If one of your answers contained ‘",[990,51661,51662],{},"absolute positioning","‘, ‘",[990,51665,51666],{},"floating","‘ or ‘",[990,51669,51670],{},"JavaScript","‘ you’re welcome for further\nreading about my favourite CSS 3 features. Wait, CSS 3? The thing that enables rounded corners and gradients? Yep,\nexactly, and despite the new trend of flat design CSS 3 is still useful since it has a bit more to offer than rounded\ncorners and gradients.",[439,51673,51674],{},[448,51675,51676,51679,51680,51683],{},[29523,51677,51678],{},"Unfortunately this is still bleeding edge and even Firefox (version 21.0 on ubuntu 13.04) doesn’t render the\nexamples."," Update: A few hours ago version 22 of Firefox was released with support for ",[471,51681,51682],{},"display: flex"," *yay*",[439,51685,51686],{},"Feel free to add working examples in the comments below 🙂",[3938,51688,51682],{"id":51689},"display-flex",[439,51691,51692,51693,51695],{},"My most favourite feature is the new display property ",[471,51694,51682],{},". This solves the first three questions including\nthe most painful 100% height problem. Remember the ugly JavaScript workarounds to set the height equally to to highest\ndiv? Or the abuse of the border attribute and absolute positioned divs? Well, forget that. All you gonna need in the\nfuture are a few lines of css code.",[464,51697,51699],{"className":16895,"code":51698,"language":16897,"meta":469,"style":469},".container {\n display: -webkit-flex;\n display: flex;\n}\n.menu {\n overflow: hidden;\n background-color: #D8E47F;\n -webkit-flex: 1;\n flex: 1;\n}\n.content {\n -webkit-flex: 3;\n flex: 3;\n}\n.sidenote {\n padding: 1em;\n background-color: #D8E47F;\n -webkit-flex: 2;\n flex: 2;\n}\n.menu > ul {\n margin: 1em;\n list-style-type: none;\n white-space: nowrap;\n}\n.menu a {\n color: black;\n}\n.content article {\n margin: 1em;\n}\n* {\n padding: 0;\n margin: 0;\n}\n\n",[471,51700,51701,51706,51711,51716,51720,51725,51730,51735,51740,51745,51749,51754,51759,51764,51768,51773,51778,51782,51787,51792,51796,51801,51806,51811,51816,51820,51825,51830,51834,51839,51843,51847,51852,51857,51862],{"__ignoreMap":469},[474,51702,51703],{"class":476,"line":477},[474,51704,51705],{},".container {\n",[474,51707,51708],{"class":476,"line":507},[474,51709,51710],{}," display: -webkit-flex;\n",[474,51712,51713],{"class":476,"line":547},[474,51714,51715],{}," display: flex;\n",[474,51717,51718],{"class":476,"line":584},[474,51719,703],{},[474,51721,51722],{"class":476,"line":607},[474,51723,51724],{},".menu {\n",[474,51726,51727],{"class":476,"line":642},[474,51728,51729],{}," overflow: hidden;\n",[474,51731,51732],{"class":476,"line":663},[474,51733,51734],{}," background-color: #D8E47F;\n",[474,51736,51737],{"class":476,"line":694},[474,51738,51739],{}," -webkit-flex: 1;\n",[474,51741,51742],{"class":476,"line":700},[474,51743,51744],{}," flex: 1;\n",[474,51746,51747],{"class":476,"line":913},[474,51748,703],{},[474,51750,51751],{"class":476,"line":920},[474,51752,51753],{},".content {\n",[474,51755,51756],{"class":476,"line":926},[474,51757,51758],{}," -webkit-flex: 3;\n",[474,51760,51761],{"class":476,"line":932},[474,51762,51763],{}," flex: 3;\n",[474,51765,51766],{"class":476,"line":938},[474,51767,703],{},[474,51769,51770],{"class":476,"line":944},[474,51771,51772],{},".sidenote {\n",[474,51774,51775],{"class":476,"line":950},[474,51776,51777],{}," padding: 1em;\n",[474,51779,51780],{"class":476,"line":956},[474,51781,51734],{},[474,51783,51784],{"class":476,"line":962},[474,51785,51786],{}," -webkit-flex: 2;\n",[474,51788,51789],{"class":476,"line":4876},[474,51790,51791],{}," flex: 2;\n",[474,51793,51794],{"class":476,"line":4888},[474,51795,703],{},[474,51797,51798],{"class":476,"line":4900},[474,51799,51800],{},".menu > ul {\n",[474,51802,51803],{"class":476,"line":4913},[474,51804,51805],{}," margin: 1em;\n",[474,51807,51808],{"class":476,"line":4921},[474,51809,51810],{}," list-style-type: none;\n",[474,51812,51813],{"class":476,"line":4932},[474,51814,51815],{}," white-space: nowrap;\n",[474,51817,51818],{"class":476,"line":4938},[474,51819,703],{},[474,51821,51822],{"class":476,"line":4946},[474,51823,51824],{},".menu a {\n",[474,51826,51827],{"class":476,"line":4952},[474,51828,51829],{}," color: black;\n",[474,51831,51832],{"class":476,"line":4957},[474,51833,703],{},[474,51835,51836],{"class":476,"line":4969},[474,51837,51838],{},".content article {\n",[474,51840,51841],{"class":476,"line":4990},[474,51842,51805],{},[474,51844,51845],{"class":476,"line":5001},[474,51846,703],{},[474,51848,51849],{"class":476,"line":5013},[474,51850,51851],{},"* {\n",[474,51853,51854],{"class":476,"line":5024},[474,51855,51856],{}," padding: 0;\n",[474,51858,51859],{"class":476,"line":5035},[474,51860,51861],{}," margin: 0;\n",[474,51863,51864],{"class":476,"line":5047},[474,51865,703],{},[439,51867,51868,51869,51872,51873,51876,51877,51880,51881,51884,51885,51887,51888,51891],{},"The value of the ",[471,51870,51871],{},"flex"," property tells the browser how much space the section should fill of the available place. In our\nexample the ",[990,51874,51875],{},".main-nav"," is the smallest, followed by ",[990,51878,51879],{},".side-note"," which is twice as big and by ",[990,51882,51883],{},".content"," which is\nthree times as big as the ",[990,51886,51875],{},". The children of ",[990,51889,51890],{},".wrapper"," are flexible (as the name flex tells us), in other\nwords these sections will adjust their width relatively to the parent element. Feel free to play around with the code\npen above! Open it in a new window and change the browser size and see how the other columns adapt their height\nautomatically to the ‘master’ column. Awesome, isn’t it? No more JavaScript calculations or ugly CSS workarounds for\nthis trivial use case.",[439,51893,51894,51895,51898,51899,51902,51903,51905,51906,1402],{},"Now what if you want a static width for the ",[990,51896,51897],{},".main-menu",". Well, just remove ",[471,51900,51901],{},"flex: 1"," or replace it by a width\ndeclaration, that’s it. The other columns will take the remaining place that is left, of course relative to it’s set\n",[471,51904,51871],{}," value. If you’re interested to dive deaper into the amazing flexbox layout I recommend the article\non ",[1002,51907,51911],{"href":51908,"rel":51909,"title":51910},"http://css-tricks.com/snippets/css/a-guide-to-flexbox/",[1006],"css-tricks.com | flexbox","css-tricks.com",[439,51913,51914],{},"Impressed so far? We’re just getting started with CSS 3!",[3938,51916,51918],{"id":51917},"media","@media",[439,51920,51921,51922,51925,51926,51932],{},"Nowadays we developers ",[29523,51923,51924],{},"hopefully"," want to support various devices and resolutions. How often do I curse webpages\nwhile surfing on it with my smartphone. Most smartphones uses Webkit as browser platform, so maybe it’s worth to take a\nlook\nat ",[1002,51927,51931],{"href":51928,"rel":51929,"title":51930},"https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries",[1006],"MDN | Media Queries","CSS 3 Media Queries","\neven now.",[439,51934,51935,51936,51938],{},"In the codepen below the ",[471,51937,51918],{}," is at the bottom since it must override the default css values. In this example the\nsidenote will be hidden if the display is too small. Furthermore the menu on the left will be positioned at the top when\ndecreasing the display size even more.",[464,51940,51942],{"className":16895,"code":51941,"language":16897,"meta":469,"style":469},".container {\n display: -webkit-flex;\n display: flex;\n}\n.menu {\n overflow: hidden;\n background-color: #D8E47F;\n -webkit-flex: 1;\n flex: 1;\n -webkit-transition: -webkit-flex 1s;\n transition: flex 1s;\n}\n.content {\n -webkit-flex: 3;\n flex: 3;\n}\n.sidenote {\n padding: 1em;\n background-color: #D8E47F;\n -webkit-flex: 2;\n flex: 2;\n}\n.menu > ul {\n margin: 1em;\n list-style-type: none;\n white-space: nowrap;\n}\n.menu a {\n color: black;\n}\n.content article {\n margin: 1em;\n}\n@media (max-width: 800px) {\n .sidenote {\n display: none;\n }\n}\n@media (max-width: 400px) {\n .menu {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n }\n .menu > ul {\n display: -webkit-flex;\n display: -moz-box;\n display: flex;\n }\n .menu > ul > li {\n padding: 0 1em;\n -webkit-flex: 1;\n flex: 1;\n }\n .content {\n padding-top: 3em\n }\n}\n* {\n padding: 0;\n margin: 0;\n}\n\n",[471,51943,51944,51948,51952,51956,51960,51964,51968,51972,51976,51980,51985,51990,51994,51998,52002,52006,52010,52014,52018,52022,52026,52030,52034,52038,52042,52046,52050,52054,52058,52062,52066,52070,52074,52078,52083,52088,52093,52097,52101,52106,52111,52116,52121,52126,52131,52135,52140,52145,52150,52155,52159,52164,52169,52174,52179,52183,52188,52193,52197,52201,52205,52209,52213],{"__ignoreMap":469},[474,51945,51946],{"class":476,"line":477},[474,51947,51705],{},[474,51949,51950],{"class":476,"line":507},[474,51951,51710],{},[474,51953,51954],{"class":476,"line":547},[474,51955,51715],{},[474,51957,51958],{"class":476,"line":584},[474,51959,703],{},[474,51961,51962],{"class":476,"line":607},[474,51963,51724],{},[474,51965,51966],{"class":476,"line":642},[474,51967,51729],{},[474,51969,51970],{"class":476,"line":663},[474,51971,51734],{},[474,51973,51974],{"class":476,"line":694},[474,51975,51739],{},[474,51977,51978],{"class":476,"line":700},[474,51979,51744],{},[474,51981,51982],{"class":476,"line":913},[474,51983,51984],{}," -webkit-transition: -webkit-flex 1s;\n",[474,51986,51987],{"class":476,"line":920},[474,51988,51989],{}," transition: flex 1s;\n",[474,51991,51992],{"class":476,"line":926},[474,51993,703],{},[474,51995,51996],{"class":476,"line":932},[474,51997,51753],{},[474,51999,52000],{"class":476,"line":938},[474,52001,51758],{},[474,52003,52004],{"class":476,"line":944},[474,52005,51763],{},[474,52007,52008],{"class":476,"line":950},[474,52009,703],{},[474,52011,52012],{"class":476,"line":956},[474,52013,51772],{},[474,52015,52016],{"class":476,"line":962},[474,52017,51777],{},[474,52019,52020],{"class":476,"line":4876},[474,52021,51734],{},[474,52023,52024],{"class":476,"line":4888},[474,52025,51786],{},[474,52027,52028],{"class":476,"line":4900},[474,52029,51791],{},[474,52031,52032],{"class":476,"line":4913},[474,52033,703],{},[474,52035,52036],{"class":476,"line":4921},[474,52037,51800],{},[474,52039,52040],{"class":476,"line":4932},[474,52041,51805],{},[474,52043,52044],{"class":476,"line":4938},[474,52045,51810],{},[474,52047,52048],{"class":476,"line":4946},[474,52049,51815],{},[474,52051,52052],{"class":476,"line":4952},[474,52053,703],{},[474,52055,52056],{"class":476,"line":4957},[474,52057,51824],{},[474,52059,52060],{"class":476,"line":4969},[474,52061,51829],{},[474,52063,52064],{"class":476,"line":4990},[474,52065,703],{},[474,52067,52068],{"class":476,"line":5001},[474,52069,51838],{},[474,52071,52072],{"class":476,"line":5013},[474,52073,51805],{},[474,52075,52076],{"class":476,"line":5024},[474,52077,703],{},[474,52079,52080],{"class":476,"line":5035},[474,52081,52082],{},"@media (max-width: 800px) {\n",[474,52084,52085],{"class":476,"line":5047},[474,52086,52087],{}," .sidenote {\n",[474,52089,52090],{"class":476,"line":5055},[474,52091,52092],{}," display: none;\n",[474,52094,52095],{"class":476,"line":5062},[474,52096,15319],{},[474,52098,52099],{"class":476,"line":5067},[474,52100,703],{},[474,52102,52103],{"class":476,"line":5072},[474,52104,52105],{},"@media (max-width: 400px) {\n",[474,52107,52108],{"class":476,"line":5084},[474,52109,52110],{}," .menu {\n",[474,52112,52113],{"class":476,"line":5100},[474,52114,52115],{}," position: fixed;\n",[474,52117,52118],{"class":476,"line":5111},[474,52119,52120],{}," top: 0;\n",[474,52122,52123],{"class":476,"line":5122},[474,52124,52125],{}," left: 0;\n",[474,52127,52128],{"class":476,"line":5134},[474,52129,52130],{}," right: 0;\n",[474,52132,52133],{"class":476,"line":5145},[474,52134,15319],{},[474,52136,52137],{"class":476,"line":5156},[474,52138,52139],{}," .menu > ul {\n",[474,52141,52142],{"class":476,"line":5163},[474,52143,52144],{}," display: -webkit-flex;\n",[474,52146,52147],{"class":476,"line":5170},[474,52148,52149],{}," display: -moz-box;\n",[474,52151,52152],{"class":476,"line":5175},[474,52153,52154],{}," display: flex;\n",[474,52156,52157],{"class":476,"line":5180},[474,52158,15319],{},[474,52160,52161],{"class":476,"line":5192},[474,52162,52163],{}," .menu > ul > li {\n",[474,52165,52166],{"class":476,"line":5217},[474,52167,52168],{}," padding: 0 1em;\n",[474,52170,52171],{"class":476,"line":5229},[474,52172,52173],{}," -webkit-flex: 1;\n",[474,52175,52176],{"class":476,"line":5240},[474,52177,52178],{}," flex: 1;\n",[474,52180,52181],{"class":476,"line":5251},[474,52182,15319],{},[474,52184,52185],{"class":476,"line":5262},[474,52186,52187],{}," .content {\n",[474,52189,52190],{"class":476,"line":5274},[474,52191,52192],{}," padding-top: 3em\n",[474,52194,52195],{"class":476,"line":5281},[474,52196,15319],{},[474,52198,52199],{"class":476,"line":5294},[474,52200,703],{},[474,52202,52203],{"class":476,"line":5307},[474,52204,51851],{},[474,52206,52207],{"class":476,"line":5318},[474,52208,51856],{},[474,52210,52211],{"class":476,"line":5323},[474,52212,51861],{},[474,52214,52215],{"class":476,"line":5330},[474,52216,703],{},[439,52218,52219,52220,52222],{},"A more complex use case could be the alignment of the navigation dependent of the screen size. With ",[471,52221,51918],{}," we can\nsimply place it on the top if the screen is small like on a smartphone or place it on the right if the page is visited\nby a desktop browser or even by a mobile one in landscape mode. And all magic is done with CSS only! Again, feel free to\nplay around with the given codepen.",[439,52224,52225,52226,52228,52229,1402],{},"There are a lot more ",[471,52227,51918],{}," properties and possibilities that can be best read\non ",[1002,52230,51911],{"href":52231,"rel":52232,"title":52233},"http://css-tricks.com/css-media-queries/",[1006],"css-tricks | Media Queries",[3938,52235,52236],{"id":52236},"transitions",[439,52238,52239],{},"Last but not least let me introduce css transitions if you never heard of it so far. As well as you won’t need\nJavaScript for 100% height calculations anymore you won’t need it for simple animations.",[439,52241,52242],{},"Let’s imagine that we have too much content and want to use every pixel the display gives us, but we cannot hide the\nmenu on the left because it’s too important!!1!. Why don’t we let the user decide what is important or not? And with\nsome extra animation he will love our application even more!",[439,52244,52245,52246,52249,52250,52253],{},"Due to simple adding or removing the class ",[990,52247,52248],{},".hidden"," we can change the width of the menu container. The ",[471,52251,52252],{},"transition","\nproperty takes all the magic for us and animates the width change. Try to increase the duration and click ‘hide’ and\n‘show’ in the codepen before the animation time is over. Note how the animation stops immediately and returns to the\nprevious state as soon as you click again. Did you ever implemented something like this with JavaScript? Luckily I\ndidn’t.",[439,52255,52256,52257,52262],{},"As well as the above mentioned CSS 3 features, transitions are much more powerful\nand ",[1002,52258,51911],{"href":52259,"rel":52260,"title":52261},"http://css-tricks.com/search-results/?q=transition",[1006],"css-tricks.com | transition"," is a nice source\nto learn more and to play with advanced examples.",[439,52264,52265],{},"Thanks for reading! And hopefully we can enjoy the full power of CSS 3 as soon as possible throughout all modern\nbrowsers (even IE…).",[1024,52267,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":52269},[52270,52271,52272],{"id":51689,"depth":507,"text":51682},{"id":51917,"depth":507,"text":51918},{"id":52236,"depth":507,"text":52236},[1030],"2013-06-26T11:51:48","https://synyx.de/blog/awesome-css-3-layouting/",{},"/blog/awesome-css-3-layouting",{"title":51633,"description":51642},"blog/awesome-css-3-layouting",[52281],"css3","At first let me ask you a few questions about developing web applications: How do you create multiple column layouts? How do you make it flexible? How do you solve…","4xpg-1Jio9AMHOQ32Q2XmEODn-Xarn6L9ODwyq0oQhE",{"id":52285,"title":52286,"author":52287,"body":52288,"category":52375,"date":52376,"description":52377,"extension":1034,"link":52378,"meta":52379,"navigation":916,"path":52380,"seo":52381,"slug":52292,"stem":52382,"tags":52383,"teaser":52384,"__hash__":52385},"blog/blog/the-open-source-datacenter-conference-2013.md","The Open Source Datacenter Conference 2013",[178],{"type":432,"value":52289,"toc":52373},[52290,52293,52296,52304,52307,52310,52313,52316,52319,52322,52325,52328,52331,52334,52337,52340,52343,52346,52349,52352,52355,52358,52361,52364,52367,52370],[435,52291,52286],{"id":52292},"the-open-source-datacenter-conference-2013",[439,52294,52295],{},"As some of you might already know, synyx provides their employees with an annual budget that we can spend on anything\nthat helps us improve our job skills, such as professional literature, training or seminars and conferences. So, a few\nweeks ago, when a coworker mentioned the upcoming Open Source Data Center Conference in Nuremberg, we decided to sign up\nfor it and go on an exciting adventure to improve our knowledge of the newest system administration technologies.",[439,52297,52298,52299,52303],{},"The Open Source Data Center Conference – OSDC – is a professional conference (hosted by ",[1002,52300,33400],{"href":52301,"rel":52302},"http://netways.de",[1006],", an\nopen source IT company, just like ourselves) for experienced Open Source professionals and insiders to gather and share\nknowledge and expertise over presentations, workshops and hands on examples, and also to learn from each others\nexperience in how we deal with everyday problems. Oh, and not to forget to enjoy some social networking and getting to\nknow some other system administrators.",[439,52305,52306],{},"We arrived on Tuesday night. My Coworker, Sascha, stayed at the Holiday Inn in Nuremberg – a pretty decent hotel located\nin downtown Nuremberg, right between the medieval town walls and the Pegnitz river, featuring nice, modern guest rooms\nand several conference rooms. Each day, we enjoyed the hotels breakfast bar, a great lunch buffet and tasty Dinner – and\nbetween talks and workshops, we’ve been fed with snacks, coffee and ice cream. They know how to keep system\nadministrators happy! Since I have family living in the Nuremberg area, I decided to opt out of staying at the hotel and\njust stayed at my folks house, so I can’t tell you much about the hotel rooms. I’ve been told, they also have been great\nthough.",[439,52308,52309],{},"Well, enough talking about the hotel and all the enjoyable amenities they provided us with. Let’s talk about the\nworkshops. The program was structured into three tracks: “Cloud & Big Data”, “DevOps and Methods” and “Infrastructure\nServices”. On Wednesday, I attended the following talks:",[439,52311,52312],{},"– “2000 databases later”: Kris Köhntopp talking about how they planned and rolled out the MySQL upgrade at Booking.com’s\ndatacenter. Kris’ talk was very entertaining and informative and a great opening for the conference. In my opinion the\nbest talk of the day!",[439,52314,52315],{},"– “Deconstructing the Cloud”: A talk by Nicholas Mailer about the advantages, but more important, trying to create\nawareness about the downsides and risks of cloud computing, by giving examples of how the cloud suffers from dangerous\nambiguity that can subvert the very freedoms and flexibility that Open Source Software provides. Not quite as\nentertaining as Kris’ talk, but still very interesting!",[439,52317,52318],{},"– “Petabyte storage with Ceph”: Martin Loschwitz explaining the Ceph object storage solution.",[439,52320,52321],{},"– “Configuration management and Linux packages”: Schlomo Schapiro showing how he handles service configuration\ndistribution by auto-generating and deploying configuration packages. Well… Also an interesting topic, but I didn’t\nreally see any advantages compared to puppet.",[439,52323,52324],{},"– “The truth is in the logs”: Finishing of the first day with Jan Dobersteins talk about log management. It made me want\nto try logstash now…",[439,52326,52327],{},"Kris Köhntopp also set up an ad-hoc Ingress workshop later this evening, introducing some of us to googles augmented\nreality game. Well, what can I say… I still don’t get what the fuzz is all about… *shrug*.",[439,52329,52330],{},"Most of the folks went to the “Indabahn” bar at the Hauptbahnhof, to get dinner, some drinks and to socialize a bit.\nApparently, the socializing part went quite well – not everybody got up in time to attend the first talk the next\nmorning :).",[439,52332,52333],{},"The second day had the following topics for me:",[439,52335,52336],{},"– “lxc@libvirt”:. Erkan Yanar talking about how LXC can be managed by libvirt. I heard some people were somewhat\ndisappointed about this talk, others found it pretty entertaining and interesting, and it made me want to look a little\ndeeper into this topic.",[439,52338,52339],{},"– “Software packaging with RPM demystified”: Andrew Fords talk about packaging with RPM. I was interested in this topic,\nso attending this session seemed like a good idea. Didn’t really learn anything new though.",[439,52341,52342],{},"– “Foreman – an interface for Puppet”: I thought I should give this talk a try, since I’ve heard about Foreman before\nand simply knew it was a “management tool to edit and deploy puppet manifests”. Boy, was I wrong. Ohad Levy was\nproviding us with lots of examples of how Foreman is a very powerful tool to manage every stage of the lifecycle of a\nserver. We are definitely looking into using Foreman here at synyx now.",[439,52344,52345],{},"– One of the best talks of the day for me was “Linux firewall change management resilient” by Lindsay Holmwood, explain\nhow Ript utilises iptables features to work its magic, and provide some concrete examples of how Ript can help increase\nthe reliability of the services you delive.",[439,52347,52348],{},"In this talk Lindsay will cover how Bulletproof’s approach to these problems has evolved over the last 4 years, and some\nof the tools Bulletproof has developed and built upon to provide an awesome service to our custo Hadoop”, which gave me\na chance to learn something more about a topic I have no experience with so far.",[439,52350,52351],{},"This evening, we didn’t do much in terms of socializing, so my Coworker and I just walked through the town, enjoying the\nnice weather and visiting some of the towns historic sights and eventually hanging out at the Kaiserburg.",[439,52353,52354],{},"Friday wasn’t technically part of the OSDC anymore, instead it was the “Puppet Camp”. A one day workshop with talks\nabout – you’ve guessed it – puppet.",[439,52356,52357],{},"– “The current state of Puppet”: Puppet Labs CTO Nigel Kersten talking about Puppet. Pretty straight forward. Pretty\ninteresting. What else can I say?",[439,52359,52360],{},"– “Refactoring Puppet”: James Fryman talking about how to refactor Puppet code in a controlled and organized method and\nhow to organize modules to take advantage of Data Drive infrastructure.",[439,52362,52363],{},"– “More of the Same – Please!”: Michael Haselgruebler talking about managing Multi-Tenant Application Servers with\nPuppet.",[439,52365,52366],{},"– “Foreman”: Pretty much Ohad Levys talk from Thursday again.",[439,52368,52369],{},"– “Puppet at Bulletproof Networks”: Lindsay Holmwood covering how his company has been working with Puppet for the last\n4 years and how they handle the problems and challenges they have been facing.",[439,52371,52372],{},"The OSDC and the Puppet Camp were very interesting and I learned a lot about new Data Center technologies. Still, after\nattending 3 days of talks and workshops, I was glad to call it a week. The OSDC gave me a lot of new ideas and it\ndefinitely was worth attending. Next years OSDC will be held in Berlin instead of Nuremberg and I am planning to visit.\nSee you guys there!",{"title":469,"searchDepth":507,"depth":507,"links":52374},[],[9045,1412],"2013-05-23T15:06:59","As some of you might already know, synyx provides their employees with an annual budget that we can spend on anything\\nthat helps us improve our job skills, such as professional literature, training or seminars and conferences. So, a few\\nweeks ago, when a coworker mentioned the upcoming Open Source Data Center Conference in Nuremberg, we decided to sign up\\nfor it and go on an exciting adventure to improve our knowledge of the newest system administration technologies.","https://synyx.de/blog/the-open-source-datacenter-conference-2013/",{},"/blog/the-open-source-datacenter-conference-2013",{"title":52286,"description":52295},"blog/the-open-source-datacenter-conference-2013",[],"As some of you might already know, synyx provides their employees with an annual budget that we can spend on anything that helps us improve our job skills, such as…","ND5vndQ0jBqYQYcFPXsNZfTIQOyWghjn2LVkBzpAaIs",{"id":52387,"title":52388,"author":52389,"body":52390,"category":53346,"date":53347,"description":53348,"extension":1034,"link":53349,"meta":53350,"navigation":916,"path":53351,"seo":53352,"slug":53354,"stem":53355,"tags":53356,"teaser":53358,"__hash__":53359},"blog/blog/asynchronous-concurrency-with-vert-x-part-2.md","Asynchronous concurrency with vert.x – Part 2",[9],{"type":432,"value":52391,"toc":53344},[52392,52395,52410,52424,52427,52450,52453,52477,52480,52543,52546,52554,52557,52572,52579,52845,52848,52851,53080,53083,53112,53119,53187,53190,53269,53280,53319,53322,53340,53342],[435,52393,52388],{"id":52394},"asynchronous-concurrency-with-vertx-part-2",[439,52396,52397,52398,52403,52404,52409],{},"CoffeeScript\nVert.x supports JavaScript through the ",[1002,52399,52402],{"href":52400,"rel":52401},"https://developer.mozilla.org/en/docs/Rhino",[1006],"Rhino JavaScript engine",". Although\nJavaScript is a decent language once you get to know it, I prefer ",[1002,52405,52408],{"href":52406,"rel":52407},"http://www.coffeescript.org",[1006],"CoffeeScript",", a\nlanguage that compiles to JavaScript. Luckily, vert.x has built-in support for CoffeeScript, so I can use it nearly\ntransparently. You will only notice the JavaScript under the hood when reading stack traces, which will refer to the\ncompiled JavaScript file.\nFor the examples in this blog post, the only thing you need to know a little CoffeeScript:",[464,52411,52413],{"className":16895,"code":52412,"language":16897,"meta":469,"style":469},"\nfoo = (a, b) -> a + b\n\n",[471,52414,52415,52419],{"__ignoreMap":469},[474,52416,52417],{"class":476,"line":477},[474,52418,917],{"emptyLinePlaceholder":916},[474,52420,52421],{"class":476,"line":507},[474,52422,52423],{},"foo = (a, b) -> a + b\n",[439,52425,52426],{},"Translates to the JavaScript code",[464,52428,52430],{"className":16895,"code":52429,"language":16897,"meta":469,"style":469},"\nvar foo = function (a, b) {\n return a + b; // (the last statement is returned)\n}\n\n",[471,52431,52432,52436,52441,52446],{"__ignoreMap":469},[474,52433,52434],{"class":476,"line":477},[474,52435,917],{"emptyLinePlaceholder":916},[474,52437,52438],{"class":476,"line":507},[474,52439,52440],{},"var foo = function (a, b) {\n",[474,52442,52443],{"class":476,"line":547},[474,52444,52445],{}," return a + b; // (the last statement is returned)\n",[474,52447,52448],{"class":476,"line":584},[474,52449,703],{},[439,52451,52452],{},"Also parentheses around function arguments are optional",[464,52454,52456],{"className":16895,"code":52455,"language":16897,"meta":469,"style":469},"\n foo a, b, c\n # same as\n foo(a, b, c)\n\n",[471,52457,52458,52462,52467,52472],{"__ignoreMap":469},[474,52459,52460],{"class":476,"line":477},[474,52461,917],{"emptyLinePlaceholder":916},[474,52463,52464],{"class":476,"line":507},[474,52465,52466],{}," foo a, b, c\n",[474,52468,52469],{"class":476,"line":547},[474,52470,52471],{}," # same as\n",[474,52473,52474],{"class":476,"line":584},[474,52475,52476],{}," foo(a, b, c)\n",[439,52478,52479],{},"The translated source code from the example described in the last post is",[464,52481,52483],{"className":16895,"code":52482,"language":16897,"meta":469,"style":469},"\nvertx = require 'vertx'\naddress = 'example.address'\nhandler = (message, replier) ->\n stdout.println \"sender sent \" + message\n replier \"pong 1\", (message, replier) ->\n # and so on\nvertx.eventBus.registerHandler address, handler\nvertx.eventBus.send address, \"ping 1\", (message, replier) ->\n stdout.println \"handler sent \" + message\n replier \"ping 2\", (message, replier) ->\n # and so on\n\n",[471,52484,52485,52489,52494,52499,52504,52509,52514,52519,52524,52529,52534,52539],{"__ignoreMap":469},[474,52486,52487],{"class":476,"line":477},[474,52488,917],{"emptyLinePlaceholder":916},[474,52490,52491],{"class":476,"line":507},[474,52492,52493],{},"vertx = require 'vertx'\n",[474,52495,52496],{"class":476,"line":547},[474,52497,52498],{},"address = 'example.address'\n",[474,52500,52501],{"class":476,"line":584},[474,52502,52503],{},"handler = (message, replier) ->\n",[474,52505,52506],{"class":476,"line":607},[474,52507,52508],{}," stdout.println \"sender sent \" + message\n",[474,52510,52511],{"class":476,"line":642},[474,52512,52513],{}," replier \"pong 1\", (message, replier) ->\n",[474,52515,52516],{"class":476,"line":663},[474,52517,52518],{}," # and so on\n",[474,52520,52521],{"class":476,"line":694},[474,52522,52523],{},"vertx.eventBus.registerHandler address, handler\n",[474,52525,52526],{"class":476,"line":700},[474,52527,52528],{},"vertx.eventBus.send address, \"ping 1\", (message, replier) ->\n",[474,52530,52531],{"class":476,"line":913},[474,52532,52533],{}," stdout.println \"handler sent \" + message\n",[474,52535,52536],{"class":476,"line":920},[474,52537,52538],{}," replier \"ping 2\", (message, replier) ->\n",[474,52540,52541],{"class":476,"line":926},[474,52542,52518],{},[439,52544,52545],{},"The shorter function declaration notation is a huge improvement, especially when dealing with the kind of\ncallback-heavy code that is prevalent when dealing with asynchronous concurrency.",[439,52547,52548,52549,6562],{},"The Sleeping Barber Problem\nTo challenge vert.x with something more exciting than ping-pong, I decided to model a basic concurrency problem that\nmirrors some of the challenges that our new application will face – the\nfamous ",[1002,52550,52553],{"href":52551,"rel":52552},"http://en.wikipedia.org/wiki/Sleeping_barber_problem",[1006],"Sleeping Barber Problem",[439,52555,52556],{},"The analogy is based upon a hypothetical barber shop with one barber. The barber has one barber chair and a waiting room\nwith a number of chairs in it. When the barber finishes cutting a customer’s hair, he dismisses the customer and then\ngoes to the waiting room to see if there are other customers waiting. If there are, he brings one of them back to the\nchair and cuts his hair. If there are no other customers waiting, he returns to his chair and sleeps in it.\nEach customer, when he arrives, looks to see what the barber is doing. If the barber is sleeping, then the customer\nwakes him up and sits in the chair. If the barber is cutting hair, then the customer goes to the waiting room. If there\nis a free chair in the waiting room, the customer sits in it and waits his turn. If there is no free chair, then the\ncustomer leaves. Based on a naïve analysis, the above description should ensure that the shop functions correctly, with\nthe barber cutting the hair of anyone who arrives until there are no more customers, and then sleeping until the next\ncustomer arrives. In practice, there are a number of problems that can occur that are illustrative of general scheduling\nproblems.",[439,52558,52559,52560,52565,52566,52571],{},"I’ve ",[1002,52561,52564],{"href":52562,"rel":52563},"https://github.com/OttoAllmendinger/term-paper-stm",[1006],"previously solved this problem","\nusing ",[1002,52567,52570],{"href":52568,"rel":52569},"http://en.wikipedia.org/wiki/Software_transactional_memory",[1006],"Software Transactional Memory"," and was interested how\nthe message-passing style of vert.x compares.",[439,52573,52574,52575,52578],{},"Barber.coffee\nThe barber shop problem nicely separates into two systems: a ",[471,52576,52577],{},"barber"," message handler that keeps track of incoming\ncustomers and manages the queue, and set of callback methods representing the customer, which initiate a communication\nsequence with the message handler. The following code defines the barber message handler.",[464,52580,52582],{"className":16895,"code":52581,"language":16897,"meta":469,"style":469},"\nvertx = require 'vertx'\naddr = 'barber'\nwaitTime = -> Math.random() * 100\nbarber = ->\n # the state of the message handler lives\n # in this closure\n busy = false\n queue = []\n freeSeats = 20\n # make the system a little indeterministic\n log = (message) ->\n stdout.println \"barber: #{message}\"\n # the following methods define the core behavior\n checkQueue = ->\n if queue.length > 0\n serveCustomer queue.shift()\n freeSeats += 1\n return true\n else\n return false\n serveCustomer = ({customer, replier}) ->\n log \"serving #{customer}\"\n busy = true\n replier 'serve', (message, replier) ->\n vertx.setTimer waitTime(), ->\n log \"done serving #{customer}\"\n busy = checkQueue()\n replier 'done'\n # this is the handler's callback method that\n # is being returned by the barber function\n (message, replier) ->\n customer = message\n if busy\n # there is an intermediate state where we know that we\n # have to queue the customer because there aren't any\n # free seats, but the customer must first acknowledge\n # the waiting state before we can actually put him in\n # the queue.\n if freeSeats > 0\n freeSeats -= 1\n log \"sending #{customer} to queue\"\n replier 'busy', (message, replier) ->\n # customer waiting ack\n queue.push {customer, replier}\n log \"queued #{customer} - \" +\n \"length: #{queue.length} - free seats: #{freeSeats}\"\n else\n replier 'full'\n else\n serveCustomer {customer, replier}\nexports.start = ->\n vertx.eventBus.registerHandler addr, barber()\n\n",[471,52583,52584,52588,52592,52597,52602,52607,52612,52617,52622,52627,52632,52637,52642,52647,52652,52657,52662,52667,52672,52677,52682,52686,52691,52696,52701,52706,52711,52716,52721,52726,52731,52736,52741,52746,52751,52756,52761,52766,52771,52776,52781,52786,52791,52796,52801,52806,52811,52816,52821,52826,52830,52835,52840],{"__ignoreMap":469},[474,52585,52586],{"class":476,"line":477},[474,52587,917],{"emptyLinePlaceholder":916},[474,52589,52590],{"class":476,"line":507},[474,52591,52493],{},[474,52593,52594],{"class":476,"line":547},[474,52595,52596],{},"addr = 'barber'\n",[474,52598,52599],{"class":476,"line":584},[474,52600,52601],{},"waitTime = -> Math.random() * 100\n",[474,52603,52604],{"class":476,"line":607},[474,52605,52606],{},"barber = ->\n",[474,52608,52609],{"class":476,"line":642},[474,52610,52611],{}," # the state of the message handler lives\n",[474,52613,52614],{"class":476,"line":663},[474,52615,52616],{}," # in this closure\n",[474,52618,52619],{"class":476,"line":694},[474,52620,52621],{}," busy = false\n",[474,52623,52624],{"class":476,"line":700},[474,52625,52626],{}," queue = []\n",[474,52628,52629],{"class":476,"line":913},[474,52630,52631],{}," freeSeats = 20\n",[474,52633,52634],{"class":476,"line":920},[474,52635,52636],{}," # make the system a little indeterministic\n",[474,52638,52639],{"class":476,"line":926},[474,52640,52641],{}," log = (message) ->\n",[474,52643,52644],{"class":476,"line":932},[474,52645,52646],{}," stdout.println \"barber: #{message}\"\n",[474,52648,52649],{"class":476,"line":938},[474,52650,52651],{}," # the following methods define the core behavior\n",[474,52653,52654],{"class":476,"line":944},[474,52655,52656],{}," checkQueue = ->\n",[474,52658,52659],{"class":476,"line":950},[474,52660,52661],{}," if queue.length > 0\n",[474,52663,52664],{"class":476,"line":956},[474,52665,52666],{}," serveCustomer queue.shift()\n",[474,52668,52669],{"class":476,"line":962},[474,52670,52671],{}," freeSeats += 1\n",[474,52673,52674],{"class":476,"line":4876},[474,52675,52676],{}," return true\n",[474,52678,52679],{"class":476,"line":4888},[474,52680,52681],{}," else\n",[474,52683,52684],{"class":476,"line":4900},[474,52685,47084],{},[474,52687,52688],{"class":476,"line":4913},[474,52689,52690],{}," serveCustomer = ({customer, replier}) ->\n",[474,52692,52693],{"class":476,"line":4921},[474,52694,52695],{}," log \"serving #{customer}\"\n",[474,52697,52698],{"class":476,"line":4932},[474,52699,52700],{}," busy = true\n",[474,52702,52703],{"class":476,"line":4938},[474,52704,52705],{}," replier 'serve', (message, replier) ->\n",[474,52707,52708],{"class":476,"line":4946},[474,52709,52710],{}," vertx.setTimer waitTime(), ->\n",[474,52712,52713],{"class":476,"line":4952},[474,52714,52715],{}," log \"done serving #{customer}\"\n",[474,52717,52718],{"class":476,"line":4957},[474,52719,52720],{}," busy = checkQueue()\n",[474,52722,52723],{"class":476,"line":4969},[474,52724,52725],{}," replier 'done'\n",[474,52727,52728],{"class":476,"line":4990},[474,52729,52730],{}," # this is the handler's callback method that\n",[474,52732,52733],{"class":476,"line":5001},[474,52734,52735],{}," # is being returned by the barber function\n",[474,52737,52738],{"class":476,"line":5013},[474,52739,52740],{}," (message, replier) ->\n",[474,52742,52743],{"class":476,"line":5024},[474,52744,52745],{}," customer = message\n",[474,52747,52748],{"class":476,"line":5035},[474,52749,52750],{}," if busy\n",[474,52752,52753],{"class":476,"line":5047},[474,52754,52755],{}," # there is an intermediate state where we know that we\n",[474,52757,52758],{"class":476,"line":5055},[474,52759,52760],{}," # have to queue the customer because there aren't any\n",[474,52762,52763],{"class":476,"line":5062},[474,52764,52765],{}," # free seats, but the customer must first acknowledge\n",[474,52767,52768],{"class":476,"line":5067},[474,52769,52770],{}," # the waiting state before we can actually put him in\n",[474,52772,52773],{"class":476,"line":5072},[474,52774,52775],{}," # the queue.\n",[474,52777,52778],{"class":476,"line":5084},[474,52779,52780],{}," if freeSeats > 0\n",[474,52782,52783],{"class":476,"line":5100},[474,52784,52785],{}," freeSeats -= 1\n",[474,52787,52788],{"class":476,"line":5111},[474,52789,52790],{}," log \"sending #{customer} to queue\"\n",[474,52792,52793],{"class":476,"line":5122},[474,52794,52795],{}," replier 'busy', (message, replier) ->\n",[474,52797,52798],{"class":476,"line":5134},[474,52799,52800],{}," # customer waiting ack\n",[474,52802,52803],{"class":476,"line":5145},[474,52804,52805],{}," queue.push {customer, replier}\n",[474,52807,52808],{"class":476,"line":5156},[474,52809,52810],{}," log \"queued #{customer} - \" +\n",[474,52812,52813],{"class":476,"line":5163},[474,52814,52815],{}," \"length: #{queue.length} - free seats: #{freeSeats}\"\n",[474,52817,52818],{"class":476,"line":5170},[474,52819,52820],{}," else\n",[474,52822,52823],{"class":476,"line":5175},[474,52824,52825],{}," replier 'full'\n",[474,52827,52828],{"class":476,"line":5180},[474,52829,52681],{},[474,52831,52832],{"class":476,"line":5192},[474,52833,52834],{}," serveCustomer {customer, replier}\n",[474,52836,52837],{"class":476,"line":5217},[474,52838,52839],{},"exports.start = ->\n",[474,52841,52842],{"class":476,"line":5229},[474,52843,52844],{}," vertx.eventBus.registerHandler addr, barber()\n",[439,52846,52847],{},"The state of the barber is encoded by the callback method that will be called for an upcoming event and the values of\nthe variables defined in the closure. By being able to store repliers you can easily trigger remote state changes\natomically, when they should occur.",[439,52849,52850],{},"Customer.coffee\nLet’s define the behavior of the customer in a separate file",[464,52852,52854],{"className":16895,"code":52853,"language":16897,"meta":469,"style":469},"\nvertx = require 'vertx'\naddr = 'barber'\nwaitTime = -> Math.random() * 100\nsendCustomer = (i) ->\n # As with the barber, the customer's state is\n # defined in this closure. The variables will\n # be modified by the callback methods that are\n # triggered by the message handler's replies.\n waiting = false\n beingServed = false\n log = (message) ->\n stdout.println \"customer #{i}: #{message}\"\n # just a shorthand\n send = (message, callback) ->\n vertx.eventBus.send addr, message, callback\n # factor out the exit method:\n # a customer can exit after having been served\n # or when the queue is full\n exit = (message) ->\n log message + \" - exiting\"\n # this method doesn't send a response\n # via the replier\n getHaircut = (message, replier) ->\n waiting = false\n beingServed = true\n log \"being served\"\n replier 'being-served', exit\n log \"enters\"\n send \"customer #{i}\", (message, replier) ->\n switch message\n when 'busy'\n waiting = true\n log 'waiting'\n replier 'waiting', getHaircut\n when 'serve'\n getHaircut message, replier\n when 'full'\n exit message\n# a loop that continuously sends customers\n# to the barber\nsendCustomerLoop = (i) ->\n sendCustomer i\n vertx.setTimer waitTime(), -> sendCustomerLoop i + 1\nexports.start = ->\n sendCustomerLoop 1\n\n",[471,52855,52856,52860,52864,52868,52872,52877,52882,52887,52892,52897,52902,52907,52911,52916,52921,52926,52931,52936,52941,52946,52951,52956,52961,52966,52971,52976,52981,52986,52991,52996,53001,53006,53011,53016,53021,53026,53031,53036,53041,53046,53051,53056,53061,53066,53071,53075],{"__ignoreMap":469},[474,52857,52858],{"class":476,"line":477},[474,52859,917],{"emptyLinePlaceholder":916},[474,52861,52862],{"class":476,"line":507},[474,52863,52493],{},[474,52865,52866],{"class":476,"line":547},[474,52867,52596],{},[474,52869,52870],{"class":476,"line":584},[474,52871,52601],{},[474,52873,52874],{"class":476,"line":607},[474,52875,52876],{},"sendCustomer = (i) ->\n",[474,52878,52879],{"class":476,"line":642},[474,52880,52881],{}," # As with the barber, the customer's state is\n",[474,52883,52884],{"class":476,"line":663},[474,52885,52886],{}," # defined in this closure. The variables will\n",[474,52888,52889],{"class":476,"line":694},[474,52890,52891],{}," # be modified by the callback methods that are\n",[474,52893,52894],{"class":476,"line":700},[474,52895,52896],{}," # triggered by the message handler's replies.\n",[474,52898,52899],{"class":476,"line":913},[474,52900,52901],{}," waiting = false\n",[474,52903,52904],{"class":476,"line":920},[474,52905,52906],{}," beingServed = false\n",[474,52908,52909],{"class":476,"line":926},[474,52910,52641],{},[474,52912,52913],{"class":476,"line":932},[474,52914,52915],{}," stdout.println \"customer #{i}: #{message}\"\n",[474,52917,52918],{"class":476,"line":938},[474,52919,52920],{}," # just a shorthand\n",[474,52922,52923],{"class":476,"line":944},[474,52924,52925],{}," send = (message, callback) ->\n",[474,52927,52928],{"class":476,"line":950},[474,52929,52930],{}," vertx.eventBus.send addr, message, callback\n",[474,52932,52933],{"class":476,"line":956},[474,52934,52935],{}," # factor out the exit method:\n",[474,52937,52938],{"class":476,"line":962},[474,52939,52940],{}," # a customer can exit after having been served\n",[474,52942,52943],{"class":476,"line":4876},[474,52944,52945],{}," # or when the queue is full\n",[474,52947,52948],{"class":476,"line":4888},[474,52949,52950],{}," exit = (message) ->\n",[474,52952,52953],{"class":476,"line":4900},[474,52954,52955],{}," log message + \" - exiting\"\n",[474,52957,52958],{"class":476,"line":4913},[474,52959,52960],{}," # this method doesn't send a response\n",[474,52962,52963],{"class":476,"line":4921},[474,52964,52965],{}," # via the replier\n",[474,52967,52968],{"class":476,"line":4932},[474,52969,52970],{}," getHaircut = (message, replier) ->\n",[474,52972,52973],{"class":476,"line":4938},[474,52974,52975],{}," waiting = false\n",[474,52977,52978],{"class":476,"line":4946},[474,52979,52980],{}," beingServed = true\n",[474,52982,52983],{"class":476,"line":4952},[474,52984,52985],{}," log \"being served\"\n",[474,52987,52988],{"class":476,"line":4957},[474,52989,52990],{}," replier 'being-served', exit\n",[474,52992,52993],{"class":476,"line":4969},[474,52994,52995],{}," log \"enters\"\n",[474,52997,52998],{"class":476,"line":4990},[474,52999,53000],{}," send \"customer #{i}\", (message, replier) ->\n",[474,53002,53003],{"class":476,"line":5001},[474,53004,53005],{}," switch message\n",[474,53007,53008],{"class":476,"line":5013},[474,53009,53010],{}," when 'busy'\n",[474,53012,53013],{"class":476,"line":5024},[474,53014,53015],{}," waiting = true\n",[474,53017,53018],{"class":476,"line":5035},[474,53019,53020],{}," log 'waiting'\n",[474,53022,53023],{"class":476,"line":5047},[474,53024,53025],{}," replier 'waiting', getHaircut\n",[474,53027,53028],{"class":476,"line":5055},[474,53029,53030],{}," when 'serve'\n",[474,53032,53033],{"class":476,"line":5062},[474,53034,53035],{}," getHaircut message, replier\n",[474,53037,53038],{"class":476,"line":5067},[474,53039,53040],{}," when 'full'\n",[474,53042,53043],{"class":476,"line":5072},[474,53044,53045],{}," exit message\n",[474,53047,53048],{"class":476,"line":5084},[474,53049,53050],{},"# a loop that continuously sends customers\n",[474,53052,53053],{"class":476,"line":5100},[474,53054,53055],{},"# to the barber\n",[474,53057,53058],{"class":476,"line":5111},[474,53059,53060],{},"sendCustomerLoop = (i) ->\n",[474,53062,53063],{"class":476,"line":5122},[474,53064,53065],{}," sendCustomer i\n",[474,53067,53068],{"class":476,"line":5134},[474,53069,53070],{}," vertx.setTimer waitTime(), -> sendCustomerLoop i + 1\n",[474,53072,53073],{"class":476,"line":5145},[474,53074,52839],{},[474,53076,53077],{"class":476,"line":5156},[474,53078,53079],{}," sendCustomerLoop 1\n",[439,53081,53082],{},"barbershop.coffee\nThis time, we want to run both handler and sender in the same process, for easier testing.",[464,53084,53086],{"className":16895,"code":53085,"language":16897,"meta":469,"style":469},"\nbarber = require 'barber'\ncustomer = require 'customer'\nbarber.start()\ncustomer.start()\n\n",[471,53087,53088,53092,53097,53102,53107],{"__ignoreMap":469},[474,53089,53090],{"class":476,"line":477},[474,53091,917],{"emptyLinePlaceholder":916},[474,53093,53094],{"class":476,"line":507},[474,53095,53096],{},"barber = require 'barber'\n",[474,53098,53099],{"class":476,"line":547},[474,53100,53101],{},"customer = require 'customer'\n",[474,53103,53104],{"class":476,"line":584},[474,53105,53106],{},"barber.start()\n",[474,53108,53109],{"class":476,"line":607},[474,53110,53111],{},"customer.start()\n",[439,53113,53114,53115,53118],{},"Running the shop\nWhen we start the ",[471,53116,53117],{},"barbershop.coffee"," script, we can see in the log that the shop is running as it is supposed to:",[464,53120,53122],{"className":16895,"code":53121,"language":16897,"meta":469,"style":469},"\ncustomer 1: enters\nbarber: serving customer 1\ncustomer 1: being served\nbarber: done serving customer 1\ncustomer 1: done - exiting\ncustomer 2: enters\nbarber: serving customer 2\ncustomer 2: being served\nbarber: done serving customer 2\ncustomer 2: done - exiting\ncustomer 3: enters\n[...]\n\n",[471,53123,53124,53128,53133,53138,53143,53148,53153,53158,53163,53168,53173,53178,53183],{"__ignoreMap":469},[474,53125,53126],{"class":476,"line":477},[474,53127,917],{"emptyLinePlaceholder":916},[474,53129,53130],{"class":476,"line":507},[474,53131,53132],{},"customer 1: enters\n",[474,53134,53135],{"class":476,"line":547},[474,53136,53137],{},"barber: serving customer 1\n",[474,53139,53140],{"class":476,"line":584},[474,53141,53142],{},"customer 1: being served\n",[474,53144,53145],{"class":476,"line":607},[474,53146,53147],{},"barber: done serving customer 1\n",[474,53149,53150],{"class":476,"line":642},[474,53151,53152],{},"customer 1: done - exiting\n",[474,53154,53155],{"class":476,"line":663},[474,53156,53157],{},"customer 2: enters\n",[474,53159,53160],{"class":476,"line":694},[474,53161,53162],{},"barber: serving customer 2\n",[474,53164,53165],{"class":476,"line":700},[474,53166,53167],{},"customer 2: being served\n",[474,53169,53170],{"class":476,"line":913},[474,53171,53172],{},"barber: done serving customer 2\n",[474,53174,53175],{"class":476,"line":920},[474,53176,53177],{},"customer 2: done - exiting\n",[474,53179,53180],{"class":476,"line":926},[474,53181,53182],{},"customer 3: enters\n",[474,53184,53185],{"class":476,"line":932},[474,53186,8703],{},[439,53188,53189],{},"This is what the output looks like when there is no congestion at all. By chance, these customers came in just after the\nprevious customer was served. If we wait a little longer, we can see a customer entering while the barber is busy:",[464,53191,53193],{"className":16895,"code":53192,"language":16897,"meta":469,"style":469},"\nbarber: serving customer 3\ncustomer 3: being served\ncustomer 4: enters\nbarber: sending customer 4 to queue\ncustomer 4: waiting\nbarber: queued customer 4 - length: 1 - free seats: 19\ncustomer 5: enters\nbarber: sending customer 5 to queue\ncustomer 5: waiting\nbarber: queued customer 5 - length: 2 - free seats: 18\nbarber: done serving customer 3\nbarber: serving customer 4\ncustomer 3: done - exiting\ncustomer 4: being served\n\n",[471,53194,53195,53199,53204,53209,53214,53219,53224,53229,53234,53239,53244,53249,53254,53259,53264],{"__ignoreMap":469},[474,53196,53197],{"class":476,"line":477},[474,53198,917],{"emptyLinePlaceholder":916},[474,53200,53201],{"class":476,"line":507},[474,53202,53203],{},"barber: serving customer 3\n",[474,53205,53206],{"class":476,"line":547},[474,53207,53208],{},"customer 3: being served\n",[474,53210,53211],{"class":476,"line":584},[474,53212,53213],{},"customer 4: enters\n",[474,53215,53216],{"class":476,"line":607},[474,53217,53218],{},"barber: sending customer 4 to queue\n",[474,53220,53221],{"class":476,"line":642},[474,53222,53223],{},"customer 4: waiting\n",[474,53225,53226],{"class":476,"line":663},[474,53227,53228],{},"barber: queued customer 4 - length: 1 - free seats: 19\n",[474,53230,53231],{"class":476,"line":694},[474,53232,53233],{},"customer 5: enters\n",[474,53235,53236],{"class":476,"line":700},[474,53237,53238],{},"barber: sending customer 5 to queue\n",[474,53240,53241],{"class":476,"line":913},[474,53242,53243],{},"customer 5: waiting\n",[474,53245,53246],{"class":476,"line":920},[474,53247,53248],{},"barber: queued customer 5 - length: 2 - free seats: 18\n",[474,53250,53251],{"class":476,"line":926},[474,53252,53253],{},"barber: done serving customer 3\n",[474,53255,53256],{"class":476,"line":932},[474,53257,53258],{},"barber: serving customer 4\n",[474,53260,53261],{"class":476,"line":938},[474,53262,53263],{},"customer 3: done - exiting\n",[474,53265,53266],{"class":476,"line":944},[474,53267,53268],{},"customer 4: being served\n",[439,53270,53271,53272,53275,53276,53279],{},"As you can see, customer 4 was added to the queue and is being served right customer 3 is done. But what happens if the\nqueue is full? Let’s set ",[471,53273,53274],{},"waitTime = -> Math.random() * 80"," in ",[471,53277,53278],{},"customer.coffee"," so that there are a few more customers\nentering than leaving.",[464,53281,53283],{"className":16895,"code":53282,"language":16897,"meta":469,"style":469},"\ncustomer 34: enters\nbarber: sending customer 34 to queue\ncustomer 34: waiting\nbarber: queued customer 34 - length: 20 - free seats: 0\ncustomer 35: enters\ncustomer 35: full - exiting\n\n",[471,53284,53285,53289,53294,53299,53304,53309,53314],{"__ignoreMap":469},[474,53286,53287],{"class":476,"line":477},[474,53288,917],{"emptyLinePlaceholder":916},[474,53290,53291],{"class":476,"line":507},[474,53292,53293],{},"customer 34: enters\n",[474,53295,53296],{"class":476,"line":547},[474,53297,53298],{},"barber: sending customer 34 to queue\n",[474,53300,53301],{"class":476,"line":584},[474,53302,53303],{},"customer 34: waiting\n",[474,53305,53306],{"class":476,"line":607},[474,53307,53308],{},"barber: queued customer 34 - length: 20 - free seats: 0\n",[474,53310,53311],{"class":476,"line":642},[474,53312,53313],{},"customer 35: enters\n",[474,53315,53316],{"class":476,"line":663},[474,53317,53318],{},"customer 35: full - exiting\n",[439,53320,53321],{},"New customers are being turned away, as expected. The important thing is that there is no deadlocks and no invalid\nstates, which can be easily checked by reading the log output. Knowing that there is just one callback method being\nexecuted at any point in time is a great help when reasoning about the program.",[439,53323,53324,53325,22768,53328,53331,53332,53335,53336,53339],{},"Conclusion\nThe central primitive is the construct ",[471,53326,53327],{},"replier(send_message, next_state)",[471,53329,53330],{},"replier"," triggers a state transition in\nthe remote system through ",[471,53333,53334],{},"send_message"," and defines the local ",[471,53337,53338],{},"next_state",".\nIf you can model your system as something similar to linked state machines, this concurrency approach is easy to\nimplement and very powerful.",[17894,53341],{},[1024,53343,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":53345},[],[1030,1412],"2013-04-24T12:34:33","CoffeeScript\\nVert.x supports JavaScript through the Rhino JavaScript engine. Although\\nJavaScript is a decent language once you get to know it, I prefer CoffeeScript, a\\nlanguage that compiles to JavaScript. Luckily, vert.x has built-in support for CoffeeScript, so I can use it nearly\\ntransparently. You will only notice the JavaScript under the hood when reading stack traces, which will refer to the\\ncompiled JavaScript file.\\nFor the examples in this blog post, the only thing you need to know a little CoffeeScript:","https://synyx.de/blog/asynchronous-concurrency-with-vert-x-part-2/",{},"/blog/asynchronous-concurrency-with-vert-x-part-2",{"title":52388,"description":53353},"CoffeeScript\nVert.x supports JavaScript through the Rhino JavaScript engine. Although\nJavaScript is a decent language once you get to know it, I prefer CoffeeScript, a\nlanguage that compiles to JavaScript. Luckily, vert.x has built-in support for CoffeeScript, so I can use it nearly\ntransparently. You will only notice the JavaScript under the hood when reading stack traces, which will refer to the\ncompiled JavaScript file.\nFor the examples in this blog post, the only thing you need to know a little CoffeeScript:","asynchronous-concurrency-with-vert-x-part-2","blog/asynchronous-concurrency-with-vert-x-part-2",[53357,33647,15201,45722],"coffeescript","CoffeeScript Vert.x supports JavaScript through the Rhino JavaScript engine. Although JavaScript is a decent language once you get to know it, I prefer CoffeeScript, a language that compiles to JavaScript.…","jAhQ7HOciuqYnRkEMPmBHkaC1DBrMMrda632jXz81ds",{"id":53361,"title":53362,"author":53363,"body":53364,"category":53406,"date":53407,"description":53408,"extension":1034,"link":53409,"meta":53410,"navigation":916,"path":53411,"seo":53412,"slug":53368,"stem":53413,"tags":53414,"teaser":53415,"__hash__":53416},"blog/blog/super-double-senior-expert-software-architect.md","Super Double Senior Expert Software Architect",[312],{"type":432,"value":53365,"toc":53404},[53366,53369,53372,53375,53378,53381,53384,53387,53394],[435,53367,53362],{"id":53368},"super-double-senior-expert-software-architect",[439,53370,53371],{},"Aloha, liebe Freunde des Visitenkarten-Bullshit-Bingos. Ich brauche eure Hülfe! Mich treibt folgende Frage um: „Welche\nTitel soll synyx in Zukunft auf den Visitenkarten und in E-Mail-Signaturen verwenden?“",[439,53373,53374],{},"Wir werden in absehbarer Zeit in ein anderes Büro umziehen. Sprich, eine neue Anschrift haben. Das wiederum bedeutet,\ndass wir unsere alten Visitenkarten entsorgen können und neue benötigen. Also, ein guter Zeitpunkt sich Gedanken über\ndie jeweiligen Titel auf den Visitenkarten zu machen.",[439,53376,53377],{},"Meiner Kenntnis nach hat ein Titel auf der Visitenkarte den Zweck, dem Empfänger Auskunft über die Funktion im\nUnternehmen zu geben bzw. ihm bei der Vorstellung zu helfen, welche Aufgabe und Verantwortung man im Unternehmen hat.\nVor der Funktion findet man sehr häufig die Angabe Junior, Senior, Director oder ähnliches im Titel. Dieser Zusatz zur\nFunktion, soll eine gewisse Kompetenz und Erfahrung suggerieren. Also, ein Senior ist besser/höher/schneller als ein\nJunior und eine Director ist besser/höher/schneller als ein Senior und so weiter.",[439,53379,53380],{},"Und nun mein Problem: An welchen Kriterien mache ich denn fest ob einer das Senior oder Director Level erreicht hat, UND\ndem Titel gerecht wird? Weil, also, ich will ja dem Kunden nicht was unter/über Wert verkaufen. Und, nehmen wir mal an,\nwir hätten die Methode für synyx gefunden, wie wir unsere Kollegen korrekt in Level aufteilen; sind unsere Kriterien die\ngleichen wie die des Kunden oder des Marktbegleiters? Man will ja nicht Äpfel mit Birnen vergleichen.",[439,53382,53383],{},"Da diese Bewertungskriterien ganz individuell und nicht allgemeingültig sind, macht es für uns wahrscheinlich keinen\nSinn Titel mit Angaben zu vermeintlicher Erfahrung oder Kompetenz zu führen. Vielleicht ist sogar die Funktion fraglich?\nZumindest bei unseren Softwareentwicklern, Architekten und Beratern. Denn oftmals haben die Kollegen mehrere Rollen im\nProjekt.",[439,53385,53386],{},"Mich beschleicht auch das Gefühl, dass das Level Senior und die Funktion Architekt mittlerweile inflationär auf\nVisitenkarten zu finden sind. Oder, ist das nur mein Eindruck? Über die Gründe kann man natürlich spekulieren. Ich tippe\nja auf vermeintliche, kaufmännische Vorteile.",[439,53388,53389,53390],{},"Mal sehen, wie sich dieses Thema bei uns entwickelt. Vielleicht gehen wir ja auch, wie so oft, synyx-Style-mäßig vor.\nDas kann Lustig werden! Während dessen freue ich mich über jeden Kommentar, eigene Erfahrungen dazu oder Vorschläge, wie\nwir das handhaben könnten – gerne an ",[1002,53391,53393],{"href":53392},"mailto:rueckert@synyx.de","rueckert@synyx.de",[439,53395,53396,53397,53403],{},"PS: ",[1002,53398,53402],{"href":53399,"rel":53400,"title":53401},"http://www.spiegel.de/karriere/berufsleben/jobtitel-generator-neue-berufsbezeichungen-fuer-angeber-und-aufschneider-a-771682.html",[1006],"Jobtitel-Generator","Den","\nkenne ich bereits 😉",{"title":469,"searchDepth":507,"depth":507,"links":53405},[],[1031],"2013-04-24T10:03:40","Aloha, liebe Freunde des Visitenkarten-Bullshit-Bingos. Ich brauche eure Hülfe! Mich treibt folgende Frage um: „Welche\\nTitel soll synyx in Zukunft auf den Visitenkarten und in E-Mail-Signaturen verwenden?“","https://synyx.de/blog/super-double-senior-expert-software-architect/",{},"/blog/super-double-senior-expert-software-architect",{"title":53362,"description":53371},"blog/super-double-senior-expert-software-architect",[],"Aloha, liebe Freunde des Visitenkarten-Bullshit-Bingos. Ich brauche eure Hülfe! Mich treibt folgende Frage um: „Welche Titel soll synyx in Zukunft auf den Visitenkarten und in E-Mail-Signaturen verwenden?“ Wir werden in…","c72x5-UMIzmyjN_CKj1lBH_TPUnNjxsTAYSMAwnurIQ",{"id":53418,"title":53419,"author":53420,"body":53421,"category":53599,"date":53600,"description":53601,"extension":1034,"link":53602,"meta":53603,"navigation":916,"path":53604,"seo":53605,"slug":53431,"stem":53606,"tags":53607,"teaser":53610,"__hash__":53611},"blog/blog/what-is-an-acceptance-test.md","Acceptance testing at synyx – Part 5",[166],{"type":432,"value":53422,"toc":53596},[53423,53426,53429,53433,53462,53465,53468,53539,53546,53553,53564,53588,53591,53594],[435,53424,53419],{"id":53425},"acceptance-testing-at-synyx-part-5",[439,53427,53428],{},"The last few blogs about acceptance-testing focused on setting up a nice and scalable infrastructure to do testing\nthrough the (web)-GUI using a Selenium grid. Since we’ve got this running now we can go on to topics that focus how we\nwrite these tests. At synyx we try to write our web-tests as “acceptance-tests” so we first take a small dive into\nwhat that is.",[3938,53430,53432],{"id":53431},"what-is-an-acceptance-test","What is an Acceptance Test?",[439,53434,53435,53436,53441,53442,53447,53448,53451,53452,53455,53456,53461],{},"In the first place an acceptance test cares about what is tested, not so much about how this is done. Consider that you\nspecify features for an application, hence you write ",[1002,53437,53440],{"href":53438,"rel":53439},"http://en.wikipedia.org/wiki/User_story",[1006],"user stories"," for them.\nThen you will soon get to the question, when the work at a story is completed. A good approach to define when you are\nreally done with a story is to define some ",[1002,53443,53446],{"href":53444,"rel":53445},"http://scrummethodology.com/scrum-acceptance-criteria/",[1006],"acceptance criteria","\nfor it. Then you check if the application meets these criteria to determine if the story you are working on is done. So\nif a story has the title ",[990,53449,53450],{},"“A shop-item can be added to the shopping cart”"," you probably can define acceptance criterias\npretty easy. One of them could be ",[990,53453,53454],{},"“each item in the shop that is currently available for ordering can be added to the\nusers shopping cart. If the user views his cart afterwards the item is listed there”",". Traditionally these criteria\nmight get tested by the developers themself and later some variation\nof ",[1002,53457,53460],{"href":53458,"rel":53459},"https://web.archive.org/web/20170331070234if_/http://cdn.memegenerator.net/instances/400x/24216149.jpg",[1006],"QA",". But\nit’s preferable if these criteria are tested automatically somewhere in the build pipeline.",[439,53463,53464],{},"Automating acceptance-testing has big advantages over manual testing: Noone has to do the same thing all over again.\nBecause if a human does, he will make mistakes. He will forget to test something and maybe skip over other things. Also,\nmanual tests are boring and take alot of time. And if you get more and more tests by the time you’d have to hire more\nand more people which will cost alot of money. Much more than simply scaling your test-cluster up.",[439,53466,53467],{},"But lets think about this… If you are writing unit tests you probably also write acceptance tests. Yes, some tests are\nrather technical and low-level. Therefore they will not be part of the acceptance criteria of a user story given to the\ndevelopers by the product owner. But some of the tests we write are acceptance-tests “by accident”:",[464,53469,53471],{"className":709,"code":53470,"language":711,"meta":469,"style":469},"\n@Test\npublic void addsAvailableItemsToCart() {\n Item item = new Item(\"synyx coffee mug\");\n item.setAvailable(true);\n cart.add(item);\n Assert.assertThat(cart.getItems(), containsItem(item));\n}\n@Test(expected = ItemUnavailableException.class)\npublic void doesNotAddUnavailableItemsToCart() {\n Item item = new Item(\"synyx coffee mug\");\n item.setAvailable(false);\n cart.add(item); // exception expected\n}\n\n",[471,53472,53473,53477,53482,53487,53492,53497,53502,53507,53511,53516,53521,53525,53530,53535],{"__ignoreMap":469},[474,53474,53475],{"class":476,"line":477},[474,53476,917],{"emptyLinePlaceholder":916},[474,53478,53479],{"class":476,"line":507},[474,53480,53481],{},"@Test\n",[474,53483,53484],{"class":476,"line":547},[474,53485,53486],{},"public void addsAvailableItemsToCart() {\n",[474,53488,53489],{"class":476,"line":584},[474,53490,53491],{}," Item item = new Item(\"synyx coffee mug\");\n",[474,53493,53494],{"class":476,"line":607},[474,53495,53496],{}," item.setAvailable(true);\n",[474,53498,53499],{"class":476,"line":642},[474,53500,53501],{}," cart.add(item);\n",[474,53503,53504],{"class":476,"line":663},[474,53505,53506],{}," Assert.assertThat(cart.getItems(), containsItem(item));\n",[474,53508,53509],{"class":476,"line":694},[474,53510,703],{},[474,53512,53513],{"class":476,"line":700},[474,53514,53515],{},"@Test(expected = ItemUnavailableException.class)\n",[474,53517,53518],{"class":476,"line":913},[474,53519,53520],{},"public void doesNotAddUnavailableItemsToCart() {\n",[474,53522,53523],{"class":476,"line":920},[474,53524,53491],{},[474,53526,53527],{"class":476,"line":926},[474,53528,53529],{}," item.setAvailable(false);\n",[474,53531,53532],{"class":476,"line":932},[474,53533,53534],{}," cart.add(item); // exception expected\n",[474,53536,53537],{"class":476,"line":938},[474,53538,703],{},[439,53540,53541,53542,53545],{},"As you can see the two test methods above could be a way to verify the criteria of the story ",[990,53543,53544],{},"“A shop-item can be added\nto the shopping cart”"," I described above.",[439,53547,53548,53549,53552],{},"But – of course – there are other ways to test the same thing. The test above looks like a unit test (our unit is the\nshopping cart implementation ",[471,53550,53551],{},"Cart.java","). We could also test on service-level or through the GUI. Since the mentioned\nexample is kind of trivial there is no need to test it on another level than the unit-level. But some stories have more\ncomplex acceptance criteria and need more complex tests that have to be tested on higher levels. You might also want to\nbe sure that some components work together to complete a task and you want to test these together.",[439,53554,53555,53556,53559,53560,53563],{},"Acceptance criteria should focus on functional requirements and therefore be without technical details. The criteria\nbelongs directly to the user stories and should be created along with the user stories. Because requirements or stories\nare usually written by non-technical pepole a common language is needed: Business-People have to be able to write them\nand developers or testers have to understand and implement them. This is why I prefer the BDD-Style of specifig\nacceptance criteria. Here you define usage scenarios of the application / the story. The scenario defines ",[448,53557,53558],{},"what"," the\nuser wants to do, how this breaks down to single ",[448,53561,53562],{},"steps",", what preconditons have to be met and what the expected\noutcome of the actions are. So these criteria are often verbalized in given/when/then form:",[464,53565,53567],{"className":16895,"code":53566,"language":16897,"meta":469,"style":469},"\nGiven I look at the details of the item \"synyx coffee mug\"\nWhen I add the item to the shopping cart\nThen there is a \"synyx coffee mug\" in my Cart.\n\n",[471,53568,53569,53573,53578,53583],{"__ignoreMap":469},[474,53570,53571],{"class":476,"line":477},[474,53572,917],{"emptyLinePlaceholder":916},[474,53574,53575],{"class":476,"line":507},[474,53576,53577],{},"Given I look at the details of the item \"synyx coffee mug\"\n",[474,53579,53580],{"class":476,"line":547},[474,53581,53582],{},"When I add the item to the shopping cart\n",[474,53584,53585],{"class":476,"line":584},[474,53586,53587],{},"Then there is a \"synyx coffee mug\" in my Cart.\n",[439,53589,53590],{},"As you can see I wrote the acceptance criteria based on the GUI my webshop has. I think this is much easier for a\nnon-technical person since noone has to focus on services, components and whatever exists in your webshop application\nbut on what the product owner “knows” and sees (or wants to see) when he is using the application.",[439,53592,53593],{},"This is the time it comes in handy to have a selenium-grid at our service. So in the next few posts we are gonna\ndiscuss how to write tests using selenium to do acceptance testing through the GUI. And – of course – we also gonna\ndiscuss how to turn the BDD-Style criteria into code. So again… stay tuned 🙂",[1024,53595,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":53597},[53598],{"id":53431,"depth":507,"text":53432},[1030],"2013-04-18T07:42:31","The last few blogs about acceptance-testing focused on setting up a nice and scalable infrastructure to do testing\\nthrough the (web)-GUI using a Selenium grid. Since we’ve got this running now we can go on to topics that focus how we\\nwrite these tests. At synyx we try to write our web-tests as “acceptance-tests” so we first take a small dive into\\nwhat that is.","https://synyx.de/blog/what-is-an-acceptance-test/",{},"/blog/what-is-an-acceptance-test",{"title":53419,"description":53428},"blog/what-is-an-acceptance-test",[53608,53609,24886,18497,21404],"acceptance-test","attd","The last few blogs about acceptance-testing focused on setting up a nice and scalable infrastructure to do testing through the (web)-GUI using a Selenium grid. Since we’ve got this running…","m_bJhikfuGrMJf27m7nMvsA46csiSIYMdBVJc77JpJI",{"id":53613,"title":53614,"author":53615,"body":53616,"category":53917,"date":53918,"description":53919,"extension":1034,"link":53920,"meta":53921,"navigation":916,"path":53922,"seo":53923,"slug":53925,"stem":53926,"tags":53927,"teaser":53928,"__hash__":53929},"blog/blog/asynchronous-concurrency-with-vert-x-part-1.md","Asynchronous concurrency with vert.x – Part 1",[9],{"type":432,"value":53617,"toc":53913},[53618,53621,53652,53655,53667,53729,53732,53792,53795,53814,53823,53902,53911],[435,53619,53614],{"id":53620},"asynchronous-concurrency-with-vertx-part-1",[439,53622,53623,53624,53629,53630,53633,53634,53637,53638,53641,53642,53645,53646,53651],{},"Event-Driven Concurrency\nAt synyx, we are looking at ",[1002,53625,53628],{"href":53626,"rel":53627},"http://www.vertx.io",[1006],"vert.x"," for an upcoming project where we are building a system that\nwill need to scale under load. The tag-line of vert.x is ",[990,53631,53632],{},"effortless asynchronous application development for the\nmodern web and enterprise",", which fits the bill, so I decided to play around with it a little bit.\nThe advantage of event-driven concurrency compared to traditional technologies is the reduced risk of deadlocks,\nlivelocks and race conditions. Using mutexes and semaphores correctly is extremely difficult and can lead to very subtle\nbugs that are difficult to reproduce. The downside is that information can only be shared by passing messages.\nAnybody who has has used jQuery’s ",[471,53635,53636],{},"$.ajax"," should have some idea of what event-driven concurrency means: an event loop\ntriggers predefined callbacks after a certain event happens. In that case, the system is retrieving the data in the\nbackground, while your JavaScript program can do something else in the meantime, like respond to user events. Once the\ndata has arrived, the callback method is triggered and the data is passed as a function argument – no other callback\nfunction can run simultaneously. The same is true for ",[471,53639,53640],{},"setTimeout",", which is used extensively for animations: adjust the\nproperties of an element a little bit each call, then return to the event loop.\nThis is the reason why there is no ",[471,53643,53644],{},"sleep()"," function in JavaScript – the browser would freeze, the user couldn’t\ninteract with the web page. Each callback method must be short-running.\nWith ",[1002,53647,53650],{"href":53648,"rel":53649},"https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers",[1006],"WebWorkers",", you can now also perform client-side\ncomputation without blocking the main event loop, putting your multi-core CPU to use. The mechanism of communication\nbetween the background task and the main task is the same as with doing asynchronous IO – using callbacks and message\npassing.",[439,53653,53654],{},"vert.x\nVert.x brings this concept to the server side on top of the JVM. It allows writing applications using a event-driven\nconcurrency model. There are bindings for basically every language that runs on top of the JVM: Java, Ruby, Groovy,\nPython and JavaScript. The distributed event bus provides seamless scaling over multiple cores or hosts. You perform a\ncomputation one process, send data via the event bus to another process where a callback method is executed.",[439,53656,53657,53658,53663,53664,6562],{},"Event bus\nA short example in JavaScript that uses the event bus from\nthe ",[1002,53659,53662],{"href":53660,"rel":53661},"https://github.com/vert-x/vert.x/blob/master/vertx-examples/src/main/javascript/eventbus",[1006],"vert.x github repository"," –\ndefine a message handler that simply displays messages sent to the event bus address ",[471,53665,53666],{},"example.address",[464,53668,53670],{"className":16895,"code":53669,"language":16897,"meta":469,"style":469},"\n// file handler.js\nload('vertx.js')\nvar eb = vertx.eventBus;\nvar address = 'example.address'\nvar handler = function(message) {\n stdout.println('Received message ' + message)\n}\neb.registerHandler(address, handler);\nfunction vertxStop() {\n eb.unregisterHandler(address, handler);\n}\n\n",[471,53671,53672,53676,53681,53686,53691,53696,53701,53706,53710,53715,53720,53725],{"__ignoreMap":469},[474,53673,53674],{"class":476,"line":477},[474,53675,917],{"emptyLinePlaceholder":916},[474,53677,53678],{"class":476,"line":507},[474,53679,53680],{},"// file handler.js\n",[474,53682,53683],{"class":476,"line":547},[474,53684,53685],{},"load('vertx.js')\n",[474,53687,53688],{"class":476,"line":584},[474,53689,53690],{},"var eb = vertx.eventBus;\n",[474,53692,53693],{"class":476,"line":607},[474,53694,53695],{},"var address = 'example.address'\n",[474,53697,53698],{"class":476,"line":642},[474,53699,53700],{},"var handler = function(message) {\n",[474,53702,53703],{"class":476,"line":663},[474,53704,53705],{}," stdout.println('Received message ' + message)\n",[474,53707,53708],{"class":476,"line":694},[474,53709,703],{},[474,53711,53712],{"class":476,"line":700},[474,53713,53714],{},"eb.registerHandler(address, handler);\n",[474,53716,53717],{"class":476,"line":913},[474,53718,53719],{},"function vertxStop() {\n",[474,53721,53722],{"class":476,"line":920},[474,53723,53724],{}," eb.unregisterHandler(address, handler);\n",[474,53726,53727],{"class":476,"line":926},[474,53728,703],{},[439,53730,53731],{},"We put the program that sends messages in a different file to achieve process isolation:",[464,53733,53735],{"className":16895,"code":53734,"language":16897,"meta":469,"style":469},"\n// file sender.js\nload('vertx.js')\nvar eb = vertx.eventBus;\nvar address = 'example.address'\nvertx.setPeriodic(2000, sendMessage)\nvar count = 0\nfunction sendMessage() {\n var msg = \"some-message-\" + count++;\n eb.send(address, msg);\n stdout.println(\"sent message \" + msg)\n}\n\n",[471,53736,53737,53741,53746,53750,53754,53758,53763,53768,53773,53778,53783,53788],{"__ignoreMap":469},[474,53738,53739],{"class":476,"line":477},[474,53740,917],{"emptyLinePlaceholder":916},[474,53742,53743],{"class":476,"line":507},[474,53744,53745],{},"// file sender.js\n",[474,53747,53748],{"class":476,"line":547},[474,53749,53685],{},[474,53751,53752],{"class":476,"line":584},[474,53753,53690],{},[474,53755,53756],{"class":476,"line":607},[474,53757,53695],{},[474,53759,53760],{"class":476,"line":642},[474,53761,53762],{},"vertx.setPeriodic(2000, sendMessage)\n",[474,53764,53765],{"class":476,"line":663},[474,53766,53767],{},"var count = 0\n",[474,53769,53770],{"class":476,"line":694},[474,53771,53772],{},"function sendMessage() {\n",[474,53774,53775],{"class":476,"line":700},[474,53776,53777],{}," var msg = \"some-message-\" + count++;\n",[474,53779,53780],{"class":476,"line":913},[474,53781,53782],{}," eb.send(address, msg);\n",[474,53784,53785],{"class":476,"line":920},[474,53786,53787],{}," stdout.println(\"sent message \" + msg)\n",[474,53789,53790],{"class":476,"line":926},[474,53791,703],{},[439,53793,53794],{},"Both programs are then started separately using the vertx runtime. They can then communicate on the event bus via the\nnetwork:",[464,53796,53798],{"className":16895,"code":53797,"language":16897,"meta":469,"style":469},"\n# vertx run handler.js -cluster -cluster-port 10001 &\n# vertx run sender.js -cluster -cluster-port 10002\n\n",[471,53799,53800,53804,53809],{"__ignoreMap":469},[474,53801,53802],{"class":476,"line":477},[474,53803,917],{"emptyLinePlaceholder":916},[474,53805,53806],{"class":476,"line":507},[474,53807,53808],{},"# vertx run handler.js -cluster -cluster-port 10001 &\n",[474,53810,53811],{"class":476,"line":547},[474,53812,53813],{},"# vertx run sender.js -cluster -cluster-port 10002\n",[439,53815,53816,53817,53822],{},"Repliers\nThis described system can be extended by the use\nof ",[1002,53818,53821],{"href":53819,"rel":53820},"http://vertx.io/core_manual_js.html#replying-to-messages",[1006],"repliers",", which can be used to start a dialog between a\nmessage handler and a sender. The sender and the replier both live in the same file this time:",[464,53824,53826],{"className":16895,"code":53825,"language":16897,"meta":469,"style":469},"\nvar vertx = require('vertx'); // alternative import\nvar address = \"example.address\";\nvar handler = function (message, replier) {\n stdout.println(\"sender sent \" + message);\n replier(\"pong 1\", function (message, replier) {\n // and so on\n });\n}\nvertx.eventBus.registerHandler(address, handler);\nvertx.eventBus.send(address, \"ping 1\", function (message, replier) {\n stdout.println(\"handler sent \" + message);\n replier(\"ping 2\", function(message, replier) {\n // and so on\n });\n});\n\n",[471,53827,53828,53832,53837,53842,53847,53852,53857,53862,53866,53870,53875,53880,53885,53890,53894,53898],{"__ignoreMap":469},[474,53829,53830],{"class":476,"line":477},[474,53831,917],{"emptyLinePlaceholder":916},[474,53833,53834],{"class":476,"line":507},[474,53835,53836],{},"var vertx = require('vertx'); // alternative import\n",[474,53838,53839],{"class":476,"line":547},[474,53840,53841],{},"var address = \"example.address\";\n",[474,53843,53844],{"class":476,"line":584},[474,53845,53846],{},"var handler = function (message, replier) {\n",[474,53848,53849],{"class":476,"line":607},[474,53850,53851],{}," stdout.println(\"sender sent \" + message);\n",[474,53853,53854],{"class":476,"line":642},[474,53855,53856],{}," replier(\"pong 1\", function (message, replier) {\n",[474,53858,53859],{"class":476,"line":663},[474,53860,53861],{}," // and so on\n",[474,53863,53864],{"class":476,"line":694},[474,53865,15314],{},[474,53867,53868],{"class":476,"line":700},[474,53869,703],{},[474,53871,53872],{"class":476,"line":913},[474,53873,53874],{},"vertx.eventBus.registerHandler(address, handler);\n",[474,53876,53877],{"class":476,"line":920},[474,53878,53879],{},"vertx.eventBus.send(address, \"ping 1\", function (message, replier) {\n",[474,53881,53882],{"class":476,"line":926},[474,53883,53884],{}," stdout.println(\"handler sent \" + message);\n",[474,53886,53887],{"class":476,"line":932},[474,53888,53889],{}," replier(\"ping 2\", function(message, replier) {\n",[474,53891,53892],{"class":476,"line":938},[474,53893,53861],{},[474,53895,53896],{"class":476,"line":944},[474,53897,15314],{},[474,53899,53900],{"class":476,"line":950},[474,53901,15357],{},[3938,53903,53905,53906,53910],{"id":53904},"every-sent-message-can-be-acknowledged-with-a-reply-by-the-other-side-and-vice-versa-this-concurrency-model-is-very-easy-to-grasp-and-very-powerful-we-will-use-it-in-the-next-part-of-this-series-where-we-tackle-the-sleeping-barber-problem-stay-tuned","Every sent message can be acknowledged with a reply by the other side and vice versa. This concurrency model is very easy to grasp and very powerful. We will use it in the next part of this series, where we tackle the ",[1002,53907,53909],{"href":52551,"rel":53908},[1006],"Sleeping barber problem"," – stay tuned!",[1024,53912,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":53914},[53915],{"id":53904,"depth":507,"text":53916},"Every sent message can be acknowledged with a reply by the other side and vice versa. This concurrency model is very easy to grasp and very powerful. We will use it in the next part of this series, where we tackle the Sleeping barber problem – stay tuned!",[1030,1412],"2013-04-15T20:59:34","Event-Driven Concurrency\\nAt synyx, we are looking at vert.x for an upcoming project where we are building a system that\\nwill need to scale under load. The tag-line of vert.x is effortless asynchronous application development for the\\nmodern web and enterprise, which fits the bill, so I decided to play around with it a little bit.\\nThe advantage of event-driven concurrency compared to traditional technologies is the reduced risk of deadlocks,\\nlivelocks and race conditions. Using mutexes and semaphores correctly is extremely difficult and can lead to very subtle\\nbugs that are difficult to reproduce. The downside is that information can only be shared by passing messages.\\nAnybody who has has used jQuery’s $.ajax should have some idea of what event-driven concurrency means: an event loop\\ntriggers predefined callbacks after a certain event happens. In that case, the system is retrieving the data in the\\nbackground, while your JavaScript program can do something else in the meantime, like respond to user events. Once the\\ndata has arrived, the callback method is triggered and the data is passed as a function argument – no other callback\\nfunction can run simultaneously. The same is true for setTimeout, which is used extensively for animations: adjust the\\nproperties of an element a little bit each call, then return to the event loop.\\nThis is the reason why there is no sleep() function in JavaScript – the browser would freeze, the user couldn’t\\ninteract with the web page. Each callback method must be short-running.\\nWith WebWorkers, you can now also perform client-side\\ncomputation without blocking the main event loop, putting your multi-core CPU to use. The mechanism of communication\\nbetween the background task and the main task is the same as with doing asynchronous IO – using callbacks and message\\npassing.","https://synyx.de/blog/asynchronous-concurrency-with-vert-x-part-1/",{},"/blog/asynchronous-concurrency-with-vert-x-part-1",{"title":53614,"description":53924},"Event-Driven Concurrency\nAt synyx, we are looking at vert.x for an upcoming project where we are building a system that\nwill need to scale under load. The tag-line of vert.x is effortless asynchronous application development for the\nmodern web and enterprise, which fits the bill, so I decided to play around with it a little bit.\nThe advantage of event-driven concurrency compared to traditional technologies is the reduced risk of deadlocks,\nlivelocks and race conditions. Using mutexes and semaphores correctly is extremely difficult and can lead to very subtle\nbugs that are difficult to reproduce. The downside is that information can only be shared by passing messages.\nAnybody who has has used jQuery’s $.ajax should have some idea of what event-driven concurrency means: an event loop\ntriggers predefined callbacks after a certain event happens. In that case, the system is retrieving the data in the\nbackground, while your JavaScript program can do something else in the meantime, like respond to user events. Once the\ndata has arrived, the callback method is triggered and the data is passed as a function argument – no other callback\nfunction can run simultaneously. The same is true for setTimeout, which is used extensively for animations: adjust the\nproperties of an element a little bit each call, then return to the event loop.\nThis is the reason why there is no sleep() function in JavaScript – the browser would freeze, the user couldn’t\ninteract with the web page. Each callback method must be short-running.\nWith WebWorkers, you can now also perform client-side\ncomputation without blocking the main event loop, putting your multi-core CPU to use. The mechanism of communication\nbetween the background task and the main task is the same as with doing asynchronous IO – using callbacks and message\npassing.","asynchronous-concurrency-with-vert-x-part-1","blog/asynchronous-concurrency-with-vert-x-part-1",[33647,15201,45722],"Event-Driven Concurrency At synyx, we are looking at vert.x for an upcoming project where we are building a system that will need to scale under load. The tag-line of vert.x…","DkNmAOE6c6bSo2QAVLXKERm9hchogl5z2xWxAovYy3Q",{"id":53931,"title":53932,"author":53933,"body":53934,"category":54565,"date":54566,"description":54567,"extension":1034,"link":54568,"meta":54569,"navigation":916,"path":54570,"seo":54571,"slug":53938,"stem":54573,"tags":54574,"teaser":54579,"__hash__":54580},"blog/blog/liquibase-our-setup-in-a-larger-scale-project.md","Liquibase: Our setup in a larger scale project",[190],{"type":432,"value":53935,"toc":54555},[53936,53939,53948,53952,53955,53978,53981,53992,53996,54000,54003,54006,54189,54192,54195,54199,54202,54302,54306,54309,54367,54376,54379,54389,54393,54396,54399,54471,54474,54477,54522,54525,54528,54532,54535,54544,54547,54550,54553],[435,53937,53932],{"id":53938},"liquibase-our-setup-in-a-larger-scale-project",[439,53940,53941,53942,53947],{},"In this post, we want to show you our ",[1002,53943,53946],{"href":53944,"rel":53945},"http://www.liquibase.org/",[1006],"Liquibase"," setup in a larger scale project that we’ve\nbeen developing for some time now.",[3938,53949,53951],{"id":53950},"gather-requirements","Gather Requirements",[439,53953,53954],{},"First off, a bit more information about the project and the whole project environment:",[994,53956,53957,53960,53963,53966,53969,53972,53975],{},[997,53958,53959],{},"The software developed in this project consists of different applications",[997,53961,53962],{},"Some applications use the same database, some use different ones",[997,53964,53965],{},"The software runs in multiple branch offices of a company",[997,53967,53968],{},"Not every application runs in every branch office",[997,53970,53971],{},"Some of the data in the databases of the branch offices is the same as in the others, some isn’t",[997,53973,53974],{},"We use maven with profiles to build for the different branch offices, because they need different config",[997,53976,53977],{},"As we took over the project from the company, the applications and also the database already existed for some years",[439,53979,53980],{},"This results in some requirements for our liquibase setup:",[994,53982,53983,53986,53989],{},[997,53984,53985],{},"We need a configuration for each application, because they don’t neccessarily use the same database, and we can’t be\nsure that in every branch office, this setup is the same",[997,53987,53988],{},"We need different configurations for each branch office",[997,53990,53991],{},"We need different liquibase scripts for each branch office for some data, while we need the same scripts for other\ndata",[3938,53993,53995],{"id":53994},"liquibase-setup","Liquibase setup",[1065,53997,53999],{"id":53998},"the-pom-file","The pom file",[439,54001,54002],{},"Because we use maven to build our projects, we also want to use it to build and execute the liquibase scripts. Luckily,\nliquibase brings a maven plugin out of the box. So we created a new maven project and added the liquibase maven plugin\nto it. We configured it to run on the install phase of maven, because we want to preprocess the scripts before they are\nexecuted (to fill in the parameters). The scripts and additional config files will be located in the src/main/resources\nfolder of our project.",[439,54004,54005],{},"As it needs a connection to the database, don’t forget to add the needed database driver dependencies! Also change the\nliquibase artifact corresponding to your database!",[464,54007,54009],{"className":6253,"code":54008,"language":6255,"meta":469,"style":469},"\u003Cbuild>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>org.liquibase\u003C/groupId>\n \u003CartifactId>liquibase-maven-plugin\u003C/artifactId>\n \u003Cversion>2.0.3\u003C/version>\n \u003Cconfiguration>\n \u003CmigrationSqlOutputFile>\n ${project.build.directory}/liquibase/migrate-${projectname.dbName}-${projectname.environment}.sql\n \u003C/migrationSqlOutputFile>\n \u003CpropertyFile>target/classes/liquibase-${projectname.environment}.properties\u003C/propertyFile>\n \u003C/configuration>\n \u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.liquibase.ext\u003C/groupId>\n \u003CartifactId>liquibase-oracle\u003C/artifactId>\n \u003Cversion>1.2.0\u003C/version>\n \u003C/dependency>\n \u003C/dependencies>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>install\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>update\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003C/plugin>\n \u003C/plugins>\n \u003Cresources>\n \u003Cresource>\n \u003Cdirectory>src/main/resources\u003C/directory>\n \u003Cfiltering>true\u003C/filtering>\n \u003C/resource>\n \u003C/resources>\n\u003C/build>\n",[471,54010,54011,54016,54021,54026,54031,54036,54041,54046,54051,54056,54061,54066,54071,54076,54080,54085,54090,54095,54099,54104,54109,54114,54119,54124,54129,54134,54139,54144,54149,54154,54159,54164,54169,54174,54179,54184],{"__ignoreMap":469},[474,54012,54013],{"class":476,"line":477},[474,54014,54015],{},"\u003Cbuild>\n",[474,54017,54018],{"class":476,"line":507},[474,54019,54020],{}," \u003Cplugins>\n",[474,54022,54023],{"class":476,"line":547},[474,54024,54025],{}," \u003Cplugin>\n",[474,54027,54028],{"class":476,"line":584},[474,54029,54030],{}," \u003CgroupId>org.liquibase\u003C/groupId>\n",[474,54032,54033],{"class":476,"line":607},[474,54034,54035],{}," \u003CartifactId>liquibase-maven-plugin\u003C/artifactId>\n",[474,54037,54038],{"class":476,"line":642},[474,54039,54040],{}," \u003Cversion>2.0.3\u003C/version>\n",[474,54042,54043],{"class":476,"line":663},[474,54044,54045],{}," \u003Cconfiguration>\n",[474,54047,54048],{"class":476,"line":694},[474,54049,54050],{}," \u003CmigrationSqlOutputFile>\n",[474,54052,54053],{"class":476,"line":700},[474,54054,54055],{}," ${project.build.directory}/liquibase/migrate-${projectname.dbName}-${projectname.environment}.sql\n",[474,54057,54058],{"class":476,"line":913},[474,54059,54060],{}," \u003C/migrationSqlOutputFile>\n",[474,54062,54063],{"class":476,"line":920},[474,54064,54065],{}," \u003CpropertyFile>target/classes/liquibase-${projectname.environment}.properties\u003C/propertyFile>\n",[474,54067,54068],{"class":476,"line":926},[474,54069,54070],{}," \u003C/configuration>\n",[474,54072,54073],{"class":476,"line":932},[474,54074,54075],{}," \u003Cdependencies>\n",[474,54077,54078],{"class":476,"line":938},[474,54079,50062],{},[474,54081,54082],{"class":476,"line":944},[474,54083,54084],{}," \u003CgroupId>org.liquibase.ext\u003C/groupId>\n",[474,54086,54087],{"class":476,"line":950},[474,54088,54089],{}," \u003CartifactId>liquibase-oracle\u003C/artifactId>\n",[474,54091,54092],{"class":476,"line":956},[474,54093,54094],{}," \u003Cversion>1.2.0\u003C/version>\n",[474,54096,54097],{"class":476,"line":962},[474,54098,50077],{},[474,54100,54101],{"class":476,"line":4876},[474,54102,54103],{}," \u003C/dependencies>\n",[474,54105,54106],{"class":476,"line":4888},[474,54107,54108],{}," \u003Cexecutions>\n",[474,54110,54111],{"class":476,"line":4900},[474,54112,54113],{}," \u003Cexecution>\n",[474,54115,54116],{"class":476,"line":4913},[474,54117,54118],{}," \u003Cphase>install\u003C/phase>\n",[474,54120,54121],{"class":476,"line":4921},[474,54122,54123],{}," \u003Cgoals>\n",[474,54125,54126],{"class":476,"line":4932},[474,54127,54128],{}," \u003Cgoal>update\u003C/goal>\n",[474,54130,54131],{"class":476,"line":4938},[474,54132,54133],{}," \u003C/goals>\n",[474,54135,54136],{"class":476,"line":4946},[474,54137,54138],{}," \u003C/execution>\n",[474,54140,54141],{"class":476,"line":4952},[474,54142,54143],{}," \u003C/executions>\n",[474,54145,54146],{"class":476,"line":4957},[474,54147,54148],{}," \u003C/plugin>\n",[474,54150,54151],{"class":476,"line":4969},[474,54152,54153],{}," \u003C/plugins>\n",[474,54155,54156],{"class":476,"line":4990},[474,54157,54158],{}," \u003Cresources>\n",[474,54160,54161],{"class":476,"line":5001},[474,54162,54163],{}," \u003Cresource>\n",[474,54165,54166],{"class":476,"line":5013},[474,54167,54168],{}," \u003Cdirectory>src/main/resources\u003C/directory>\n",[474,54170,54171],{"class":476,"line":5024},[474,54172,54173],{}," \u003Cfiltering>true\u003C/filtering>\n",[474,54175,54176],{"class":476,"line":5035},[474,54177,54178],{}," \u003C/resource>\n",[474,54180,54181],{"class":476,"line":5047},[474,54182,54183],{}," \u003C/resources>\n",[474,54185,54186],{"class":476,"line":5055},[474,54187,54188],{},"\u003C/build>\n",[439,54190,54191],{},"The resource filtering is needed, because we’ll use placeholders in the liquibase files.",[439,54193,54194],{},"In the configurations, we specify to output the whole sql that is executed by liquibase to be exported to a specific\nfile. Furthermore we use a different configuration file, based on the environment that liquibase is built against. With\nthis, we can specify some configs that are the same for each test server, for each staging server, or for each\nproduction server regardless of the branch, without the need to put it in every maven profile (more on the maven\nprofiles later).",[1065,54196,54198],{"id":54197},"the-folder-structure","The folder structure",[439,54200,54201],{},"As for the liquibase folder structure, we set it up as followed:",[464,54203,54205],{"className":16895,"code":54204,"language":16897,"meta":469,"style":469},"src/main/resources/application1/\n├── changes \u003C-- folder for changes\n├── data \u003C-- folder for the initial data imports\n│ ├── all \u003C-- Liqiubase scripts , that are executed\n│ │ │ for every branch\n│ │ ├── csv \u003C-- CSV files for data imports\n│ │ ├── data-xyz-001.xml \u003C-- liquibase scripts\n│ │ └── data-xyz-002.xml\n│ ├── branchX \u003C-- Branch specific folder. contains scripts and\n│ └── branchY ... csv files, that are specific for this branch\n├── init \u003C-- folder for database init (executed as SYSDBA)\n├── install \u003C-- folder for some more database initialisation.\n│ that can be executed as the actual user,\n│ table creation and stuff\n├── db.changelog.xml \u003C-- Liquibase Changelog that contains references\n│ to the single liquibase scripts\n└── db.init.xml \u003C-- Initial Liquibase Changelog that\n has to be executed as @SYSDBA@\n and sets up the schemas and users (init folder)\n",[471,54206,54207,54212,54217,54222,54227,54232,54237,54242,54247,54252,54257,54262,54267,54272,54277,54282,54287,54292,54297],{"__ignoreMap":469},[474,54208,54209],{"class":476,"line":477},[474,54210,54211],{},"src/main/resources/application1/\n",[474,54213,54214],{"class":476,"line":507},[474,54215,54216],{},"├── changes \u003C-- folder for changes\n",[474,54218,54219],{"class":476,"line":547},[474,54220,54221],{},"├── data \u003C-- folder for the initial data imports\n",[474,54223,54224],{"class":476,"line":584},[474,54225,54226],{},"│ ├── all \u003C-- Liqiubase scripts , that are executed\n",[474,54228,54229],{"class":476,"line":607},[474,54230,54231],{},"│ │ │ for every branch\n",[474,54233,54234],{"class":476,"line":642},[474,54235,54236],{},"│ │ ├── csv \u003C-- CSV files for data imports\n",[474,54238,54239],{"class":476,"line":663},[474,54240,54241],{},"│ │ ├── data-xyz-001.xml \u003C-- liquibase scripts\n",[474,54243,54244],{"class":476,"line":694},[474,54245,54246],{},"│ │ └── data-xyz-002.xml\n",[474,54248,54249],{"class":476,"line":700},[474,54250,54251],{},"│ ├── branchX \u003C-- Branch specific folder. contains scripts and\n",[474,54253,54254],{"class":476,"line":913},[474,54255,54256],{},"│ └── branchY ... csv files, that are specific for this branch\n",[474,54258,54259],{"class":476,"line":920},[474,54260,54261],{},"├── init \u003C-- folder for database init (executed as SYSDBA)\n",[474,54263,54264],{"class":476,"line":926},[474,54265,54266],{},"├── install \u003C-- folder for some more database initialisation.\n",[474,54268,54269],{"class":476,"line":932},[474,54270,54271],{},"│ that can be executed as the actual user,\n",[474,54273,54274],{"class":476,"line":938},[474,54275,54276],{},"│ table creation and stuff\n",[474,54278,54279],{"class":476,"line":944},[474,54280,54281],{},"├── db.changelog.xml \u003C-- Liquibase Changelog that contains references\n",[474,54283,54284],{"class":476,"line":950},[474,54285,54286],{},"│ to the single liquibase scripts\n",[474,54288,54289],{"class":476,"line":956},[474,54290,54291],{},"└── db.init.xml \u003C-- Initial Liquibase Changelog that\n",[474,54293,54294],{"class":476,"line":962},[474,54295,54296],{}," has to be executed as @SYSDBA@\n",[474,54298,54299],{"class":476,"line":4876},[474,54300,54301],{}," and sets up the schemas and users (init folder)\n",[1065,54303,54305],{"id":54304},"the-liquibase-changelog","The liquibase changelog",[439,54307,54308],{},"Here’s an example for the changelog file:",[464,54310,54312],{"className":6253,"code":54311,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\u003CdatabaseChangeLog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/tables.xml\"/>\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/procedures.xml\"/>\n ...\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/all/table_add_column_xyz.xml\"/>\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/${projectname.branch}/adjust_procedure_asd.xml\"/>\n ...\n\u003C/databaseChangeLog>\n",[471,54313,54314,54319,54324,54329,54334,54339,54344,54348,54353,54358,54362],{"__ignoreMap":469},[474,54315,54316],{"class":476,"line":477},[474,54317,54318],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",[474,54320,54321],{"class":476,"line":507},[474,54322,54323],{},"\u003CdatabaseChangeLog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n",[474,54325,54326],{"class":476,"line":547},[474,54327,54328],{}," xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[474,54330,54331],{"class":476,"line":584},[474,54332,54333],{}," xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n",[474,54335,54336],{"class":476,"line":607},[474,54337,54338],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/tables.xml\"/>\n",[474,54340,54341],{"class":476,"line":642},[474,54342,54343],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/procedures.xml\"/>\n",[474,54345,54346],{"class":476,"line":663},[474,54347,743],{},[474,54349,54350],{"class":476,"line":694},[474,54351,54352],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/all/table_add_column_xyz.xml\"/>\n",[474,54354,54355],{"class":476,"line":700},[474,54356,54357],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/${projectname.branch}/adjust_procedure_asd.xml\"/>\n",[474,54359,54360],{"class":476,"line":913},[474,54361,743],{},[474,54363,54364],{"class":476,"line":920},[474,54365,54366],{},"\u003C/databaseChangeLog>\n",[439,54368,54369,54370,54375],{},"As you can see, we have used the ",[448,54371,54372],{},[990,54373,54374],{},"${projectname.branch}"," placeholder in the path of a changelog. The file that is\nreferenced there, has to be added for each of the branches, because this changelog is also used for every branch. This\ncan be somewhat inconvenient in some times, when you only have to add a change to one of the branches, but that should\nnot happen that often. It’s more likely (at least for our case) that you have to adjust the same thing for all branches,\nbut a little differnt, or fill some table with different data.",[439,54377,54378],{},"Also, the right execution order of the scripts is secured this way. Furthermore, we don’t have to create and update one\nchangelog for every branch, where it can easily happen, that one file is left out and it goes through unnoticed. In our\nsetup, if you forget to add a file that’s declared in the changelog, that’s another case, because you will know it as\nsoon as you execute the script for the specific branch. So we considered this to be the best method to address multiple\nbranches.",[439,54380,54381,54382,54385,54386,54388],{},"You can also use the placeholder in other places, like the ",[990,54383,54384],{},"loadUpdateData"," tag, where you can specify a .csv file from\nwhich liquibase will load data. There, You’ll only need to add the changelog to the ‘",[990,54387,38086],{},"‘ folder and the .csv files in\neach branch folder. Furthermore, we are",[1065,54390,54392],{"id":54391},"maven-profiles","maven profiles",[439,54394,54395],{},"To configure and execute liquibase, we use different maven profiles. We need to specify the url, username and password\nfor each server, so we have one profile for each of them. The properties that are the same based on the environment (\ntest, stage, prod), are defined in a config file included from the pom (as already seen above), so we also need to add a\nproperty for the environment in each profile. Like this we can create a liquibase profile for each application of an\nenvironment of a branch (yup, there are quite some profiles because of this, but it is simply needed – you don’t have to\nkeep them in your settings.xml all the time, though, so it isn’t that much of a pain, once they are created 😛 ). By\nsetting the username and password locally in the maven settings.xml, we also keep sure that no passwords are commited in\nour version control.",[439,54397,54398],{},"example profile:",[464,54400,54402],{"className":6253,"code":54401,"language":6255,"meta":469,"style":469},"\n \u003Cprofile>\n \u003Cid>xyz-test\u003C/id>\n \u003Cproperties>\n \u003Cprojectname.branch>xyz\u003C/projectname.branch>\n \u003Cprojectname.environment>test\u003C/projectname.environment>\n \u003Cprojectname.dbName>dbname\u003C/projectname.dbName>\n \u003Cprojectname.liquibase.url>jdbc:oracle:thin:@192.168.224.234:1521:DBID\u003C/projectname.liquibase.url>\n \u003Cprojectname.liquibase.username>username\u003C/projectname.liquibase.username>\n \u003Cprojectname.liquibase.password>password\u003C/projectname.liquibase.password>\n \u003Cprojectname.liquibase.schemaName>schema\u003C/projectname.liquibase.schemaName>\n \u003Cprojectname.liquibase.changeLogFile>target/classes/path/to/changelog/db.changelog.xml\u003C/projectname.liquibase.changeLogFile>\n \u003C/properties>\n \u003C/profile>\n",[471,54403,54404,54408,54413,54418,54422,54427,54432,54437,54442,54447,54452,54457,54462,54466],{"__ignoreMap":469},[474,54405,54406],{"class":476,"line":477},[474,54407,917],{"emptyLinePlaceholder":916},[474,54409,54410],{"class":476,"line":507},[474,54411,54412],{}," \u003Cprofile>\n",[474,54414,54415],{"class":476,"line":547},[474,54416,54417],{}," \u003Cid>xyz-test\u003C/id>\n",[474,54419,54420],{"class":476,"line":584},[474,54421,50130],{},[474,54423,54424],{"class":476,"line":607},[474,54425,54426],{}," \u003Cprojectname.branch>xyz\u003C/projectname.branch>\n",[474,54428,54429],{"class":476,"line":642},[474,54430,54431],{}," \u003Cprojectname.environment>test\u003C/projectname.environment>\n",[474,54433,54434],{"class":476,"line":663},[474,54435,54436],{}," \u003Cprojectname.dbName>dbname\u003C/projectname.dbName>\n",[474,54438,54439],{"class":476,"line":694},[474,54440,54441],{}," \u003Cprojectname.liquibase.url>jdbc:oracle:thin:@192.168.224.234:1521:DBID\u003C/projectname.liquibase.url>\n",[474,54443,54444],{"class":476,"line":700},[474,54445,54446],{}," \u003Cprojectname.liquibase.username>username\u003C/projectname.liquibase.username>\n",[474,54448,54449],{"class":476,"line":913},[474,54450,54451],{}," \u003Cprojectname.liquibase.password>password\u003C/projectname.liquibase.password>\n",[474,54453,54454],{"class":476,"line":920},[474,54455,54456],{}," \u003Cprojectname.liquibase.schemaName>schema\u003C/projectname.liquibase.schemaName>\n",[474,54458,54459],{"class":476,"line":926},[474,54460,54461],{}," \u003Cprojectname.liquibase.changeLogFile>target/classes/path/to/changelog/db.changelog.xml\u003C/projectname.liquibase.changeLogFile>\n",[474,54463,54464],{"class":476,"line":932},[474,54465,50145],{},[474,54467,54468],{"class":476,"line":938},[474,54469,54470],{}," \u003C/profile>\n",[439,54472,54473],{},"With this config, it uses the property file target/classes/liquibase-test.properties (keep in mind, the file initially\nlies in the folder src/main/resources, but because we build the project before we execute liquibase, it is then located\nunder target/classes/ , with its parameters replaced by our properties).",[439,54475,54476],{},"liquibase-test.properties:",[464,54478,54480],{"className":16895,"code":54479,"language":16897,"meta":469,"style":469},"changeLogFile=${projectname.liquibase.changeLogFile}\ndriver=oracle.jdbc.OracleDriver\nurl=${projectname.liquibase.url}\nusername=${projectname.liquibase.username}\npassword=${projectname.liquibase.password}\ndefaultSchemaName=${projectname.liquibase.schemaName}\nverbose=true\ndropFirst=false\n",[471,54481,54482,54487,54492,54497,54502,54507,54512,54517],{"__ignoreMap":469},[474,54483,54484],{"class":476,"line":477},[474,54485,54486],{},"changeLogFile=${projectname.liquibase.changeLogFile}\n",[474,54488,54489],{"class":476,"line":507},[474,54490,54491],{},"driver=oracle.jdbc.OracleDriver\n",[474,54493,54494],{"class":476,"line":547},[474,54495,54496],{},"url=${projectname.liquibase.url}\n",[474,54498,54499],{"class":476,"line":584},[474,54500,54501],{},"username=${projectname.liquibase.username}\n",[474,54503,54504],{"class":476,"line":607},[474,54505,54506],{},"password=${projectname.liquibase.password}\n",[474,54508,54509],{"class":476,"line":642},[474,54510,54511],{},"defaultSchemaName=${projectname.liquibase.schemaName}\n",[474,54513,54514],{"class":476,"line":663},[474,54515,54516],{},"verbose=true\n",[474,54518,54519],{"class":476,"line":694},[474,54520,54521],{},"dropFirst=false\n",[439,54523,54524],{},"Here we map our properties from the profiles to the actual liquibase property names and also set a few other liquibase\nconfigs.",[439,54526,54527],{},"For scripts you need to execute in another schema as the one the db user has set as the default schema, we also set the\ndefaultSchemaName property of liquibase (mainly the case, if we execute scripts as the SYSDBA user).",[3938,54529,54531],{"id":54530},"execution-conclusion","Execution & Conclusion",[439,54533,54534],{},"Because of the use of maven, we can execute all of the changes from our local machines very easy:",[464,54536,54538],{"className":16895,"code":54537,"language":16897,"meta":469,"style":469},"mvn clean install -Pxyz-test\n",[471,54539,54540],{"__ignoreMap":469},[474,54541,54542],{"class":476,"line":477},[474,54543,54537],{},[439,54545,54546],{},"If you connect against a remote server, you are even warned with a dialogue that contains the database name, url and\nusername, it wants to execute the scripts on, before the scripts are actually executed. So you can check them again and\nabort the migration if you used the wrong profile.",[439,54548,54549],{},"With this setup we can now add scripts for only one branch, multiple branches, or all branches, without having to worry\nto forget to add one change to a branch and leaving the error unnoticed. Even if we forget to put some file in the\nfolder of one branch, our changelog file is global for all branches! So if we try to execute it the next time, liquibase\nnotices the missing file and informs us about this (and aborts the execution). And because we don’t have different\nfolders for the environments, but only the branches, this gets noticed on the test machines.",[439,54551,54552],{},"Please let us know what you think of our approach and if you know an even better one!",[1024,54554,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":54556},[54557,54558,54564],{"id":53950,"depth":507,"text":53951},{"id":53994,"depth":507,"text":53995,"children":54559},[54560,54561,54562,54563],{"id":53998,"depth":547,"text":53999},{"id":54197,"depth":547,"text":54198},{"id":54304,"depth":547,"text":54305},{"id":54391,"depth":547,"text":54392},{"id":54530,"depth":507,"text":54531},[1030],"2013-04-12T11:19:55","In this post, we want to show you our Liquibase setup in a larger scale project that we’ve\\nbeen developing for some time now.","https://synyx.de/blog/liquibase-our-setup-in-a-larger-scale-project/",{},"/blog/liquibase-our-setup-in-a-larger-scale-project",{"title":53932,"description":54572},"In this post, we want to show you our Liquibase setup in a larger scale project that we’ve\nbeen developing for some time now.","blog/liquibase-our-setup-in-a-larger-scale-project",[54575,54576,54577,54578],"database","database-change-management","database-migration","liquibase","In this post, we want to show you our Liquibase setup in a larger scale project that we’ve been developing for some time now. Gather Requirements First off, a bit…","Edhd0rsE8wWORJZowAN7nLiXbUivWvpaqbjev0vh3MQ",{"id":54582,"title":54583,"author":54584,"body":54585,"category":56684,"date":56685,"description":469,"extension":1034,"link":56686,"meta":56687,"navigation":916,"path":56688,"seo":56689,"slug":54589,"stem":56690,"tags":56691,"teaser":56695,"__hash__":56696},"blog/blog/continuous-deployment-automatic-backup-script.md","Continuous Deployment – Automatic Backup Script",[30],{"type":432,"value":54586,"toc":56666},[54587,54590,54594,54609,54613,54622,54631,54634,54637,54648,54655,54659,54680,54683,54718,54721,54730,54783,54797,54803,54817,54821,54824,54844,55041,55045,55048,55052,55055,55069,55076,55183,55187,55190,55504,55508,55511,55719,55723,55729,55738,55741,55775,55778,55787,55790,55944,55948,55951,55962,55972,56212,56216,56222,56238,56255,56269,56436,56440,56443,56462,56614,56625,56629,56638,56642,56645,56649,56656,56663],[435,54588,54583],{"id":54589},"continuous-deployment-automatic-backup-script",[3938,54591,54593],{"id":54592},"a-few-words-about-continuous-deployment","A few words about Continuous Deployment",[11947,54595,54596,54599,54602],{},[439,54597,54598],{},"Continuous Deployment is the deployment or release of code to Production as soon as it is ready. (…) The automated\nprocess is key because it should be able to be performed by anyone in a matter of minutes (preferably by the press of a\nbutton).",[439,54600,54601],{},"Once you have moved to a Continuous Deployment process, you will have to have several pieces of automation in place.\nYou must automate your Continuous Integration Build Server and Continuous Delivery to Staging, as well as have the\nability to automatically deploy to Production.",[439,54603,54604],{},[1002,54605,54608],{"href":54606,"rel":54607,"title":29943},"http://blog.assembla.com/assemblablog/tabid/12618/bid/92411/Continuous-Delivery-vs-Continuous-Deployment-vs-Continuous-Integration-Wait-huh.aspx",[1006],"Read full blog post",[3938,54610,54612],{"id":54611},"were-on-the-way","…we’re on the way",[439,54614,54615,54616,54621],{},"Because every manual step implies a risk for failure, our goal is to minimize such risks as well as our amount of work\nby automating our processes of software delivery. In several of our applications we already use a deployment script for\nautomatic deployment of ",[1002,54617,54620],{"href":54618,"rel":54619,"title":54620},"http://tomcat.apache.org/",[1006],"Tomcat"," applications.",[439,54623,54624,54625,54630],{},"For some applications we even use a continuous deployment script triggered by crontab (e.g. every hour) to check\nif ",[1002,54626,54629],{"href":54627,"rel":54628,"title":54629},"http://www.sonatype.org/nexus/",[1006],"Nexus"," has a new version of the application and if so to fetch and deploy it\nautomatically.",[439,54632,54633],{},"However the backup process was still manual – although you always perform the same steps. This is a perfect opportunity\nfor automation. And a perfect opportunity for me to leave the Java world for a while and to learn more about shell\nscripting.",[439,54635,54636],{},"The typical backup steps before a deployment are:",[994,54638,54639,54642,54645],{},[997,54640,54641],{},"saving war file resp. information about the current deployed version",[997,54643,54644],{},"saving database data in a dump",[997,54646,54647],{},"saving directories and/or files, e.g. generated error reports",[439,54649,54650,54651,1402],{},"This blog post explains the configuration and the most important steps of the backup script. You can find the entire\nbackup script and the configuration files on ",[1002,54652,27413],{"href":54653,"rel":54654,"title":27413},"https://github.com/murygina/automated-backup",[1006],[3938,54656,54658],{"id":54657},"configuration-for-mysqldump","Configuration for mysqldump",[439,54660,22944,54661,54664,54665,54668,54669,54672,54673,54676,54677,54679],{},[471,54662,54663],{},"[mysqldump](http://dev.mysql.com/doc/refman/5.1/de/mysqldump.html \"mysqldump\")"," client is the usual command line\ntool to dump a database for backup or for transferring the database data. The created dump contains SQL statements to\ncreate and/or to populate table(s). To be able to execute ",[471,54666,54667],{},"mysqldump"," in an automatic process with nobody around to\nenter the password, you have to provide the access data in a file. Create a ",[471,54670,54671],{},".ini","-style file named ",[471,54674,54675],{},".my.cnf"," providing\nuser and password information. Later in the script, ",[471,54678,54667],{}," will read this configuration and will automatically use\nthe supplied user name and password.",[439,54681,54682],{},"If you have only one database to be dumped, following lines are enough:",[464,54684,54688],{"className":54685,"code":54686,"language":54687,"meta":469,"style":469},"language-shell shiki shiki-themes github-light github-dark","\n[mysqldump]\n user = myUser\n password = xxx\n\n","shell",[471,54689,54690,54694,54699,54708],{"__ignoreMap":469},[474,54691,54692],{"class":476,"line":477},[474,54693,917],{"emptyLinePlaceholder":916},[474,54695,54696],{"class":476,"line":507},[474,54697,54698],{"class":503},"[mysqldump]\n",[474,54700,54701,54703,54705],{"class":476,"line":547},[474,54702,601],{"class":480},[474,54704,1661],{"class":484},[474,54706,54707],{"class":484}," myUser\n",[474,54709,54710,54713,54715],{"class":476,"line":584},[474,54711,54712],{"class":480}," password",[474,54714,1661],{"class":484},[474,54716,54717],{"class":484}," xxx\n",[439,54719,54720],{},"If you have more than one database to be dumped needing different access data, you have to specify an access data\nsection for every database.",[439,54722,54723,54724,18175,54727,1402],{},"For example, if you want the databases “foo” and “bar” to be dumped, you’ll need two sections with the corresponding\ndatabase name as suffix: ",[471,54725,54726],{},"[mysqldumpfoo]",[471,54728,54729],{},"[mysqldumpbar]",[464,54731,54733],{"className":54685,"code":54732,"language":54687,"meta":469,"style":469},"\n[mysqldumpfoo]\n user = fooUser\n password = xxx\n[mysqldumpbar]\n user = barUser\n password = xxx\n\n",[471,54734,54735,54739,54744,54753,54761,54766,54775],{"__ignoreMap":469},[474,54736,54737],{"class":476,"line":477},[474,54738,917],{"emptyLinePlaceholder":916},[474,54740,54741],{"class":476,"line":507},[474,54742,54743],{"class":503},"[mysqldumpfoo]\n",[474,54745,54746,54748,54750],{"class":476,"line":547},[474,54747,601],{"class":480},[474,54749,1661],{"class":484},[474,54751,54752],{"class":484}," fooUser\n",[474,54754,54755,54757,54759],{"class":476,"line":584},[474,54756,54712],{"class":480},[474,54758,1661],{"class":484},[474,54760,54717],{"class":484},[474,54762,54763],{"class":476,"line":607},[474,54764,54765],{"class":503},"[mysqldumpbar]\n",[474,54767,54768,54770,54772],{"class":476,"line":642},[474,54769,601],{"class":480},[474,54771,1661],{"class":484},[474,54773,54774],{"class":484}," barUser\n",[474,54776,54777,54779,54781],{"class":476,"line":663},[474,54778,54712],{"class":480},[474,54780,1661],{"class":484},[474,54782,54717],{"class":484},[439,54784,54785,54788,54789,54792,54793,54796],{},[448,54786,54787],{},"Caution:"," In this case there ",[448,54790,54791],{},"must not"," be a ",[471,54794,54795],{},"[mysqldump]"," section since the more specific sections with database\nname as suffix would be ignored.",[439,54798,54799,54800,54802],{},"If you want to prevent other users from reading your ",[471,54801,54675],{},", change the file permission so that only the owner has\nfull read and write permissions.",[464,54804,54806],{"className":16895,"code":54805,"language":16897,"meta":469,"style":469},"\nchmod 600 ~/.my.cnf\n\n",[471,54807,54808,54812],{"__ignoreMap":469},[474,54809,54810],{"class":476,"line":477},[474,54811,917],{"emptyLinePlaceholder":916},[474,54813,54814],{"class":476,"line":507},[474,54815,54816],{},"chmod 600 ~/.my.cnf\n",[3938,54818,54820],{"id":54819},"configuration-for-backup-script","Configuration for Backup script",[439,54822,54823],{},"The backup script will depend on a configuration file containing details for the backup process:",[994,54825,54826,54829,54832,54835,54838,54841],{},[997,54827,54828],{},"the absolute path of the deployed webapp",[997,54830,54831],{},"the absolute path of the parent directory where the backups should be stored",[997,54833,54834],{},"the names of the databases to be dumped",[997,54836,54837],{},"the absolute path of the mysqldump config file with access data",[997,54839,54840],{},"if the deployed war file itself or if only the information about the current deployed version should be saved",[997,54842,54843],{},"if there are files and/or directories that should be moved or copied to the backup directory",[464,54845,54847],{"className":54685,"code":54846,"language":54687,"meta":469,"style":469},"\n# absolute path of the deployed webapp\nWEBAPP_PATH=\"$HOME/tomcat/webapps/ROOT\"\n# absolute path where the backups should be stored\nBACKUP_PATH=\"$HOME/backup\"\n# names of the databases to be dumped\n# separate with whitespace\nDB_NAME=\"db1 db2 db3\"\n# absolute path of the mysql config file with user data\n# for the above specified databases\nMYSQL_CONF=\"$HOME/.my.cnf\"\n# decide if current deployed war should be saved in backup dir\n# or if only the information about the version that is deployed\n# should be saved\n#\n# 0: save war only if the current deployed version is a snapshot\n# 1: always save war\n#\n# if you specify nothing or something that is not 0 or 1,\n# current deployed war file won't be saved\nWAR=1\n# specifiy folders and/or files (absolute path!)\n# that should be copied resp. moved into backup dir\n# separate with whitespace\nCONTENT_TO_BE_COPIED=\"$HOME/file $HOME/folder\"\nCONTENT_TO_BE_MOVED=\"$HOME/folder/file $HOME/folder/*\"\n\n",[471,54848,54849,54853,54858,54873,54878,54892,54897,54902,54912,54917,54922,54936,54941,54946,54951,54955,54960,54965,54969,54974,54979,54989,54994,54999,55003,55022],{"__ignoreMap":469},[474,54850,54851],{"class":476,"line":477},[474,54852,917],{"emptyLinePlaceholder":916},[474,54854,54855],{"class":476,"line":507},[474,54856,54857],{"class":2277},"# absolute path of the deployed webapp\n",[474,54859,54860,54863,54865,54867,54870],{"class":476,"line":547},[474,54861,54862],{"class":503},"WEBAPP_PATH",[474,54864,811],{"class":810},[474,54866,2289],{"class":484},[474,54868,54869],{"class":503},"$HOME",[474,54871,54872],{"class":484},"/tomcat/webapps/ROOT\"\n",[474,54874,54875],{"class":476,"line":584},[474,54876,54877],{"class":2277},"# absolute path where the backups should be stored\n",[474,54879,54880,54883,54885,54887,54889],{"class":476,"line":607},[474,54881,54882],{"class":503},"BACKUP_PATH",[474,54884,811],{"class":810},[474,54886,2289],{"class":484},[474,54888,54869],{"class":503},[474,54890,54891],{"class":484},"/backup\"\n",[474,54893,54894],{"class":476,"line":642},[474,54895,54896],{"class":2277},"# names of the databases to be dumped\n",[474,54898,54899],{"class":476,"line":663},[474,54900,54901],{"class":2277},"# separate with whitespace\n",[474,54903,54904,54907,54909],{"class":476,"line":694},[474,54905,54906],{"class":503},"DB_NAME",[474,54908,811],{"class":810},[474,54910,54911],{"class":484},"\"db1 db2 db3\"\n",[474,54913,54914],{"class":476,"line":700},[474,54915,54916],{"class":2277},"# absolute path of the mysql config file with user data\n",[474,54918,54919],{"class":476,"line":913},[474,54920,54921],{"class":2277},"# for the above specified databases\n",[474,54923,54924,54927,54929,54931,54933],{"class":476,"line":920},[474,54925,54926],{"class":503},"MYSQL_CONF",[474,54928,811],{"class":810},[474,54930,2289],{"class":484},[474,54932,54869],{"class":503},[474,54934,54935],{"class":484},"/.my.cnf\"\n",[474,54937,54938],{"class":476,"line":926},[474,54939,54940],{"class":2277},"# decide if current deployed war should be saved in backup dir\n",[474,54942,54943],{"class":476,"line":932},[474,54944,54945],{"class":2277},"# or if only the information about the version that is deployed\n",[474,54947,54948],{"class":476,"line":938},[474,54949,54950],{"class":2277},"# should be saved\n",[474,54952,54953],{"class":476,"line":944},[474,54954,40924],{"class":2277},[474,54956,54957],{"class":476,"line":950},[474,54958,54959],{"class":2277},"# 0: save war only if the current deployed version is a snapshot\n",[474,54961,54962],{"class":476,"line":956},[474,54963,54964],{"class":2277},"# 1: always save war\n",[474,54966,54967],{"class":476,"line":962},[474,54968,40924],{"class":2277},[474,54970,54971],{"class":476,"line":4876},[474,54972,54973],{"class":2277},"# if you specify nothing or something that is not 0 or 1,\n",[474,54975,54976],{"class":476,"line":4888},[474,54977,54978],{"class":2277},"# current deployed war file won't be saved\n",[474,54980,54981,54984,54986],{"class":476,"line":4900},[474,54982,54983],{"class":503},"WAR",[474,54985,811],{"class":810},[474,54987,54988],{"class":484},"1\n",[474,54990,54991],{"class":476,"line":4913},[474,54992,54993],{"class":2277},"# specifiy folders and/or files (absolute path!)\n",[474,54995,54996],{"class":476,"line":4921},[474,54997,54998],{"class":2277},"# that should be copied resp. moved into backup dir\n",[474,55000,55001],{"class":476,"line":4932},[474,55002,54901],{"class":2277},[474,55004,55005,55008,55010,55012,55014,55017,55019],{"class":476,"line":4938},[474,55006,55007],{"class":503},"CONTENT_TO_BE_COPIED",[474,55009,811],{"class":810},[474,55011,2289],{"class":484},[474,55013,54869],{"class":503},[474,55015,55016],{"class":484},"/file ",[474,55018,54869],{"class":503},[474,55020,55021],{"class":484},"/folder\"\n",[474,55023,55024,55027,55029,55031,55033,55036,55038],{"class":476,"line":4946},[474,55025,55026],{"class":503},"CONTENT_TO_BE_MOVED",[474,55028,811],{"class":810},[474,55030,2289],{"class":484},[474,55032,54869],{"class":503},[474,55034,55035],{"class":484},"/folder/file ",[474,55037,54869],{"class":503},[474,55039,55040],{"class":484},"/folder/*\"\n",[3938,55042,55044],{"id":55043},"the-backup-script","The backup script",[439,55046,55047],{},"Now let’s have a look at the actual backup script and the most important steps of it.",[1065,55049,55051],{"id":55050},"get-configuration-file","Get configuration file",[439,55053,55054],{},"You can call the script with the option -c to specify which configuration file should be used.",[464,55056,55058],{"className":16895,"code":55057,"language":16897,"meta":469,"style":469},"\n./backup.sh -c config/custom.conf\n\n",[471,55059,55060,55064],{"__ignoreMap":469},[474,55061,55062],{"class":476,"line":477},[474,55063,917],{"emptyLinePlaceholder":916},[474,55065,55066],{"class":476,"line":507},[474,55067,55068],{},"./backup.sh -c config/custom.conf\n",[439,55070,55071,55072,55075],{},"If you call the script without the -c option, the script tries to get a file named ",[471,55073,55074],{},"backup.conf"," in the directory the\nscript is located.",[464,55077,55079],{"className":54685,"code":55078,"language":54687,"meta":469,"style":469},"\n# default config file\nCONF_FILE=\"$(dirname \"$0\")/backup.conf\"\n# a colon after the option means that the option\n# expects an argument\nwhile getopts \"hc:\" OPTION; do\ncase \"$OPTION\" in\nc)\nCONF_FILE=$OPTARG\n;;\nesac\ndone\n\n",[471,55080,55081,55085,55090,55111,55116,55121,55139,55154,55161,55170,55174,55179],{"__ignoreMap":469},[474,55082,55083],{"class":476,"line":477},[474,55084,917],{"emptyLinePlaceholder":916},[474,55086,55087],{"class":476,"line":507},[474,55088,55089],{"class":2277},"# default config file\n",[474,55091,55092,55095,55097,55100,55103,55105,55108],{"class":476,"line":547},[474,55093,55094],{"class":503},"CONF_FILE",[474,55096,811],{"class":810},[474,55098,55099],{"class":484},"\"$(",[474,55101,55102],{"class":480},"dirname",[474,55104,2720],{"class":484},[474,55106,55107],{"class":510},"$0",[474,55109,55110],{"class":484},"\")/backup.conf\"\n",[474,55112,55113],{"class":476,"line":584},[474,55114,55115],{"class":2277},"# a colon after the option means that the option\n",[474,55117,55118],{"class":476,"line":607},[474,55119,55120],{"class":2277},"# expects an argument\n",[474,55122,55123,55125,55128,55131,55134,55137],{"class":476,"line":642},[474,55124,13497],{"class":810},[474,55126,55127],{"class":510}," getopts",[474,55129,55130],{"class":484}," \"hc:\"",[474,55132,55133],{"class":484}," OPTION",[474,55135,55136],{"class":503},"; ",[474,55138,2701],{"class":810},[474,55140,55141,55144,55146,55149,55151],{"class":476,"line":663},[474,55142,55143],{"class":810},"case",[474,55145,2720],{"class":484},[474,55147,55148],{"class":503},"$OPTION",[474,55150,2289],{"class":484},[474,55152,55153],{"class":810}," in\n",[474,55155,55156,55159],{"class":476,"line":694},[474,55157,27189],{"class":55158},"sA_wV",[474,55160,1101],{"class":810},[474,55162,55163,55165,55167],{"class":476,"line":700},[474,55164,55094],{"class":503},[474,55166,811],{"class":810},[474,55168,55169],{"class":503},"$OPTARG\n",[474,55171,55172],{"class":476,"line":913},[474,55173,24122],{"class":503},[474,55175,55176],{"class":476,"line":920},[474,55177,55178],{"class":810},"esac\n",[474,55180,55181],{"class":476,"line":926},[474,55182,2879],{"class":810},[1065,55184,55186],{"id":55185},"read-and-validate-configuration-file","Read and validate configuration file",[439,55188,55189],{},"The script makes sure that the configuration file exists and contains all the required parameters. Otherwise it will\nfail with an appropriate error message.",[464,55191,55193],{"className":54685,"code":55192,"language":54687,"meta":469,"style":469},"\n# variable is empty\nif [ -z \"$CONF_FILE\" ]; then\n echo \"CONF_FILE not set\"\n exit 1\nfi\n# no file found for the given variable\nif [ ! -f \"$CONF_FILE\" ]; then\n echo \"No configuration file found\"\n exit 1\nfi\n# read in variables of conf file\n. \"$CONF_FILE\"\n# make sure that conf file contains all required variables\nif [ -z \"$WEBAPP_PATH\" ] || [ -z \"$BACKUP_PATH\" ] || [ -z \"$DB_NAME\" ] || [ -z \"$MYSQL_CONF\" ]; then\n echo \"Configuration file seems to be incorrect: required variables missing. Please check your config file: $(readlink -f \"$CONF_FILE\")\"\n exit 1\nfi\n# make sure that WEBAPP_PATH exists\nif [ ! -d \"$WEBAPP_PATH\" ]; then\n echo \"The given webapp path doesn't exist. Please check your config file: $(readlink -f \"$CONF_FILE\")\"\n exit 1\nfi\n# make sure that mysql config file exists\nif [ ! -f \"$MYSQL_CONF\" ]; then\n echo \"The given mysql config file doesn't exist. Please check your config file: $(readlink -f \"$CONF_FILE\")\"\n exit 1\nfi\n\n",[471,55194,55195,55199,55204,55224,55231,55238,55242,55247,55268,55275,55281,55285,55290,55300,55305,55371,55390,55396,55400,55405,55425,55442,55448,55452,55457,55477,55494,55500],{"__ignoreMap":469},[474,55196,55197],{"class":476,"line":477},[474,55198,917],{"emptyLinePlaceholder":916},[474,55200,55201],{"class":476,"line":507},[474,55202,55203],{"class":2277},"# variable is empty\n",[474,55205,55206,55208,55210,55213,55215,55218,55220,55222],{"class":476,"line":547},[474,55207,2283],{"class":810},[474,55209,2286],{"class":503},[474,55211,55212],{"class":810},"-z",[474,55214,2720],{"class":484},[474,55216,55217],{"class":503},"$CONF_FILE",[474,55219,2289],{"class":484},[474,55221,2339],{"class":503},[474,55223,2308],{"class":810},[474,55225,55226,55228],{"class":476,"line":584},[474,55227,2735],{"class":510},[474,55229,55230],{"class":484}," \"CONF_FILE not set\"\n",[474,55232,55233,55236],{"class":476,"line":607},[474,55234,55235],{"class":510}," exit",[474,55237,6824],{"class":510},[474,55239,55240],{"class":476,"line":642},[474,55241,2354],{"class":810},[474,55243,55244],{"class":476,"line":663},[474,55245,55246],{"class":2277},"# no file found for the given variable\n",[474,55248,55249,55251,55253,55255,55258,55260,55262,55264,55266],{"class":476,"line":694},[474,55250,2283],{"class":810},[474,55252,2286],{"class":503},[474,55254,18773],{"class":810},[474,55256,55257],{"class":810}," -f",[474,55259,2720],{"class":484},[474,55261,55217],{"class":503},[474,55263,2289],{"class":484},[474,55265,2339],{"class":503},[474,55267,2308],{"class":810},[474,55269,55270,55272],{"class":476,"line":700},[474,55271,2735],{"class":510},[474,55273,55274],{"class":484}," \"No configuration file found\"\n",[474,55276,55277,55279],{"class":476,"line":913},[474,55278,55235],{"class":510},[474,55280,6824],{"class":510},[474,55282,55283],{"class":476,"line":920},[474,55284,2354],{"class":810},[474,55286,55287],{"class":476,"line":926},[474,55288,55289],{"class":2277},"# read in variables of conf file\n",[474,55291,55292,55294,55296,55298],{"class":476,"line":932},[474,55293,1402],{"class":510},[474,55295,2720],{"class":484},[474,55297,55217],{"class":503},[474,55299,2744],{"class":484},[474,55301,55302],{"class":476,"line":938},[474,55303,55304],{"class":2277},"# make sure that conf file contains all required variables\n",[474,55306,55307,55309,55311,55313,55315,55318,55320,55323,55326,55328,55330,55332,55335,55337,55339,55341,55343,55345,55347,55350,55352,55354,55356,55358,55360,55362,55365,55367,55369],{"class":476,"line":944},[474,55308,2283],{"class":810},[474,55310,2286],{"class":503},[474,55312,55212],{"class":810},[474,55314,2720],{"class":484},[474,55316,55317],{"class":503},"$WEBAPP_PATH",[474,55319,2289],{"class":484},[474,55321,55322],{"class":503}," ] ",[474,55324,55325],{"class":810},"||",[474,55327,2286],{"class":503},[474,55329,55212],{"class":810},[474,55331,2720],{"class":484},[474,55333,55334],{"class":503},"$BACKUP_PATH",[474,55336,2289],{"class":484},[474,55338,55322],{"class":503},[474,55340,55325],{"class":810},[474,55342,2286],{"class":503},[474,55344,55212],{"class":810},[474,55346,2720],{"class":484},[474,55348,55349],{"class":503},"$DB_NAME",[474,55351,2289],{"class":484},[474,55353,55322],{"class":503},[474,55355,55325],{"class":810},[474,55357,2286],{"class":503},[474,55359,55212],{"class":810},[474,55361,2720],{"class":484},[474,55363,55364],{"class":503},"$MYSQL_CONF",[474,55366,2289],{"class":484},[474,55368,2339],{"class":503},[474,55370,2308],{"class":810},[474,55372,55373,55375,55378,55381,55383,55385,55387],{"class":476,"line":950},[474,55374,2735],{"class":510},[474,55376,55377],{"class":484}," \"Configuration file seems to be incorrect: required variables missing. Please check your config file: $(",[474,55379,55380],{"class":480},"readlink",[474,55382,55257],{"class":510},[474,55384,2720],{"class":484},[474,55386,55217],{"class":503},[474,55388,55389],{"class":484},"\")\"\n",[474,55391,55392,55394],{"class":476,"line":956},[474,55393,55235],{"class":510},[474,55395,6824],{"class":510},[474,55397,55398],{"class":476,"line":962},[474,55399,2354],{"class":810},[474,55401,55402],{"class":476,"line":4876},[474,55403,55404],{"class":2277},"# make sure that WEBAPP_PATH exists\n",[474,55406,55407,55409,55411,55413,55415,55417,55419,55421,55423],{"class":476,"line":4888},[474,55408,2283],{"class":810},[474,55410,2286],{"class":503},[474,55412,18773],{"class":810},[474,55414,2616],{"class":810},[474,55416,2720],{"class":484},[474,55418,55317],{"class":503},[474,55420,2289],{"class":484},[474,55422,2339],{"class":503},[474,55424,2308],{"class":810},[474,55426,55427,55429,55432,55434,55436,55438,55440],{"class":476,"line":4900},[474,55428,2735],{"class":510},[474,55430,55431],{"class":484}," \"The given webapp path doesn't exist. Please check your config file: $(",[474,55433,55380],{"class":480},[474,55435,55257],{"class":510},[474,55437,2720],{"class":484},[474,55439,55217],{"class":503},[474,55441,55389],{"class":484},[474,55443,55444,55446],{"class":476,"line":4913},[474,55445,55235],{"class":510},[474,55447,6824],{"class":510},[474,55449,55450],{"class":476,"line":4921},[474,55451,2354],{"class":810},[474,55453,55454],{"class":476,"line":4932},[474,55455,55456],{"class":2277},"# make sure that mysql config file exists\n",[474,55458,55459,55461,55463,55465,55467,55469,55471,55473,55475],{"class":476,"line":4938},[474,55460,2283],{"class":810},[474,55462,2286],{"class":503},[474,55464,18773],{"class":810},[474,55466,55257],{"class":810},[474,55468,2720],{"class":484},[474,55470,55364],{"class":503},[474,55472,2289],{"class":484},[474,55474,2339],{"class":503},[474,55476,2308],{"class":810},[474,55478,55479,55481,55484,55486,55488,55490,55492],{"class":476,"line":4946},[474,55480,2735],{"class":510},[474,55482,55483],{"class":484}," \"The given mysql config file doesn't exist. Please check your config file: $(",[474,55485,55380],{"class":480},[474,55487,55257],{"class":510},[474,55489,2720],{"class":484},[474,55491,55217],{"class":503},[474,55493,55389],{"class":484},[474,55495,55496,55498],{"class":476,"line":4952},[474,55497,55235],{"class":510},[474,55499,6824],{"class":510},[474,55501,55502],{"class":476,"line":4957},[474,55503,2354],{"class":810},[1065,55505,55507],{"id":55506},"creating-the-backup-directories","Creating the backup directories",[439,55509,55510],{},"The script checks if the specified parent backup directory already exists – and will create it if it doesn’t. For every\nprocessed backup a time stamped folder (with the pattern yyyy-MM-dd_hh-mm) is created in this directory containing\nthe backup data. Allowed is only one backup per minute. If you try to run the script and there is already a backup\nfolder for the current minute, the script will fail with an error message.",[464,55512,55514],{"className":54685,"code":55513,"language":54687,"meta":469,"style":469},"\nCURRENT_DATE=\"$(date +%Y-%m-%d_%H-%M)\"\nFULL_BACKUP_PATH=\"$BACKUP_PATH/$CURRENT_DATE\"\n# check if backup dir exists, if not create it\nif [ ! -d \"$BACKUP_PATH\" ]; then\n mkdir \"$BACKUP_PATH\"\n echo \"Create parent backup directory $(readlink -f \"$BACKUP_PATH\")\"\nfi\n# check if there is already a dir for current date, if not create it\nif [ ! -d \"$FULL_BACKUP_PATH\" ]; then\n mkdir \"$FULL_BACKUP_PATH\"\n echo \"Create backup directory $(readlink -f \"$FULL_BACKUP_PATH\")\"\nelse\n echo \"$(readlink -f \"$FULL_BACKUP_PATH\") already exists\"\n echo \"Wait until $(readlink -f \"$BACKUP_PATH/$(date -d '+1min' +%Y-%m-%d_%H-%M)\") can be created\"\n exit 1\nfi\n\n",[471,55515,55516,55520,55534,55552,55557,55577,55588,55605,55609,55614,55635,55645,55662,55667,55684,55709,55715],{"__ignoreMap":469},[474,55517,55518],{"class":476,"line":477},[474,55519,917],{"emptyLinePlaceholder":916},[474,55521,55522,55525,55527,55529,55531],{"class":476,"line":507},[474,55523,55524],{"class":503},"CURRENT_DATE",[474,55526,811],{"class":810},[474,55528,55099],{"class":484},[474,55530,2756],{"class":480},[474,55532,55533],{"class":484}," +%Y-%m-%d_%H-%M)\"\n",[474,55535,55536,55539,55541,55543,55545,55547,55550],{"class":476,"line":547},[474,55537,55538],{"class":503},"FULL_BACKUP_PATH",[474,55540,811],{"class":810},[474,55542,2289],{"class":484},[474,55544,55334],{"class":503},[474,55546,6875],{"class":484},[474,55548,55549],{"class":503},"$CURRENT_DATE",[474,55551,2744],{"class":484},[474,55553,55554],{"class":476,"line":584},[474,55555,55556],{"class":2277},"# check if backup dir exists, if not create it\n",[474,55558,55559,55561,55563,55565,55567,55569,55571,55573,55575],{"class":476,"line":607},[474,55560,2283],{"class":810},[474,55562,2286],{"class":503},[474,55564,18773],{"class":810},[474,55566,2616],{"class":810},[474,55568,2720],{"class":484},[474,55570,55334],{"class":503},[474,55572,2289],{"class":484},[474,55574,2339],{"class":503},[474,55576,2308],{"class":810},[474,55578,55579,55582,55584,55586],{"class":476,"line":642},[474,55580,55581],{"class":480}," mkdir",[474,55583,2720],{"class":484},[474,55585,55334],{"class":503},[474,55587,2744],{"class":484},[474,55589,55590,55592,55595,55597,55599,55601,55603],{"class":476,"line":663},[474,55591,2735],{"class":510},[474,55593,55594],{"class":484}," \"Create parent backup directory $(",[474,55596,55380],{"class":480},[474,55598,55257],{"class":510},[474,55600,2720],{"class":484},[474,55602,55334],{"class":503},[474,55604,55389],{"class":484},[474,55606,55607],{"class":476,"line":694},[474,55608,2354],{"class":810},[474,55610,55611],{"class":476,"line":700},[474,55612,55613],{"class":2277},"# check if there is already a dir for current date, if not create it\n",[474,55615,55616,55618,55620,55622,55624,55626,55629,55631,55633],{"class":476,"line":913},[474,55617,2283],{"class":810},[474,55619,2286],{"class":503},[474,55621,18773],{"class":810},[474,55623,2616],{"class":810},[474,55625,2720],{"class":484},[474,55627,55628],{"class":503},"$FULL_BACKUP_PATH",[474,55630,2289],{"class":484},[474,55632,2339],{"class":503},[474,55634,2308],{"class":810},[474,55636,55637,55639,55641,55643],{"class":476,"line":920},[474,55638,55581],{"class":480},[474,55640,2720],{"class":484},[474,55642,55628],{"class":503},[474,55644,2744],{"class":484},[474,55646,55647,55649,55652,55654,55656,55658,55660],{"class":476,"line":926},[474,55648,2735],{"class":510},[474,55650,55651],{"class":484}," \"Create backup directory $(",[474,55653,55380],{"class":480},[474,55655,55257],{"class":510},[474,55657,2720],{"class":484},[474,55659,55628],{"class":503},[474,55661,55389],{"class":484},[474,55663,55664],{"class":476,"line":932},[474,55665,55666],{"class":810},"else\n",[474,55668,55669,55671,55673,55675,55677,55679,55681],{"class":476,"line":938},[474,55670,2735],{"class":510},[474,55672,3015],{"class":484},[474,55674,55380],{"class":480},[474,55676,55257],{"class":510},[474,55678,2720],{"class":484},[474,55680,55628],{"class":503},[474,55682,55683],{"class":484},"\") already exists\"\n",[474,55685,55686,55688,55691,55693,55695,55697,55699,55702,55704,55706],{"class":476,"line":944},[474,55687,2735],{"class":510},[474,55689,55690],{"class":484}," \"Wait until $(",[474,55692,55380],{"class":480},[474,55694,55257],{"class":510},[474,55696,2720],{"class":484},[474,55698,55334],{"class":503},[474,55700,55701],{"class":484},"/$(",[474,55703,2756],{"class":480},[474,55705,2616],{"class":510},[474,55707,55708],{"class":484}," '+1min' +%Y-%m-%d_%H-%M)\") can be created\"\n",[474,55710,55711,55713],{"class":476,"line":950},[474,55712,55235],{"class":510},[474,55714,6824],{"class":510},[474,55716,55717],{"class":476,"line":956},[474,55718,2354],{"class":810},[1065,55720,55722],{"id":55721},"save-the-information-about-the-current-deployed-version","Save the information about the current deployed version",[439,55724,55725,55726,6562],{},"You find the information about the current deployed version in the ",[471,55727,55728],{},"pom.properties",[464,55730,55732],{"className":16895,"code":55731,"language":16897,"meta":469,"style":469}," ~/tomcat/webapps/ROOT/META-INF/maven/org.synyx/foo/pom.properties\n",[471,55733,55734],{"__ignoreMap":469},[474,55735,55736],{"class":476,"line":477},[474,55737,55731],{},[439,55739,55740],{},"It looks like this:",[464,55742,55744],{"className":16895,"code":55743,"language":16897,"meta":469,"style":469},"\n#Generated by Maven\n#Sat Mar 30 02:03:52 CET 2013\nversion=1.1.10-SNAPSHOT\ngroupId=org.synyx\nartifactId=foo\n\n",[471,55745,55746,55750,55755,55760,55765,55770],{"__ignoreMap":469},[474,55747,55748],{"class":476,"line":477},[474,55749,917],{"emptyLinePlaceholder":916},[474,55751,55752],{"class":476,"line":507},[474,55753,55754],{},"#Generated by Maven\n",[474,55756,55757],{"class":476,"line":547},[474,55758,55759],{},"#Sat Mar 30 02:03:52 CET 2013\n",[474,55761,55762],{"class":476,"line":584},[474,55763,55764],{},"version=1.1.10-SNAPSHOT\n",[474,55766,55767],{"class":476,"line":607},[474,55768,55769],{},"groupId=org.synyx\n",[474,55771,55772],{"class":476,"line":642},[474,55773,55774],{},"artifactId=foo\n",[439,55776,55777],{},"This .properties file contains all the relevant information we need if we’d like to rollback to the backup state. The\nNexus deploy script takes the group id, artifact id and version as parameters:",[464,55779,55781],{"className":16895,"code":55780,"language":16897,"meta":469,"style":469},"./nexusdeploy.sh -i org.synyx:foo:1.1.10-SNAPSHOT -w ROOT\n",[471,55782,55783],{"__ignoreMap":469},[474,55784,55785],{"class":476,"line":477},[474,55786,55780],{},[439,55788,55789],{},"So this properties file is copied to the backup directory.",[464,55791,55793],{"className":54685,"code":55792,"language":54687,"meta":469,"style":469},"\nPROPS_NAME=\"pom.properties\"\nAPP_PROPS=\"$(find \"$WEBAPP_PATH\" -name \"$PROPS_NAME\")\"\n# more than one file matching the criterias found\nif [ ! $(find \"$WEBAPP_PATH\" -name \"$PROPS_NAME\" | wc -l) -eq 1 ]; then\n echo \"No or more than one $PROPS_NAME was found under the webapp path $(readlink -f \"$WEBAPP_PATH\")\"\n exit 1\nfi\n# copy the information about the current deployed version\ncp \"$APP_PROPS\" \"$FULL_BACKUP_PATH\"\n\n",[471,55794,55795,55799,55809,55837,55842,55889,55911,55917,55921,55926],{"__ignoreMap":469},[474,55796,55797],{"class":476,"line":477},[474,55798,917],{"emptyLinePlaceholder":916},[474,55800,55801,55804,55806],{"class":476,"line":507},[474,55802,55803],{"class":503},"PROPS_NAME",[474,55805,811],{"class":810},[474,55807,55808],{"class":484},"\"pom.properties\"\n",[474,55810,55811,55814,55816,55818,55820,55822,55824,55827,55830,55832,55835],{"class":476,"line":547},[474,55812,55813],{"class":503},"APP_PROPS",[474,55815,811],{"class":810},[474,55817,55099],{"class":484},[474,55819,29924],{"class":480},[474,55821,2720],{"class":484},[474,55823,55317],{"class":503},[474,55825,55826],{"class":484},"\" ",[474,55828,55829],{"class":510},"-name",[474,55831,2720],{"class":484},[474,55833,55834],{"class":503},"$PROPS_NAME",[474,55836,55389],{"class":484},[474,55838,55839],{"class":476,"line":584},[474,55840,55841],{"class":2277},"# more than one file matching the criterias found\n",[474,55843,55844,55846,55848,55850,55853,55855,55857,55859,55861,55864,55866,55868,55870,55872,55875,55878,55880,55883,55885,55887],{"class":476,"line":607},[474,55845,2283],{"class":810},[474,55847,2286],{"class":503},[474,55849,18773],{"class":810},[474,55851,55852],{"class":503}," $(",[474,55854,29924],{"class":480},[474,55856,2720],{"class":484},[474,55858,55317],{"class":503},[474,55860,2289],{"class":484},[474,55862,55863],{"class":510}," -name",[474,55865,2720],{"class":484},[474,55867,55834],{"class":503},[474,55869,2289],{"class":484},[474,55871,14174],{"class":810},[474,55873,55874],{"class":480}," wc",[474,55876,55877],{"class":510}," -l",[474,55879,2077],{"class":503},[474,55881,55882],{"class":810},"-eq",[474,55884,2685],{"class":510},[474,55886,2339],{"class":503},[474,55888,2308],{"class":810},[474,55890,55891,55893,55896,55898,55901,55903,55905,55907,55909],{"class":476,"line":642},[474,55892,2735],{"class":510},[474,55894,55895],{"class":484}," \"No or more than one ",[474,55897,55834],{"class":503},[474,55899,55900],{"class":484}," was found under the webapp path $(",[474,55902,55380],{"class":480},[474,55904,55257],{"class":510},[474,55906,2720],{"class":484},[474,55908,55317],{"class":503},[474,55910,55389],{"class":484},[474,55912,55913,55915],{"class":476,"line":663},[474,55914,55235],{"class":510},[474,55916,6824],{"class":510},[474,55918,55919],{"class":476,"line":694},[474,55920,2354],{"class":810},[474,55922,55923],{"class":476,"line":700},[474,55924,55925],{"class":2277},"# copy the information about the current deployed version\n",[474,55927,55928,55931,55933,55936,55938,55940,55942],{"class":476,"line":913},[474,55929,55930],{"class":480},"cp",[474,55932,2720],{"class":484},[474,55934,55935],{"class":503},"$APP_PROPS",[474,55937,2289],{"class":484},[474,55939,2720],{"class":484},[474,55941,55628],{"class":503},[474,55943,2744],{"class":484},[1065,55945,55947],{"id":55946},"save-the-war-file-itself-if-configured","Save the war file itself if configured",[439,55949,55950],{},"We remember, in the configuration file there are three possibilities what to do with the war file during the backup\nprocess:",[994,55952,55953,55956,55959],{},[997,55954,55955],{},"always save the war file",[997,55957,55958],{},"never save the war file",[997,55960,55961],{},"save the war file only if the current deployed version is a snapshot",[439,55963,55964,55965,55967,55968,55971],{},"The first two states are self-explanatory: either the war file is copied to the backup directory or not. If the war\nfile should be only saved if it is a snapshot, the ",[471,55966,55728],{}," file has to be read in and the variable ",[471,55969,55970],{},"$version","\nhas to be checked if it contains the string ‘SNAPSHOT’.",[464,55973,55975],{"className":54685,"code":55974,"language":54687,"meta":469,"style":469},"\n# variable isn't empty\nif [ ! -z \"$WAR\" ]; then\nSAVE_WAR=0\n# decide if war should be saved\n# if conf = save war only if current deployed version is a snapshot\nif [ \"$WAR\" -eq 0 ]; then\n# check if current deployed version is a snapshot\n# read in properties\n. \"$APP_PROPS\"\n# $version has information about current deployed version\ncase \"$version\" in\n *SNAPSHOT)\n SAVE_WAR=1\n ;;\nesac\n# if conf = save war always\nelif [ \"$WAR\" -eq 1 ]; then\n SAVE_WAR=1\nfi\nif [ \"$SAVE_WAR\" -eq 1 ]; then\n echo \"Backup war file: \"\n cp -v \"$WEBAPP_PATH\"/../*.war \"$FULL_BACKUP_PATH\"\nfi\nfi\n\n",[471,55976,55977,55981,55986,56008,56018,56023,56028,56048,56053,56058,56068,56073,56085,56095,56104,56109,56113,56118,56138,56147,56151,56172,56179,56204,56208],{"__ignoreMap":469},[474,55978,55979],{"class":476,"line":477},[474,55980,917],{"emptyLinePlaceholder":916},[474,55982,55983],{"class":476,"line":507},[474,55984,55985],{"class":2277},"# variable isn't empty\n",[474,55987,55988,55990,55992,55994,55997,55999,56002,56004,56006],{"class":476,"line":547},[474,55989,2283],{"class":810},[474,55991,2286],{"class":503},[474,55993,18773],{"class":810},[474,55995,55996],{"class":810}," -z",[474,55998,2720],{"class":484},[474,56000,56001],{"class":503},"$WAR",[474,56003,2289],{"class":484},[474,56005,2339],{"class":503},[474,56007,2308],{"class":810},[474,56009,56010,56013,56015],{"class":476,"line":584},[474,56011,56012],{"class":503},"SAVE_WAR",[474,56014,811],{"class":810},[474,56016,56017],{"class":484},"0\n",[474,56019,56020],{"class":476,"line":607},[474,56021,56022],{"class":2277},"# decide if war should be saved\n",[474,56024,56025],{"class":476,"line":642},[474,56026,56027],{"class":2277},"# if conf = save war only if current deployed version is a snapshot\n",[474,56029,56030,56032,56034,56036,56038,56040,56042,56044,56046],{"class":476,"line":663},[474,56031,2283],{"class":810},[474,56033,2286],{"class":503},[474,56035,2289],{"class":484},[474,56037,56001],{"class":503},[474,56039,2289],{"class":484},[474,56041,6801],{"class":810},[474,56043,2414],{"class":510},[474,56045,2339],{"class":503},[474,56047,2308],{"class":810},[474,56049,56050],{"class":476,"line":694},[474,56051,56052],{"class":2277},"# check if current deployed version is a snapshot\n",[474,56054,56055],{"class":476,"line":700},[474,56056,56057],{"class":2277},"# read in properties\n",[474,56059,56060,56062,56064,56066],{"class":476,"line":913},[474,56061,1402],{"class":510},[474,56063,2720],{"class":484},[474,56065,55935],{"class":503},[474,56067,2744],{"class":484},[474,56069,56070],{"class":476,"line":920},[474,56071,56072],{"class":2277},"# $version has information about current deployed version\n",[474,56074,56075,56077,56079,56081,56083],{"class":476,"line":926},[474,56076,55143],{"class":810},[474,56078,2720],{"class":484},[474,56080,55970],{"class":503},[474,56082,2289],{"class":484},[474,56084,55153],{"class":810},[474,56086,56087,56090,56093],{"class":476,"line":932},[474,56088,56089],{"class":810}," *",[474,56091,56092],{"class":55158},"SNAPSHOT",[474,56094,1101],{"class":810},[474,56096,56097,56100,56102],{"class":476,"line":938},[474,56098,56099],{"class":503}," SAVE_WAR",[474,56101,811],{"class":810},[474,56103,54988],{"class":484},[474,56105,56106],{"class":476,"line":944},[474,56107,56108],{"class":503}," ;;\n",[474,56110,56111],{"class":476,"line":950},[474,56112,55178],{"class":810},[474,56114,56115],{"class":476,"line":956},[474,56116,56117],{"class":2277},"# if conf = save war always\n",[474,56119,56120,56122,56124,56126,56128,56130,56132,56134,56136],{"class":476,"line":962},[474,56121,2323],{"class":810},[474,56123,2286],{"class":503},[474,56125,2289],{"class":484},[474,56127,56001],{"class":503},[474,56129,2289],{"class":484},[474,56131,6801],{"class":810},[474,56133,2685],{"class":510},[474,56135,2339],{"class":503},[474,56137,2308],{"class":810},[474,56139,56140,56143,56145],{"class":476,"line":4876},[474,56141,56142],{"class":503}," SAVE_WAR",[474,56144,811],{"class":810},[474,56146,54988],{"class":484},[474,56148,56149],{"class":476,"line":4888},[474,56150,2354],{"class":810},[474,56152,56153,56155,56157,56159,56162,56164,56166,56168,56170],{"class":476,"line":4900},[474,56154,2283],{"class":810},[474,56156,2286],{"class":503},[474,56158,2289],{"class":484},[474,56160,56161],{"class":503},"$SAVE_WAR",[474,56163,2289],{"class":484},[474,56165,6801],{"class":810},[474,56167,2685],{"class":510},[474,56169,2339],{"class":503},[474,56171,2308],{"class":810},[474,56173,56174,56176],{"class":476,"line":4913},[474,56175,2735],{"class":510},[474,56177,56178],{"class":484}," \"Backup war file: \"\n",[474,56180,56181,56184,56186,56188,56190,56193,56195,56198,56200,56202],{"class":476,"line":4921},[474,56182,56183],{"class":480}," cp",[474,56185,2645],{"class":510},[474,56187,2720],{"class":484},[474,56189,55317],{"class":503},[474,56191,56192],{"class":484},"\"/../",[474,56194,11737],{"class":510},[474,56196,56197],{"class":484},".war",[474,56199,2720],{"class":484},[474,56201,55628],{"class":503},[474,56203,2744],{"class":484},[474,56205,56206],{"class":476,"line":4932},[474,56207,2354],{"class":810},[474,56209,56210],{"class":476,"line":4938},[474,56211,2354],{"class":810},[1065,56213,56215],{"id":56214},"save-the-database-data","Save the database data",[439,56217,56218,56219,56221],{},"For every element in ",[471,56220,55349],{}," a dump is created.",[439,56223,56224,56225,56227,56228,56230,56231,56234,56235,56237],{},"Due to the existing ",[471,56226,54675],{}," with access data information, ",[471,56229,54667],{}," can be called without the parameters ",[471,56232,56233],{},"user"," and\n",[471,56236,15732],{},". However it needs the following information:",[994,56239,56240,56246,56252],{},[997,56241,56242,56243,17548],{},"the path to the config file with the access data (",[471,56244,56245],{},"--defaults-file",[997,56247,56248,56249,17548],{},"the suffix (database name) which access data section of the config file should be used (",[471,56250,56251],{},"--defaults-group-suffix",[997,56253,56254],{},"the database name to know which database should be dumped",[439,56256,56257,56258,56261,56262,56265,56266,56268],{},"If something goes wrong during database dump (e.g. access is denied), the error is redirected to ",[471,56259,56260],{},"/dev/null"," because the\nscript has its own error handling. It checks if ",[471,56263,56264],{},"$?"," is 0 or not. ",[471,56267,56264],{}," gives the exit status of the last executed\ncommand. A status not 0 is an error code, i.e. the last command wasn’t successful.",[464,56270,56272],{"className":54685,"code":56271,"language":54687,"meta":469,"style":469},"\nfor db in $DB_NAME\ndo\n # create dump and hide mysqldump errors due to own error handling\n mysqldump --defaults-file=\"$MYSQL_CONF\" --defaults-group-suffix=\"$db\" \"$db\" > \"$FULL_BACKUP_PATH/$db-$CURRENT_DATE.dump.sql\" 2>/dev/null\n # creating dump was successful, so return value is 0\n if [ \"$?\" -eq 0 ]; then\n echo \"Create dump for database $db: $(readlink -f \"$FULL_BACKUP_PATH/$db-$CURRENT_DATE.dump.sql\")\"\n # something went wrong while trying to create dump (e.g. access denied), so return value is not 0 but any other number\n else\n echo \"Problems encountered while trying to create dump for database $db\"\n fi\ndone\n\n",[471,56273,56274,56278,56290,56294,56299,56352,56357,56377,56408,56413,56417,56428,56432],{"__ignoreMap":469},[474,56275,56276],{"class":476,"line":477},[474,56277,917],{"emptyLinePlaceholder":916},[474,56279,56280,56282,56285,56287],{"class":476,"line":507},[474,56281,2674],{"class":810},[474,56283,56284],{"class":503}," db ",[474,56286,2680],{"class":810},[474,56288,56289],{"class":503}," $DB_NAME\n",[474,56291,56292],{"class":476,"line":547},[474,56293,2701],{"class":810},[474,56295,56296],{"class":476,"line":584},[474,56297,56298],{"class":2277}," # create dump and hide mysqldump errors due to own error handling\n",[474,56300,56301,56304,56307,56309,56311,56313,56316,56318,56321,56323,56325,56327,56329,56331,56333,56335,56337,56339,56341,56343,56346,56349],{"class":476,"line":607},[474,56302,56303],{"class":480}," mysqldump",[474,56305,56306],{"class":510}," --defaults-file=",[474,56308,2289],{"class":484},[474,56310,55364],{"class":503},[474,56312,2289],{"class":484},[474,56314,56315],{"class":510}," --defaults-group-suffix=",[474,56317,2289],{"class":484},[474,56319,56320],{"class":503},"$db",[474,56322,2289],{"class":484},[474,56324,2720],{"class":484},[474,56326,56320],{"class":503},[474,56328,2289],{"class":484},[474,56330,12487],{"class":810},[474,56332,2720],{"class":484},[474,56334,55628],{"class":503},[474,56336,6875],{"class":484},[474,56338,56320],{"class":503},[474,56340,10143],{"class":484},[474,56342,55549],{"class":503},[474,56344,56345],{"class":484},".dump.sql\"",[474,56347,56348],{"class":810}," 2>",[474,56350,56351],{"class":484},"/dev/null\n",[474,56353,56354],{"class":476,"line":642},[474,56355,56356],{"class":2277}," # creating dump was successful, so return value is 0\n",[474,56358,56359,56361,56363,56365,56367,56369,56371,56373,56375],{"class":476,"line":663},[474,56360,2980],{"class":810},[474,56362,2286],{"class":503},[474,56364,2289],{"class":484},[474,56366,56264],{"class":510},[474,56368,2289],{"class":484},[474,56370,6801],{"class":810},[474,56372,2414],{"class":510},[474,56374,2339],{"class":503},[474,56376,2308],{"class":810},[474,56378,56379,56381,56384,56386,56389,56391,56393,56395,56397,56399,56401,56403,56405],{"class":476,"line":694},[474,56380,2423],{"class":510},[474,56382,56383],{"class":484}," \"Create dump for database ",[474,56385,56320],{"class":503},[474,56387,56388],{"class":484},": $(",[474,56390,55380],{"class":480},[474,56392,55257],{"class":510},[474,56394,2720],{"class":484},[474,56396,55628],{"class":503},[474,56398,6875],{"class":484},[474,56400,56320],{"class":503},[474,56402,10143],{"class":484},[474,56404,55549],{"class":503},[474,56406,56407],{"class":484},".dump.sql\")\"\n",[474,56409,56410],{"class":476,"line":700},[474,56411,56412],{"class":2277}," # something went wrong while trying to create dump (e.g. access denied), so return value is not 0 but any other number\n",[474,56414,56415],{"class":476,"line":913},[474,56416,3025],{"class":810},[474,56418,56419,56421,56424,56426],{"class":476,"line":920},[474,56420,2423],{"class":510},[474,56422,56423],{"class":484}," \"Problems encountered while trying to create dump for database ",[474,56425,56320],{"class":503},[474,56427,2744],{"class":484},[474,56429,56430],{"class":476,"line":926},[474,56431,3037],{"class":810},[474,56433,56434],{"class":476,"line":932},[474,56435,2879],{"class":810},[1065,56437,56439],{"id":56438},"save-specified-files-andor-directories","Save specified files and/or directories",[439,56441,56442],{},"To move the specified files and/or directories to the backup directory, following steps are performed:",[994,56444,56445,56452,56459],{},[997,56446,56447,56448,56451],{},"make sure that the variable ",[471,56449,56450],{},"$CONTENT_TO_BE_MOVED"," isn’t empty",[997,56453,56454,56455,56458],{},"execute the ",[471,56456,56457],{},"move"," operation for each element",[997,56460,56461],{},"if an element doesn’t exist, print an error message",[464,56463,56465],{"className":54685,"code":56464,"language":54687,"meta":469,"style":469},"\n# make sure that variable $CONTENT_TO_BE_MOVED isn't empty\nif [ ! -z \"$CONTENT_TO_BE_MOVED\" ]; then\nfor i in $CONTENT_TO_BE_MOVED\ndo\n # if the given content is a dir or a file, process else throw an error\n if [ -d \"$i\" ] || [ -f \"$i\" ]; then\n echo \"Move $(readlink -f \"$i\") into backup dir\"\n mv \"$i\" \"$FULL_BACKUP_PATH\"\n else\n echo \"Can't move $i: doesn't exist\";\n fi\ndone\nfi\n\n",[471,56466,56467,56471,56476,56496,56507,56511,56516,56549,56567,56584,56588,56602,56606,56610],{"__ignoreMap":469},[474,56468,56469],{"class":476,"line":477},[474,56470,917],{"emptyLinePlaceholder":916},[474,56472,56473],{"class":476,"line":507},[474,56474,56475],{"class":2277},"# make sure that variable $CONTENT_TO_BE_MOVED isn't empty\n",[474,56477,56478,56480,56482,56484,56486,56488,56490,56492,56494],{"class":476,"line":547},[474,56479,2283],{"class":810},[474,56481,2286],{"class":503},[474,56483,18773],{"class":810},[474,56485,55996],{"class":810},[474,56487,2720],{"class":484},[474,56489,56450],{"class":503},[474,56491,2289],{"class":484},[474,56493,2339],{"class":503},[474,56495,2308],{"class":810},[474,56497,56498,56500,56502,56504],{"class":476,"line":584},[474,56499,2674],{"class":810},[474,56501,2677],{"class":503},[474,56503,2680],{"class":810},[474,56505,56506],{"class":503}," $CONTENT_TO_BE_MOVED\n",[474,56508,56509],{"class":476,"line":607},[474,56510,2701],{"class":810},[474,56512,56513],{"class":476,"line":642},[474,56514,56515],{"class":2277}," # if the given content is a dir or a file, process else throw an error\n",[474,56517,56518,56520,56522,56524,56526,56528,56530,56532,56534,56536,56539,56541,56543,56545,56547],{"class":476,"line":663},[474,56519,2980],{"class":810},[474,56521,2286],{"class":503},[474,56523,14077],{"class":810},[474,56525,2720],{"class":484},[474,56527,2764],{"class":503},[474,56529,2289],{"class":484},[474,56531,55322],{"class":503},[474,56533,55325],{"class":810},[474,56535,2286],{"class":503},[474,56537,56538],{"class":810},"-f",[474,56540,2720],{"class":484},[474,56542,2764],{"class":503},[474,56544,2289],{"class":484},[474,56546,2339],{"class":503},[474,56548,2308],{"class":810},[474,56550,56551,56553,56556,56558,56560,56562,56564],{"class":476,"line":694},[474,56552,2423],{"class":510},[474,56554,56555],{"class":484}," \"Move $(",[474,56557,55380],{"class":480},[474,56559,55257],{"class":510},[474,56561,2720],{"class":484},[474,56563,2764],{"class":503},[474,56565,56566],{"class":484},"\") into backup dir\"\n",[474,56568,56569,56572,56574,56576,56578,56580,56582],{"class":476,"line":700},[474,56570,56571],{"class":480}," mv",[474,56573,2720],{"class":484},[474,56575,2764],{"class":503},[474,56577,2289],{"class":484},[474,56579,2720],{"class":484},[474,56581,55628],{"class":503},[474,56583,2744],{"class":484},[474,56585,56586],{"class":476,"line":913},[474,56587,3025],{"class":810},[474,56589,56590,56592,56595,56597,56600],{"class":476,"line":920},[474,56591,2423],{"class":510},[474,56593,56594],{"class":484}," \"Can't move ",[474,56596,2764],{"class":503},[474,56598,56599],{"class":484},": doesn't exist\"",[474,56601,2139],{"class":503},[474,56603,56604],{"class":476,"line":926},[474,56605,3037],{"class":810},[474,56607,56608],{"class":476,"line":932},[474,56609,2879],{"class":810},[474,56611,56612],{"class":476,"line":938},[474,56613,2354],{"class":810},[439,56615,56616,56617,56620,56621,56624],{},"These steps are similar for the files/directories to be copied, only the action (",[471,56618,56619],{},"copy",") and the parameter (\n",[471,56622,56623],{},"$CONTENT_TO_BE_COPIED",") are different.",[1065,56626,56628],{"id":56627},"dont-forget-to-make-your-script-executable","Don’t forget to make your script executable 😉",[464,56630,56632],{"className":16895,"code":56631,"language":16897,"meta":469,"style":469},"chmod +x backup.sh\n",[471,56633,56634],{"__ignoreMap":469},[474,56635,56636],{"class":476,"line":477},[474,56637,56631],{},[3938,56639,56641],{"id":56640},"automation","Automation",[439,56643,56644],{},"Now it’s no longer required to do the backup manually. 🙂 And even better: if this script is integrated into our existing\nautomatic deployment script, every time a deployment is performed, a backup will be performed first.",[3938,56646,56648],{"id":56647},"links","Links",[439,56650,56651],{},[1002,56652,56655],{"href":54653,"rel":56653,"title":56654},[1006],"Github Project","Github Project: automated-backup",[439,56657,56658],{},[1002,56659,56662],{"href":54606,"rel":56660,"title":56661},[1006],"Link to opening quote","Link to opening quote: Continuous Delivery vs. Continuous Deployment vs. Continuous-Integration",[1024,56664,56665],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":469,"searchDepth":507,"depth":507,"links":56667},[56668,56669,56670,56671,56672,56682,56683],{"id":54592,"depth":507,"text":54593},{"id":54611,"depth":507,"text":54612},{"id":54657,"depth":507,"text":54658},{"id":54819,"depth":507,"text":54820},{"id":55043,"depth":507,"text":55044,"children":56673},[56674,56675,56676,56677,56678,56679,56680,56681],{"id":55050,"depth":547,"text":55051},{"id":55185,"depth":547,"text":55186},{"id":55506,"depth":547,"text":55507},{"id":55721,"depth":547,"text":55722},{"id":55946,"depth":547,"text":55947},{"id":56214,"depth":547,"text":56215},{"id":56438,"depth":547,"text":56439},{"id":56627,"depth":547,"text":56628},{"id":56640,"depth":507,"text":56641},{"id":56647,"depth":507,"text":56648},[13208,1030],"2013-04-10T12:01:45","https://synyx.de/blog/continuous-deployment-automatic-backup-script/",{},"/blog/continuous-deployment-automatic-backup-script",{"title":54583,"description":469},"blog/continuous-deployment-automatic-backup-script",[56640,7287,56692,56693,54667,56694],"continuous-delivery","continuous-deployment","shell-script","A few words about Continuous Deployment Continuous Deployment is the deployment or release of code to Production as soon as it is ready. (…) The automated process is key because…","Hn5KrGdZNvCGjjquVQO6wQSlqBJmAcHF-Ov442gIBu4",{"id":56698,"title":56699,"author":56700,"body":56701,"category":56743,"date":56744,"description":56745,"extension":1034,"link":56746,"meta":56747,"navigation":916,"path":56748,"seo":56749,"slug":56750,"stem":56751,"tags":56752,"teaser":56753,"__hash__":56754},"blog/blog/kann-googles-8020-formel-fur-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein.md","Kann Google's „80/20 Formel“ für ein reines Dienstleistungsunternehmen wirtschaftlich sein?",[312],{"type":432,"value":56702,"toc":56741},[56703,56706,56709,56712,56715,56718,56721,56724,56727,56730,56733,56736],[435,56704,56699],{"id":56705},"kann-googles-8020-formel-für-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein",[439,56707,56708],{},"Mit diesem Blog-Beitrag möchte ich etwas Einblick in die Unternehmensstrategie, die „Denke“ und Werte von synyx geben –\nund, natürlich aufzeigen, was das Ganze mit Google’s „80/20 Formel” zu tun hat. Seit Mitte Oktober bin ich hier für\nSales verantwortlich. Vor mir gab es in der Firma noch keinen Vertriebsmitarbeiter. Von daher war und ist es immer noch\neine Herausforderung, die eigenen aber natürlich unterschiedlichen Erfahrungen, wie Unternehmen funktionieren und sich\nentwickeln können, auf einen gemeinsamen Nenner zu bringen. Eine spannende Aufgabe, vor allem auch, weil es bei synyx\nkeine Entscheidungen von „Oben“ herab gibt, sondern wirklich alles im Team/in der Firma diskutiert und beschlossen wird.\nDas macht das Ganze natürlich sehr spannend, weil man die Kollegen für seine Ideen begeistern muss.",[439,56710,56711],{},"Zu Beginn meiner Tätigkeit haben wir intensiv über die Unternehmensziele und deren genauere Definition gesprochen. Ganz\noben steht dabei die Qualität in der Softwareentwicklung. Qualität ist der Punkt, der nicht verhandelbar ist und in dem\nwir führend sein wollen. Für mich als Verkäufer klingt das natürlich hervorragend. Wenn man nachhaltige\nKundenbeziehungen und keine One-Shot-Deals machen möchte, ist Qualität DAS Mittel dafür. Stellt sich nur noch die\nFrage, wie wir diesen hohen Anspruch an Qualität erreichen und belegen können.",[439,56713,56714],{},"Eine Vorgehensweise, welche sich synyx zur Maximierung der Qualität vorgenommen hat, ist Google’s „80/20 Formel”. Die\nIdee ist, 80 % seiner Zeit produktiv (fakturierbar) zu arbeiten und 20 % der Zeit für Innovationen aufzuwenden. In\nunserem Fall haben wir gesagt, wir wollen die 20 % unserer Zeit für die persönliche Entwicklung der Mitarbeiter (\nWeiterbildung) bzw. die Unternehmensentwicklung (R&D, Marketing) nutzen. Aktuell haben wir dieses Ziel noch nicht voll\nerreicht, aber wir arbeiten konsequent an der Umsetzung. Ein Zeitanteil von 20 % entspricht bei unserer 40 Stundenwoche,\neinem kompletten Tag pro Woche. Zusätzlich zu den 20 % Zeit hat jeder Mitarbeiter ein Weiterbildungsbudget von 2.000\nEUR pro Jahr. Das ist insgesamt ein beeindruckendes Investment, welches die Firma tätigt, um seine Qualitätsansprüchen\ngerecht zu werden.",[439,56716,56717],{},"Was bedeutet nun der faktische Verzicht auf 20 % vom Umsatz? Können wir uns das überhaupt leisten? Da wir ein reines\nDienstleistungsunternehmen sind und kein Produktgeschäft „neben her“ am Laufen haben, verdienen wir ausschließlich über\nunsere Zeit, die wir für den Kunden arbeiten.",[439,56719,56720],{},"Also noch mal die Frage: Können wir uns Google’s „80/20 Formel“ leisten? Antwort: Ja! Und ist es für uns wirtschaftlich?\nAntwort: Ja!",[439,56722,56723],{},"Im Geiste lese ich schon Blog-Kommentare wie: „Klar, könnt Ihr Euch das leisten, wenn Ihr horrende Tagessätze\nverlangt!“. Oder: „Ihr bezahlt nur Hungerlöhne! Dann geht das.“. Beide imaginären Kommentare sind nachvollziehbar und\ngehen, wenn auch nur geringfügig, in die richtige Richtung.",[439,56725,56726],{},"Zu unseren Tagessätzen: Diese sind aus meiner Sicht absolut marktkonform und wettbewerbsfähig. Mein Ziel ist es leicht\nüber dem Mittelfeld zu liegen. Darüber, weil wir aufgrund unserer „80/20 Regel“ sehr wahrscheinlich eine bessere\nQualität, als unsere Marktbegleiter liefern können. Zu den Hungerlöhnen: Es ist richtig, dass wir keine Gehälter zahlen\nkönnen, wie Sie in größeren Consulting Firmen oder bei größeren Endkunden gezahlt werden. Wir sind allerdings auch nicht\nso weit weg davon und bieten inhaltlich bzw. von der Work-life-balance oft mehr von dem, wie sich Mitarbeiter Ihre\nArbeit vorstellen. Unserer Meinung nach ist",[439,56728,56729],{},"Lebensqualität mit einem guten Gehalt mehr wert als ein Maximalgehalt",[439,56731,56732],{},"mit Stress und fehlender Work-Life Balance.",[439,56734,56735],{},"Der Entscheidende Faktor, warum synyx sich die „80/20 Regel“ leisten kann ist jedoch die Geschäftsführung selbst.\nThomas, (Jo)Achim und Markus zahlen sich selbst im Verhältnis ein sehr bescheidenes Gehalt. Dieses nicht entnommene\nGeld, ist ein maßgeblicher Grund dafür, dass wir es uns leisten können auf 20 % unseres Umsatzes zu verzichten. Im\nKontext mit den aktuellen Debatten über explodierender Manager Gehälter und hemmungsloser Mitnahmementalität, tut es\neinfach gut ein komplett anderes, auf Nachhaltigkeit ausgelegtes Unternehmensmodell zu erleben und daran mitwirken zu\nkönnen.",[439,56737,56738,56739,1402],{},"Für mich ist die „80/20 Regel“ ein klarer Wettbewerbsvorteil, den ich selbstverständlich in Kunden- oder\nPersonalgesprächen nenne und erläutere. Mich würde interessieren, welche anderen Unternehmen ähnlich viel In Qualität\ninvestieren möchten, die gleichen Ziele und Werte verfolgen und natürlich wie diese das umsetzen. Wer eines kennt, bitte\nMail an ",[1002,56740,53393],{"href":53392},{"title":469,"searchDepth":507,"depth":507,"links":56742},[],[1031],"2013-03-15T09:16:31","Mit diesem Blog-Beitrag möchte ich etwas Einblick in die Unternehmensstrategie, die „Denke“ und Werte von synyx geben –\\nund, natürlich aufzeigen, was das Ganze mit Google’s „80/20 Formel” zu tun hat. Seit Mitte Oktober bin ich hier für\\nSales verantwortlich. Vor mir gab es in der Firma noch keinen Vertriebsmitarbeiter. Von daher war und ist es immer noch\\neine Herausforderung, die eigenen aber natürlich unterschiedlichen Erfahrungen, wie Unternehmen funktionieren und sich\\nentwickeln können, auf einen gemeinsamen Nenner zu bringen. Eine spannende Aufgabe, vor allem auch, weil es bei synyx\\nkeine Entscheidungen von „Oben“ herab gibt, sondern wirklich alles im Team/in der Firma diskutiert und beschlossen wird.\\nDas macht das Ganze natürlich sehr spannend, weil man die Kollegen für seine Ideen begeistern muss.","https://synyx.de/blog/kann-googles-8020-formel-fur-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein/",{},"/blog/kann-googles-8020-formel-fur-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein",{"title":56699,"description":56708},"kann-googles-8020-formel-fur-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein","blog/kann-googles-8020-formel-fur-ein-reines-dienstleistungsunternehmen-wirtschaftlich-sein",[],"Mit diesem Blog-Beitrag möchte ich etwas Einblick in die Unternehmensstrategie, die „Denke“ und Werte von synyx geben – und, natürlich aufzeigen, was das Ganze mit Google’s „80/20 Formel” zu tun…","IRosyh8hdxiqO4H8PR5bKoTIZ43PiIIxUeGCym_05Ms",{"id":56756,"title":56757,"author":56758,"body":56759,"category":57493,"date":57494,"description":57495,"extension":1034,"link":57496,"meta":57497,"navigation":916,"path":57498,"seo":57499,"slug":56763,"stem":57501,"tags":57502,"teaser":57505,"__hash__":57506},"blog/blog/running-rxtx-on-your-raspberry-pi.md","Running RXTX on your Raspberry Pi",[112],{"type":432,"value":56760,"toc":57485},[56761,56764,56779,56782,56797,56801,56804,56828,56832,56846,56849,56853,56867,56871,56874,56877,57006,57015,57018,57021,57100,57106,57140,57144,57147,57163,57166,57186,57189,57193,57196,57397,57400,57418,57428,57431,57479,57482],[435,56762,56757],{"id":56763},"running-rxtx-on-your-raspberry-pi",[439,56765,56766,56767,56772,56773,56778],{},"A few years ago, a good friend of mine installed a\nsmall ",[1002,56768,56771],{"href":56769,"rel":56770,"title":56771},"http://en.wikipedia.org/wiki/Photovoltaic_system",[1006],"photovoltaic system"," on his roof. I’m\nvery exited about installing some solar panels on a roof and start producing electricity. It turned out that the\ninstalled inverters have massive quality problems. So the idea was born to monitor the whole setup like i know it from\ncomputer systems. Each ",[1002,56774,56777],{"href":56775,"rel":56776,"title":56777},"http://en.wikipedia.org/wiki/Power_inverter",[1006],"inverter"," in this system has a serial\nport interface to transmit data.",[439,56780,56781],{},"But at this time, the market of low cost and low power consuming computers wasn’t distinct as it is nowadays. In\nFebruary 2012, the Raspberry Pi Foundation started to sell Raspberry Pis. I saw this as a chance to get a cheap computer\nwith a minimal power consumption.",[439,56783,56784,56785,56790,56791,56796],{},"So this blog post is about connecting a Raspberry Pi to inverters (or any other device) via RS232. We will use\nthe ",[1002,56786,56789],{"href":56787,"rel":56788,"title":56789},"http://www.oracle.com/technetwork/java/index-jsp-141752.html",[1006],"Java Communications API"," to\nread received data from the inverters. We don’t use\nthe ",[1002,56792,56795],{"href":56793,"rel":56794,"title":56795},"http://www.savagehomeautomation.com/projects/raspberry-pi-installing-a-rs232-serial-port.html",[1006],"integrated RS232 port","\nbecause we want to monitore more than just a signle inverter, so we have to use USB-RS232 adapters.",[3938,56798,56800],{"id":56799},"prepare-your-pi","Prepare your Pi",[439,56802,56803],{},"To demonstrate this post, I’m using the following hardware setup:",[994,56805,56806,56809,56812,56819,56822,56825],{},[997,56807,56808],{},"Raspberry Pi – Model B rev 2",[997,56810,56811],{},"SDCard SanDisk Ultra SDHC Class 10",[997,56813,56814],{},[1002,56815,56818],{"href":56816,"rel":56817,"title":56818},"http://www.dlink.com/us/en/home-solutions/connect/usb-hubs/dub-h7-7-port-usb-2-0-hub",[1006],"D-Link USB hub",[997,56820,56821],{},"Micro USB cable",[997,56823,56824],{},"USB-RS232 adapters",[997,56826,56827],{},"Alfa Networks USB WIFI adapter",[3938,56829,56831],{"id":56830},"connect-cables-and-stuff","Connect Cables and Stuff",[439,56833,56834,56835,56839,56840,56845],{},"First of all we have to get a Pi ready running a Linux with SSH access. The Raspberry guys recommend\nusing [Raspbian “wheezy”](",[1002,56836,56837],{"href":56837,"rel":56838},"http://www.raspberrypi.org/downloads",[1006]," \"Raspbian \") with Hard-Float. Follow the Quick start\nguide ",[1002,56841,56844],{"href":56842,"rel":56843},"http://www.raspberrypi.org/quick%5C-start%5C-guide",[1006],"http://www.raspberrypi.org/quick\\-start\\-guide"," to setup your Pi.",[439,56847,56848],{},"Plug in all your USB devices into the USB hub, like keyboard, mouse, WIFI adapter, USB-RS232 adapters. The USB hub will\nbe used as a power supply, so plug in the micro USB cable into the hub and into the micro USB port of your Pi. For the\nfirst boot, connect also a network cable into your Pi. Power up the USB hub and the Pi is going to boot for the first\ntime.",[3938,56850,56852],{"id":56851},"configure-wifi","Configure WIFI",[439,56854,56855,56856,56859,56860,18175,56863,56866],{},"Now it’s time to setup the WIFI connection. Install the wpasupplicant package via ",[471,56857,56858],{},"apt-get install wpasupplicant",". Then\ncopy paste ",[471,56861,56862],{},"/etc/network/interfaces",[471,56864,56865],{},"/etc/wpa_supplicant/wpa_supplicant.conf"," from my gists and adapt the files.",[3938,56868,56870],{"id":56869},"serial-adapters","Serial Adapters",[439,56872,56873],{},"The D-Link USB hub has 7 ports (I define port 1 as the one on the left side and port 7 as the port on the right side)\nand I plugged two USB RS232 adapters into port 1 and port 2.",[439,56875,56876],{},"To check the USB-RS232 adapters are recognized by the Pi, run:",[464,56878,56880],{"className":466,"code":56879,"language":468,"meta":469,"style":469},"root@raspberrypi:/# lsusb | grep 232\nBus 001 Device 011: ID 0557:2008 ATEN International Co., Ltd UC-232A Serial Port [pl2303]\nBus 001 Device 010: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC\nroot@raspberrypi:/# ls /dev/ttyUSB*\n/dev/ttyUSB0 /dev/ttyUSB1\nroot@raspberrypi:/#\n",[471,56881,56882,56897,56941,56980,56993,57001],{"__ignoreMap":469},[474,56883,56884,56887,56890,56892,56894],{"class":476,"line":477},[474,56885,56886],{"class":480},"root@raspberrypi:/#",[474,56888,56889],{"class":484}," lsusb",[474,56891,14174],{"class":810},[474,56893,19773],{"class":480},[474,56895,56896],{"class":510}," 232\n",[474,56898,56899,56902,56905,56908,56911,56914,56917,56920,56923,56926,56929,56932,56935,56938],{"class":476,"line":507},[474,56900,56901],{"class":480},"Bus",[474,56903,56904],{"class":510}," 001",[474,56906,56907],{"class":484}," Device",[474,56909,56910],{"class":484}," 011:",[474,56912,56913],{"class":484}," ID",[474,56915,56916],{"class":484}," 0557:2008",[474,56918,56919],{"class":484}," ATEN",[474,56921,56922],{"class":484}," International",[474,56924,56925],{"class":484}," Co.,",[474,56927,56928],{"class":484}," Ltd",[474,56930,56931],{"class":484}," UC-232A",[474,56933,56934],{"class":484}," Serial",[474,56936,56937],{"class":484}," Port",[474,56939,56940],{"class":503}," [pl2303]\n",[474,56942,56943,56945,56947,56949,56952,56954,56957,56960,56963,56966,56969,56971,56974,56977],{"class":476,"line":547},[474,56944,56901],{"class":480},[474,56946,56904],{"class":510},[474,56948,56907],{"class":484},[474,56950,56951],{"class":484}," 010:",[474,56953,56913],{"class":484},[474,56955,56956],{"class":484}," 0403:6001",[474,56958,56959],{"class":484}," Future",[474,56961,56962],{"class":484}," Technology",[474,56964,56965],{"class":484}," Devices",[474,56967,56968],{"class":484}," International,",[474,56970,56928],{"class":484},[474,56972,56973],{"class":484}," FT232",[474,56975,56976],{"class":484}," USB-Serial",[474,56978,56979],{"class":503}," (UART) IC\n",[474,56981,56982,56984,56987,56990],{"class":476,"line":584},[474,56983,56886],{"class":480},[474,56985,56986],{"class":484}," ls",[474,56988,56989],{"class":484}," /dev/ttyUSB",[474,56991,56992],{"class":510},"*\n",[474,56994,56995,56998],{"class":476,"line":607},[474,56996,56997],{"class":480},"/dev/ttyUSB0",[474,56999,57000],{"class":484}," /dev/ttyUSB1\n",[474,57002,57003],{"class":476,"line":642},[474,57004,57005],{"class":480},"root@raspberrypi:/#\n",[439,57007,57008,57009,57011,57012,57014],{},"One important question is, which of the adapters is ",[471,57010,56997],{},"? Smart people will say, ",[471,57013,56997],{}," is the one,\nplugged in first – that’s right.",[439,57016,57017],{},"Lazy people (including myself) are using udev rules to create an alias per USB hub port.",[439,57019,57020],{},"After the rules are applied, all serial ports are accessible via aliases and we have a nice and easy way accessing our\nserial ports, no matter which port was plugged in first.",[464,57022,57024],{"className":466,"code":57023,"language":468,"meta":469,"style":469},"root@raspberrypi:/# ls -l /dev/ttySerialPort*\nlrwxrwxrwx 1 root root 7 Jan 1 1970 /dev/ttySerialPort1 -> ttyUSB1\nlrwxrwxrwx 1 root root 7 Jan 1 1970 /dev/ttySerialPort2 -> ttyUSB0\n",[471,57025,57026,57039,57072],{"__ignoreMap":469},[474,57027,57028,57030,57032,57034,57037],{"class":476,"line":477},[474,57029,56886],{"class":480},[474,57031,56986],{"class":484},[474,57033,55877],{"class":510},[474,57035,57036],{"class":484}," /dev/ttySerialPort",[474,57038,56992],{"class":510},[474,57040,57041,57044,57046,57048,57050,57053,57056,57059,57062,57065,57067,57069],{"class":476,"line":507},[474,57042,57043],{"class":480},"lrwxrwxrwx",[474,57045,2685],{"class":510},[474,57047,29895],{"class":484},[474,57049,29895],{"class":484},[474,57051,57052],{"class":510}," 7",[474,57054,57055],{"class":484}," Jan",[474,57057,57058],{"class":510}," 1",[474,57060,57061],{"class":510}," 1970",[474,57063,57064],{"class":484}," /dev/ttySerialPort1",[474,57066,9007],{"class":503},[474,57068,1500],{"class":810},[474,57070,57071],{"class":484}," ttyUSB1\n",[474,57073,57074,57076,57078,57080,57082,57084,57086,57088,57090,57093,57095,57097],{"class":476,"line":547},[474,57075,57043],{"class":480},[474,57077,2685],{"class":510},[474,57079,29895],{"class":484},[474,57081,29895],{"class":484},[474,57083,57052],{"class":510},[474,57085,57055],{"class":484},[474,57087,57058],{"class":510},[474,57089,57061],{"class":510},[474,57091,57092],{"class":484}," /dev/ttySerialPort2",[474,57094,9007],{"class":503},[474,57096,1500],{"class":810},[474,57098,57099],{"class":484}," ttyUSB0\n",[439,57101,57102,57103],{},"To run the Java application later without root, we need a new user which has the privilege to access serial ports. To do\nso, just add a new user to group ",[471,57104,57105],{},"dialout",[464,57107,57109],{"className":466,"code":57108,"language":468,"meta":469,"style":469},"root@raspberrypi:/# useradd -m -d /home/rxtxpi -s /bin/bash -G dialout rxtxpi\n",[471,57110,57111],{"__ignoreMap":469},[474,57112,57113,57115,57118,57121,57123,57126,57128,57131,57134,57137],{"class":476,"line":477},[474,57114,56886],{"class":480},[474,57116,57117],{"class":484}," useradd",[474,57119,57120],{"class":510}," -m",[474,57122,2616],{"class":510},[474,57124,57125],{"class":484}," /home/rxtxpi",[474,57127,14052],{"class":510},[474,57129,57130],{"class":484}," /bin/bash",[474,57132,57133],{"class":510}," -G",[474,57135,57136],{"class":484}," dialout",[474,57138,57139],{"class":484}," rxtxpi\n",[3938,57141,57143],{"id":57142},"installing-software","Installing Software",[439,57145,57146],{},"Now we’re going to install the required software to communicate via serial adapters. For Java applications, we can use\nthe Java Communications API (JCA). JCA requires some native platform specific code which is called via Java Native\nInterface (JNI). We also need the Java part of JCA.",[439,57148,57149,57150,57156,57157,57162],{},"The ugly part – JCA is not provided by Oracle via JRE and I couldn’t find any downloads from Oracle. The good part –\nthere is an Open Source implementation of JCA called ",[1002,57151,57155],{"href":57152,"rel":57153,"title":57154},"http://rxtx.qbang.org/",[1006],"RXTX project website","RXTX",". The native\npart can be installed via librxtx-java deb package. As long as there is no RXTX jar in Maven Central you can use\nthis ",[1002,57158,57159],{"href":57159,"rel":57160,"title":57161},"https://github.com/grafjo/rxtx",[1006],"inofficial RXTX maven repository"," as the Maven\nrepository for RXTX.",[439,57164,57165],{},"We just install a JRE and RXTX on Pi, because compiling software on the Pi is really really slow! Install a Java 1.6\nJDK and Gradle > 1.2 on your desktop, build the Java application there and copy it to the Pi.",[464,57167,57169],{"className":466,"code":57168,"language":468,"meta":469,"style":469},"root@raspberrypi:/# apt-get install openjdk-6-jre librxtx-java\n",[471,57170,57171],{"__ignoreMap":469},[474,57172,57173,57175,57178,57180,57183],{"class":476,"line":477},[474,57174,56886],{"class":480},[474,57176,57177],{"class":484}," apt-get",[474,57179,10028],{"class":484},[474,57181,57182],{"class":484}," openjdk-6-jre",[474,57184,57185],{"class":484}," librxtx-java\n",[439,57187,57188],{},"Now the Pi is ready to run a Java application 🙂",[3938,57190,57192],{"id":57191},"running-sample-application","Running Sample Application",[439,57194,57195],{},"The sample application just displays the plugged in serial adapters of your Pi. So switch to your desktop and build the\nsample application:",[464,57197,57199],{"className":466,"code":57198,"language":468,"meta":469,"style":469},"joo@jgraf:~$ git clone git@github.com:grafjo/rxtxpi.git\nCloning into 'rxtxpi'...\nremote: Counting objects: 25, done.\nremote: Compressing objects: 100% (16/16), done.\nremote: Total 25 (delta 1), reused 24 (delta 0)\nReceiving objects: 100% (25/25), done.\nResolving deltas: 100% (1/1), done.\njoo@jgraf:~$ cd rxtxpi/\njoo@jgraf:~/rxtxpi$ gradle distZip\n:compileJava\nwarning: [options] bootstrap class path not set in conjunction with -source 1.6\n1 warning\n:processResources UP-TO-DATE\n:classes\n:jar\n:startScripts\n:distZip\nBUILD SUCCESSFUL\nTotal time: 4.458 secs\n\n",[471,57200,57201,57214,57224,57241,57256,57281,57293,57306,57316,57327,57332,57340,57347,57355,57360,57365,57370,57375,57383],{"__ignoreMap":469},[474,57202,57203,57206,57208,57211],{"class":476,"line":477},[474,57204,57205],{"class":480},"joo@jgraf:~$",[474,57207,19448],{"class":484},[474,57209,57210],{"class":484}," clone",[474,57212,57213],{"class":484}," git@github.com:grafjo/rxtxpi.git\n",[474,57215,57216,57219,57221],{"class":476,"line":507},[474,57217,57218],{"class":480},"Cloning",[474,57220,12708],{"class":484},[474,57222,57223],{"class":484}," 'rxtxpi'...\n",[474,57225,57226,57229,57232,57235,57238],{"class":476,"line":547},[474,57227,57228],{"class":480},"remote:",[474,57230,57231],{"class":484}," Counting",[474,57233,57234],{"class":484}," objects:",[474,57236,57237],{"class":484}," 25,",[474,57239,57240],{"class":484}," done.\n",[474,57242,57243,57245,57248,57250,57253],{"class":476,"line":584},[474,57244,57228],{"class":480},[474,57246,57247],{"class":484}," Compressing",[474,57249,57234],{"class":484},[474,57251,57252],{"class":484}," 100%",[474,57254,57255],{"class":503}," (16/16), done.\n",[474,57257,57258,57260,57263,57266,57269,57271,57274,57277,57279],{"class":476,"line":607},[474,57259,57228],{"class":480},[474,57261,57262],{"class":484}," Total",[474,57264,57265],{"class":510}," 25",[474,57267,57268],{"class":503}," (delta ",[474,57270,5008],{"class":510},[474,57272,57273],{"class":503},"), reused 24 (",[474,57275,57276],{"class":480},"delta",[474,57278,2414],{"class":510},[474,57280,1101],{"class":503},[474,57282,57283,57286,57288,57290],{"class":476,"line":642},[474,57284,57285],{"class":480},"Receiving",[474,57287,57234],{"class":484},[474,57289,57252],{"class":484},[474,57291,57292],{"class":503}," (25/25), done.\n",[474,57294,57295,57298,57301,57303],{"class":476,"line":663},[474,57296,57297],{"class":480},"Resolving",[474,57299,57300],{"class":484}," deltas:",[474,57302,57252],{"class":484},[474,57304,57305],{"class":503}," (1/1), done.\n",[474,57307,57308,57310,57313],{"class":476,"line":694},[474,57309,57205],{"class":480},[474,57311,57312],{"class":484}," cd",[474,57314,57315],{"class":484}," rxtxpi/\n",[474,57317,57318,57321,57324],{"class":476,"line":700},[474,57319,57320],{"class":480},"joo@jgraf:~/rxtxpi$",[474,57322,57323],{"class":484}," gradle",[474,57325,57326],{"class":484}," distZip\n",[474,57328,57329],{"class":476,"line":913},[474,57330,57331],{"class":480},":compileJava\n",[474,57333,57334,57337],{"class":476,"line":920},[474,57335,57336],{"class":480},"warning:",[474,57338,57339],{"class":503}," [options] bootstrap class path not set in conjunction with -source 1.6\n",[474,57341,57342,57344],{"class":476,"line":926},[474,57343,5008],{"class":480},[474,57345,57346],{"class":484}," warning\n",[474,57348,57349,57352],{"class":476,"line":932},[474,57350,57351],{"class":480},":processResources",[474,57353,57354],{"class":484}," UP-TO-DATE\n",[474,57356,57357],{"class":476,"line":938},[474,57358,57359],{"class":480},":classes\n",[474,57361,57362],{"class":476,"line":944},[474,57363,57364],{"class":480},":jar\n",[474,57366,57367],{"class":476,"line":950},[474,57368,57369],{"class":480},":startScripts\n",[474,57371,57372],{"class":476,"line":956},[474,57373,57374],{"class":480},":distZip\n",[474,57376,57377,57380],{"class":476,"line":962},[474,57378,57379],{"class":480},"BUILD",[474,57381,57382],{"class":484}," SUCCESSFUL\n",[474,57384,57385,57388,57391,57394],{"class":476,"line":4876},[474,57386,57387],{"class":480},"Total",[474,57389,57390],{"class":484}," time:",[474,57392,57393],{"class":510}," 4.458",[474,57395,57396],{"class":484}," secs\n",[439,57398,57399],{},"Copy the application to your Pi:",[464,57401,57403],{"className":466,"code":57402,"language":468,"meta":469,"style":469},"joo@jgraf:~/rxtxpi$ scp build/distributions/rxtxpi.zip rxtxpi@raspberry:/home/rxtxpi\n",[471,57404,57405],{"__ignoreMap":469},[474,57406,57407,57409,57412,57415],{"class":476,"line":477},[474,57408,57320],{"class":480},[474,57410,57411],{"class":484}," scp",[474,57413,57414],{"class":484}," build/distributions/rxtxpi.zip",[474,57416,57417],{"class":484}," rxtxpi@raspberry:/home/rxtxpi\n",[439,57419,57420,57421,57424,57425,1402],{},"Switch back to your Pi, unzip the rxtxpi.zip. To use the predefined aliases for serial ports, we have to modify the\ndefault JVM options to ",[471,57422,57423],{},"DEFAULT_JVM_OPTS=\"-Dgnu.io.rxtx.SerialPorts=/dev/ttySerialPort1:/dev/ttySerialPort2\""," inside the\nrxtx_pi startup script ",[471,57426,57427],{},"rxtx_pi/bin/rxtx_pi",[439,57429,57430],{},"Ok – when every thing was installed successfully, we will get this output from our Java application using RXTX",[464,57432,57434],{"className":466,"code":57433,"language":468,"meta":469,"style":469},"rxtxpi@raspberrypi ~/rxtx_pi/bin $ ./rxtx_pi\n[main] INFO de.synyx.rxtxpi.SerialPortUtils - Looking for serial ports\n[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort1\n[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort2\nrxtxpi@raspberrypi ~/rxtx_pi/bin $\n\n",[471,57435,57436,57450,57460,57465,57470],{"__ignoreMap":469},[474,57437,57438,57441,57444,57447],{"class":476,"line":477},[474,57439,57440],{"class":480},"rxtxpi@raspberrypi",[474,57442,57443],{"class":484}," ~/rxtx_pi/bin",[474,57445,57446],{"class":503}," $ ",[474,57448,57449],{"class":484},"./rxtx_pi\n",[474,57451,57452,57455,57457],{"class":476,"line":507},[474,57453,57454],{"class":503},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Looking ",[474,57456,2674],{"class":810},[474,57458,57459],{"class":503}," serial ports\n",[474,57461,57462],{"class":476,"line":547},[474,57463,57464],{"class":503},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort1\n",[474,57466,57467],{"class":476,"line":584},[474,57468,57469],{"class":503},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort2\n",[474,57471,57472,57474,57476],{"class":476,"line":607},[474,57473,57440],{"class":480},[474,57475,57443],{"class":484},[474,57477,57478],{"class":503}," $\n",[439,57480,57481],{},"So this blog post was about getting a Pi ready to run a RXTX Java application and verified that everything works well.\nThe next post will be about how to use the Java Communications API to read data from an inverter.",[1024,57483,57484],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":57486},[57487,57488,57489,57490,57491,57492],{"id":56799,"depth":507,"text":56800},{"id":56830,"depth":507,"text":56831},{"id":56851,"depth":507,"text":56852},{"id":56869,"depth":507,"text":56870},{"id":57142,"depth":507,"text":57143},{"id":57191,"depth":507,"text":57192},[1412],"2013-03-12T11:36:50","A few years ago, a good friend of mine installed a\\nsmall photovoltaic system on his roof. I’m\\nvery exited about installing some solar panels on a roof and start producing electricity. It turned out that the\\ninstalled inverters have massive quality problems. So the idea was born to monitor the whole setup like i know it from\\ncomputer systems. Each inverter in this system has a serial\\nport interface to transmit data.","https://synyx.de/blog/running-rxtx-on-your-raspberry-pi/",{},"/blog/running-rxtx-on-your-raspberry-pi",{"title":56757,"description":57500},"A few years ago, a good friend of mine installed a\nsmall photovoltaic system on his roof. I’m\nvery exited about installing some solar panels on a roof and start producing electricity. It turned out that the\ninstalled inverters have massive quality problems. So the idea was born to monitor the whole setup like i know it from\ncomputer systems. Each inverter in this system has a serial\nport interface to transmit data.","blog/running-rxtx-on-your-raspberry-pi",[711,57503,57504],"java-communications-api","raspberry-pi","A few years ago, a good friend of mine installed a small photovoltaic system on his roof. I’m very exited about installing some solar panels on a roof and start…","79v5-5wD_6RO5aAPYmhnIk6o4FCbRSZN6rZk6bciDiw",{"id":57508,"title":57509,"author":57510,"body":57511,"category":57694,"date":57695,"description":57696,"extension":1034,"link":57697,"meta":57698,"navigation":916,"path":57699,"seo":57700,"slug":57702,"stem":57703,"tags":57704,"teaser":57708,"__hash__":57709},"blog/blog/selenium-grid-windows.md","Acceptance testing at synyx – Part 4",[166],{"type":432,"value":57512,"toc":57686},[57513,57516,57531,57534,57538,57541,57554,57564,57595,57607,57616,57623,57637,57641,57644,57651,57655,57658,57661,57664,57668,57674,57677,57681,57684],[435,57514,57509],{"id":57515},"acceptance-testing-at-synyx-part-4",[439,57517,57518,57519,57524,57525,57530],{},"In the last posts we set up our infrastructure to be able\nto ",[1002,57520,57523],{"href":57521,"rel":57522},"http://blog.synyx.de/2013/01/remote-browsers/",[1006],"aquire Browsers that run on a remote host"," and we created\na ",[1002,57526,57529],{"href":57527,"rel":57528},"http://blog.synyx.de/2013/02/setup-selenium-grid/",[1006],"selenium Grid infrastructure"," that is scalable and reboot safe.",[439,57532,57533],{},"Within this post I want to describe how to extend the grid with windows nodes to be able to test on our beloved Internet\nExplorer.",[3938,57535,57537],{"id":57536},"adding-windows-nodes-to-the-grid","Adding windows nodes to the grid",[439,57539,57540],{},"To be able to run tests on Windows browsers I wanted to go a similar way as on the linux-machines: As soon as the\nmachine boots the selenium-server should get started and registers itself to the selenium-hub (set up like mentioned\nbefore).",[439,57542,57543,57544,57547,57548,57553],{},"First I installed a current version of Java JRE and created a folder ",[471,57545,57546],{},"C:\\selenium"," where I put all the needed files to (\nnode-config, selenium-server\njar, ",[1002,57549,57552],{"href":57550,"rel":57551},"http://code.google.com/p/selenium/wiki/InternetExplorerDriver",[1006],"InternetExplorerDriver"," and so on.",[439,57555,57556,57557,57560,57561,6562],{},"Then I created a startup-script ",[471,57558,57559],{},"selenium-server.bat"," which basically executes the selenium-server process the way the\nlinux-startscripts do and put it to ",[471,57562,57563],{},"C:\\selenium\\selenium-server.bat",[464,57565,57569],{"className":57566,"code":57567,"language":57568,"meta":469,"style":469},"language-cmd shiki shiki-themes github-light github-dark","\n\"C:\\Program Files (x86)\\Java\\jre7\"\\bin\\java\n -Dwebdriver.ie.driver=C:\\selenium\\IEDriverServer.exe\n -jar C:\\selenium\\selenium-server-standalone-2.28.0.jar\n -role node -nodeConfig C:\\selenium\\nodeconfig.json\n\n","cmd",[471,57570,57571,57575,57580,57585,57590],{"__ignoreMap":469},[474,57572,57573],{"class":476,"line":477},[474,57574,917],{"emptyLinePlaceholder":916},[474,57576,57577],{"class":476,"line":507},[474,57578,57579],{},"\"C:\\Program Files (x86)\\Java\\jre7\"\\bin\\java\n",[474,57581,57582],{"class":476,"line":547},[474,57583,57584],{}," -Dwebdriver.ie.driver=C:\\selenium\\IEDriverServer.exe\n",[474,57586,57587],{"class":476,"line":584},[474,57588,57589],{}," -jar C:\\selenium\\selenium-server-standalone-2.28.0.jar\n",[474,57591,57592],{"class":476,"line":607},[474,57593,57594],{}," -role node -nodeConfig C:\\selenium\\nodeconfig.json\n",[439,57596,22944,57597,57600,57601,57606],{},[471,57598,57599],{},"nodeconfig.json"," looks similar to the linux configurations except that the capabilities-section tells the hub\nthere are Internet Explorers.",[1002,57602,57605],{"href":57603,"rel":57604},"https://media.synyx.de/uploads//2013/02/nodeconfig.json_.txt",[1006],"Click here"," to see it.\nCurrently we dont add Chromes and Firefoxes to the hub but this would be possible.",[439,57608,57609,57610,57615],{},"In order to run the script as a service I used ",[1002,57611,57614],{"href":57612,"rel":57613},"http://nssm.cc/",[1006],"Non-Sucking Service Manager"," which registers to\nWindows’ services and reduces the amount of clicking that has to be done. It also takes care of restarting the service\nif it hangs and so on.",[439,57617,57618,57619,57622],{},"To get this done in Win7 you have to open an “Adminstrator Commandline” by clicking Start, enter cmd into the search\nfield and execute with ",[471,57620,57621],{},"CTRL+SHIFT+Return",". Then nssm can be used to install the service:",[464,57624,57626],{"className":57566,"code":57625,"language":57568,"meta":469,"style":469},"\nC:\\selenium\\nssm.exe install selenium-server C:\\selenium\\selenium-server.bat\n\n",[471,57627,57628,57632],{"__ignoreMap":469},[474,57629,57630],{"class":476,"line":477},[474,57631,917],{"emptyLinePlaceholder":916},[474,57633,57634],{"class":476,"line":507},[474,57635,57636],{},"C:\\selenium\\nssm.exe install selenium-server C:\\selenium\\selenium-server.bat\n",[1065,57638,57640],{"id":57639},"service-accessing-the-desktop","Service accessing the desktop",[439,57642,57643],{},"Because selenium-server needs to access the desktop you have to allow this by clicking your way though: Navigate\nthrough Controlpanel -> Services -> selenium-server -> Properties.",[439,57645,57646,57647,57650],{},"There go to the “Log On” tab and check ",[471,57648,57649],{},"[x] allow service to interact with desktop",". Start the service or reboot the\nmachine and you’re done. As soon as the service is started it should show up at the console of your selenium-hub node.",[1065,57652,57654],{"id":57653},"problem-1-uploading-files","Problem 1: Uploading files",[439,57656,57657],{},"Unfortunately we experienced a problem with the described solution: As soon as one of our selenium tests included a file\nupload a strange popup showed up in Internet Exporer that prevented the tests from succeeding.",[439,57659,57660],{},"Actually selenium does really nice work when it comes to uploading files: If a path to a file is entered into an\ninput-field WebDriver detects this and (in case of RemoteWebDriver) uploads it to a temporary file on the\nselenium-node. Then it “chooses” this file on the file-chooser in the browser. But somehow the corresponding dialog in\nInternet Explorer causes problems when the process is not running as a “real” user. This is probably because the\nservice-user has no “home-directory” where the file-upload dialog could be initialized to. And this causes an\nerror-popup which makes the test fail.",[439,57662,57663],{},"We fixed this by taking another approach: The service can be configured not to run as the service-user but as a given\nwindows user. As soon as we used this option the popup was gone and the tests that used file uploads went back to green.",[1065,57665,57667],{"id":57666},"problem-2-performance","Problem 2: Performance",[439,57669,57670,57671,57673],{},"Now our Windows7 node has some strange performance issues which are probably related to the “accessing the desktop”\nfeature. The tests run significantly slower (like factor 5 to 10 times) when the service is run as non-service user as\ndescribed in the solution for the upload problem. This also is the case when the service runs with\n",[471,57672,57649],{}," AND noone is logged in to the VM by rdesktop.",[439,57675,57676],{},"Currently we are investigating this issue and trying to work on a solution. A workaround to this could be keeping the\ncorresponding display session open somehow (e.g. by enabling VNC). As soon as we found a solution for this problem I’ll\npost an update.",[3938,57678,57680],{"id":57679},"coming-up-next","Coming up next…",[439,57682,57683],{},"This post completes (at least for now ;-)) all the technical stuff. Next we will look at what an acceptance test is,\nhow we define acceptance criteria and ultimately how we write and report these tests.",[1024,57685,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":57687},[57688,57693],{"id":57536,"depth":507,"text":57537,"children":57689},[57690,57691,57692],{"id":57639,"depth":547,"text":57640},{"id":57653,"depth":547,"text":57654},{"id":57666,"depth":547,"text":57667},{"id":57679,"depth":507,"text":57680},[1030],"2013-02-27T08:48:44","In the last posts we set up our infrastructure to be able\\nto aquire Browsers that run on a remote host and we created\\na selenium Grid infrastructure that is scalable and reboot safe.","https://synyx.de/blog/selenium-grid-windows/",{},"/blog/selenium-grid-windows",{"title":57509,"description":57701},"In the last posts we set up our infrastructure to be able\nto aquire Browsers that run on a remote host and we created\na selenium Grid infrastructure that is scalable and reboot safe.","selenium-grid-windows","blog/selenium-grid-windows",[57705,57706,20699,57707],"grid","rdekstop","windows","In the last posts we set up our infrastructure to be able to aquire Browsers that run on a remote host and we created a selenium Grid infrastructure that is…","D1h3MEKcBwrW8ZdU1R_Cqbe-Afqnj3OK58tyadQVVz0",{"id":57711,"title":57712,"author":57713,"body":57714,"category":58942,"date":58943,"description":58944,"extension":1034,"link":58945,"meta":58946,"navigation":916,"path":58947,"seo":58948,"slug":57718,"stem":58950,"tags":58951,"teaser":58953,"__hash__":58954},"blog/blog/monitoring-nih-style-part-2.md","Monitoring – NIH style (part 2)",[157],{"type":432,"value":57715,"toc":58936},[57716,57719,57729,57732,57736,57739,57755,57770,57774,57784,57787,57792,57795,57803,57806,57814,57817,57820,58055,58058,58067,58148,58151,58154,58256,58259,58262,58307,58321,58324,58792,58802,58811,58821,58824,58830,58833,58836,58840,58859,58871,58878,58881,58892,58901,58904,58913,58916,58930,58933],[435,57717,57712],{"id":57718},"monitoring-nih-style-part-2",[439,57720,57721,57722,57728],{},"This expands on the idea in\nthe ",[1002,57723,57727],{"href":57724,"rel":57725,"title":57726},"http://blog.synyx.de/2013/02/monitoring-nih-style",[1006],"Part 1","first part of this blog series",". We will still be\nworking NIH style here – this time to improve the visuals, user-interface and information density.",[439,57730,57731],{},"The idea still is:collect arbitrary information, stay small, display distilled information. The goal is to learn more\nhow to visualize things, and of course do it within the constraints mentioned in the previous blog entry.",[3938,57733,57735],{"id":57734},"prototype-2","Prototype #2",[439,57737,57738],{},"General idea:",[994,57740,57741,57749,57752],{},[997,57742,57743,57744,17548],{},"Web interface (",[1002,57745,57748],{"href":57746,"rel":57747,"title":57748},"https://web.archive.org/web/20130301163513/http://square.github.com:80/cubism/",[1006],"Cubism",[997,57750,57751],{},"real database",[997,57753,57754],{},"real programming language",[439,57756,57757,57758,57763,57764,57769],{},"Cubism (also as recently introduced into ",[1002,57759,57762],{"href":57760,"rel":57761,"title":57762},"http://www.jolokia.org/",[1006],"Jolokia",") is a wonderful Javascript library\nusing ",[1002,57765,57768],{"href":57766,"rel":57767,"title":57768},"http://d3js.org/",[1006],"D3"," to plot information in a browser window as horizon graphs. It features automatic\nupdates to the graphs – for which we will write a server-side application. As with before, environmental restrictions\napply (I would have loved to use Jolokia, but it is not available everywhere), so we will roll our own.",[1065,57771,57773],{"id":57772},"client","Client",[439,57775,57776,57777,57783],{},"While the documentation to cubism isn’t really bad, it lacks a little on the “how do I start to use it” side. The best\nsource for information I found to be the HTML source of the main cubism site. For further information\nthe ",[1002,57778,57782],{"href":57779,"rel":57780,"title":57781},"https://github.com/square/cubism/wiki/API-Reference",[1006],"Cubism API Reference","API documentation"," on the Cubism wiki\nis immensely useful.",[439,57785,57786],{},"Parts of a Cubism page:",[994,57788,57789],{},[997,57790,57791],{},"A metric definition",[439,57793,57794],{},"This is a Javascript function which feeds a cubism callback with an array of values. It gets a start and stop-date and\na stepping (time between updates/granularity of information).",[994,57796,57797,57800],{},[997,57798,57799],{},"An HTML element on which cubism can append the graphs to.",[997,57801,57802],{},"A cubism context, which sets up cubism-specifics.",[439,57804,57805],{},"Here the graphs are set up, most importantly how much information will be displayed.",[994,57807,57808,57811],{},[997,57809,57810],{},"The metric instances themselves",[997,57812,57813],{},"A set of d3 commands to put the graphs inside the specified HTML element.",[439,57815,57816],{},"For brevity I will try to limit all code here to the really necessary parts while being verbose enough that crucial\ninformation does not get lost.",[439,57818,57819],{},"HTML parts:",[464,57821,57823],{"className":15039,"code":57822,"language":15041,"meta":469,"style":469},"\u003Cstyle>\n @import url(//square.github.com/cubism/style.css);\n #mymetric {\n min-height: 155px;\n }\n\u003C/style>\n\u003Cdiv id=\"body\">\n \u003Ch2>Metric:\u003C/h2>\n \u003Cdiv id=\"mymetric\">\u003C/div>\n\u003C/div>\n\u003Cscript type=\"text/javascript\" src=\"http://d3js.org/d3.v2.js\">\u003C/script>\n\u003Cscript\n type=\"text/javascript\"\n src=\"http://raw.github.com/square/cubism/master/cubism.v1.js\"\n>\u003C/script>\n\u003Cscript\n type=\"text/javascript\"\n src=\"http://code.jquery.com/jquery-1.8.2.min.js\"\n>\u003C/script>\n\u003Cscript type=\"text/javascript\" src=\"mymetrics.js\">\u003C/script>\n",[471,57824,57825,57833,57848,57855,57870,57874,57882,57897,57910,57929,57937,57964,57971,57981,57991,57999,58005,58013,58022,58030],{"__ignoreMap":469},[474,57826,57827,57829,57831],{"class":476,"line":477},[474,57828,15048],{"class":503},[474,57830,1024],{"class":15051},[474,57832,15070],{"class":503},[474,57834,57835,57838,57841,57843,57846],{"class":476,"line":507},[474,57836,57837],{"class":810}," @import",[474,57839,57840],{"class":510}," url",[474,57842,1483],{"class":503},[474,57844,57845],{"class":14037},"//square.github.com/cubism/style.css",[474,57847,1495],{"class":503},[474,57849,57850,57853],{"class":476,"line":547},[474,57851,57852],{"class":480}," #mymetric",[474,57854,14027],{"class":503},[474,57856,57857,57860,57862,57865,57868],{"class":476,"line":584},[474,57858,57859],{"class":510}," min-height",[474,57861,4709],{"class":503},[474,57863,57864],{"class":510},"155",[474,57866,57867],{"class":810},"px",[474,57869,2139],{"class":503},[474,57871,57872],{"class":476,"line":607},[474,57873,15319],{"class":503},[474,57875,57876,57878,57880],{"class":476,"line":642},[474,57877,15168],{"class":503},[474,57879,1024],{"class":15051},[474,57881,15070],{"class":503},[474,57883,57884,57886,57888,57890,57892,57895],{"class":476,"line":663},[474,57885,15048],{"class":503},[474,57887,15027],{"class":15051},[474,57889,15185],{"class":480},[474,57891,811],{"class":503},[474,57893,57894],{"class":484},"\"body\"",[474,57896,15070],{"class":503},[474,57898,57899,57901,57903,57906,57908],{"class":476,"line":694},[474,57900,15075],{"class":503},[474,57902,3938],{"class":15051},[474,57904,57905],{"class":503},">Metric:\u003C/",[474,57907,3938],{"class":15051},[474,57909,15070],{"class":503},[474,57911,57912,57914,57916,57918,57920,57923,57925,57927],{"class":476,"line":700},[474,57913,15075],{"class":503},[474,57915,15027],{"class":15051},[474,57917,15185],{"class":480},[474,57919,811],{"class":503},[474,57921,57922],{"class":484},"\"mymetric\"",[474,57924,15192],{"class":503},[474,57926,15027],{"class":15051},[474,57928,15070],{"class":503},[474,57930,57931,57933,57935],{"class":476,"line":913},[474,57932,15168],{"class":503},[474,57934,15027],{"class":15051},[474,57936,15070],{"class":503},[474,57938,57939,57941,57943,57945,57947,57950,57953,57955,57958,57960,57962],{"class":476,"line":920},[474,57940,15048],{"class":503},[474,57942,14722],{"class":15051},[474,57944,15081],{"class":480},[474,57946,811],{"class":503},[474,57948,57949],{"class":484},"\"text/javascript\"",[474,57951,57952],{"class":480}," src",[474,57954,811],{"class":503},[474,57956,57957],{"class":484},"\"http://d3js.org/d3.v2.js\"",[474,57959,15192],{"class":503},[474,57961,14722],{"class":15051},[474,57963,15070],{"class":503},[474,57965,57966,57968],{"class":476,"line":926},[474,57967,15048],{"class":503},[474,57969,57970],{"class":15051},"script\n",[474,57972,57973,57976,57978],{"class":476,"line":932},[474,57974,57975],{"class":480}," type",[474,57977,811],{"class":503},[474,57979,57980],{"class":484},"\"text/javascript\"\n",[474,57982,57983,57986,57988],{"class":476,"line":938},[474,57984,57985],{"class":480}," src",[474,57987,811],{"class":503},[474,57989,57990],{"class":484},"\"http://raw.github.com/square/cubism/master/cubism.v1.js\"\n",[474,57992,57993,57995,57997],{"class":476,"line":944},[474,57994,15192],{"class":503},[474,57996,14722],{"class":15051},[474,57998,15070],{"class":503},[474,58000,58001,58003],{"class":476,"line":950},[474,58002,15048],{"class":503},[474,58004,57970],{"class":15051},[474,58006,58007,58009,58011],{"class":476,"line":956},[474,58008,57975],{"class":480},[474,58010,811],{"class":503},[474,58012,57980],{"class":484},[474,58014,58015,58017,58019],{"class":476,"line":962},[474,58016,57985],{"class":480},[474,58018,811],{"class":503},[474,58020,58021],{"class":484},"\"http://code.jquery.com/jquery-1.8.2.min.js\"\n",[474,58023,58024,58026,58028],{"class":476,"line":4876},[474,58025,15192],{"class":503},[474,58027,14722],{"class":15051},[474,58029,15070],{"class":503},[474,58031,58032,58034,58036,58038,58040,58042,58044,58046,58049,58051,58053],{"class":476,"line":4888},[474,58033,15048],{"class":503},[474,58035,14722],{"class":15051},[474,58037,15081],{"class":480},[474,58039,811],{"class":503},[474,58041,57949],{"class":484},[474,58043,57952],{"class":480},[474,58045,811],{"class":503},[474,58047,58048],{"class":484},"\"mymetrics.js\"",[474,58050,15192],{"class":503},[474,58052,14722],{"class":15051},[474,58054,15070],{"class":503},[439,58056,58057],{},"This will set up your basic structure and can be prettified as much as you care to.",[439,58059,58060,58061,6562],{},"Next up is configuring the\ncubism ",[1002,58062,58066],{"href":58063,"rel":58064,"title":58065},"https://github.com/square/cubism/wiki/Cubism#wiki-context",[1006],"Cubism Context API","context",[464,58068,58070],{"className":29602,"code":58069,"language":29604,"meta":469,"style":469},"var context = cubism\n .context()\n .serverDelay(500)\n .clientDelay(100)\n .step(10e3)\n .size(960);\n",[471,58071,58072,58084,58092,58106,58120,58134],{"__ignoreMap":469},[474,58073,58074,58076,58079,58081],{"class":476,"line":477},[474,58075,46757],{"class":810},[474,58077,58078],{"class":503}," context ",[474,58080,811],{"class":810},[474,58082,58083],{"class":503}," cubism\n",[474,58085,58086,58088,58090],{"class":476,"line":507},[474,58087,30085],{"class":503},[474,58089,58066],{"class":480},[474,58091,30249],{"class":503},[474,58093,58094,58096,58099,58101,58104],{"class":476,"line":547},[474,58095,30085],{"class":503},[474,58097,58098],{"class":480},"serverDelay",[474,58100,1483],{"class":503},[474,58102,58103],{"class":510},"500",[474,58105,1101],{"class":503},[474,58107,58108,58110,58113,58115,58118],{"class":476,"line":584},[474,58109,30085],{"class":503},[474,58111,58112],{"class":480},"clientDelay",[474,58114,1483],{"class":503},[474,58116,58117],{"class":510},"100",[474,58119,1101],{"class":503},[474,58121,58122,58124,58127,58129,58132],{"class":476,"line":607},[474,58123,30085],{"class":503},[474,58125,58126],{"class":480},"step",[474,58128,1483],{"class":503},[474,58130,58131],{"class":510},"10e3",[474,58133,1101],{"class":503},[474,58135,58136,58138,58141,58143,58146],{"class":476,"line":642},[474,58137,30085],{"class":503},[474,58139,58140],{"class":480},"size",[474,58142,1483],{"class":503},[474,58144,58145],{"class":510},"960",[474,58147,1495],{"class":503},[439,58149,58150],{},"This specifies that the server and client do not react instantly, that we only update the graph once each 10 seconds and\nthat at most we will display 960 data-points.",[439,58152,58153],{},"I will leave on how to generate the metrics as the last point, as that is the part where I had the most problems with.\nBut here is how we will generate some metric to be used:",[464,58155,58157],{"className":29602,"code":58156,"language":29604,"meta":469,"style":469},"var site = \"TST\";\nvar metricGenerator = metricGeneratorForHost(\"localhost:8080\");\nvar mReqTime = metricGenerator.metric(site, \"maxRequestTime\").divide(1000);\nmReqTime.toString = function () {\n return site + \" maxReqT\";\n};\n",[471,58158,58159,58173,58192,58225,58239,58252],{"__ignoreMap":469},[474,58160,58161,58163,58166,58168,58171],{"class":476,"line":477},[474,58162,46757],{"class":810},[474,58164,58165],{"class":503}," site ",[474,58167,811],{"class":810},[474,58169,58170],{"class":484}," \"TST\"",[474,58172,2139],{"class":503},[474,58174,58175,58177,58180,58182,58185,58187,58190],{"class":476,"line":507},[474,58176,46757],{"class":810},[474,58178,58179],{"class":503}," metricGenerator ",[474,58181,811],{"class":810},[474,58183,58184],{"class":480}," metricGeneratorForHost",[474,58186,1483],{"class":503},[474,58188,58189],{"class":484},"\"localhost:8080\"",[474,58191,1495],{"class":503},[474,58193,58194,58196,58199,58201,58204,58207,58210,58213,58215,58218,58220,58223],{"class":476,"line":547},[474,58195,46757],{"class":810},[474,58197,58198],{"class":503}," mReqTime ",[474,58200,811],{"class":810},[474,58202,58203],{"class":503}," metricGenerator.",[474,58205,58206],{"class":480},"metric",[474,58208,58209],{"class":503},"(site, ",[474,58211,58212],{"class":484},"\"maxRequestTime\"",[474,58214,11288],{"class":503},[474,58216,58217],{"class":480},"divide",[474,58219,1483],{"class":503},[474,58221,58222],{"class":510},"1000",[474,58224,1495],{"class":503},[474,58226,58227,58230,58233,58235,58237],{"class":476,"line":584},[474,58228,58229],{"class":503},"mReqTime.",[474,58231,58232],{"class":480},"toString",[474,58234,1661],{"class":810},[474,58236,29842],{"class":810},[474,58238,29622],{"class":503},[474,58240,58241,58243,58245,58247,58250],{"class":476,"line":607},[474,58242,23910],{"class":810},[474,58244,58165],{"class":503},[474,58246,23407],{"class":810},[474,58248,58249],{"class":484}," \" maxReqT\"",[474,58251,2139],{"class":503},[474,58253,58254],{"class":476,"line":642},[474,58255,29914],{"class":503},[439,58257,58258],{},"The metricGenerator returns a metric which returns the maximal request time of a JBoss request in milliseconds. We\nspecify a filter which devides each datapoint by 1000 so we get more readable numbers and define a toString function so\nthe description takes less space in the graph.",[439,58260,58261],{},"Now we bind the metrics to a HTML element:",[464,58263,58265],{"className":15039,"code":58264,"language":15041,"meta":469,"style":469},"d3.select(\"#mymetric\").call(function(div) { div.append(\"div\") .attr(\"class\",\n\"axis\") .call(context.axis().orient(\"top\")); div.selectAll(\".horizon\")\n.data([mReqTime]) .enter().append(\"div\") .attr(\"class\", \"horizon\")\n.call(context.horizon()); div.append(\"div\") .attr(\"class\", \"rule\")\n.call(context.rule()); }); // On mousemove, reposition the chart values to match\nthe rule. context.on(\"focus\", function(i) {\nd3.selectAll(\".value\").style(\"right\", i == null ? null : context.size() - i +\n\"px\"); });\n",[471,58266,58267,58272,58277,58282,58287,58292,58297,58302],{"__ignoreMap":469},[474,58268,58269],{"class":476,"line":477},[474,58270,58271],{"class":503},"d3.select(\"#mymetric\").call(function(div) { div.append(\"div\") .attr(\"class\",\n",[474,58273,58274],{"class":476,"line":507},[474,58275,58276],{"class":503},"\"axis\") .call(context.axis().orient(\"top\")); div.selectAll(\".horizon\")\n",[474,58278,58279],{"class":476,"line":547},[474,58280,58281],{"class":503},".data([mReqTime]) .enter().append(\"div\") .attr(\"class\", \"horizon\")\n",[474,58283,58284],{"class":476,"line":584},[474,58285,58286],{"class":503},".call(context.horizon()); div.append(\"div\") .attr(\"class\", \"rule\")\n",[474,58288,58289],{"class":476,"line":607},[474,58290,58291],{"class":503},".call(context.rule()); }); // On mousemove, reposition the chart values to match\n",[474,58293,58294],{"class":476,"line":642},[474,58295,58296],{"class":503},"the rule. context.on(\"focus\", function(i) {\n",[474,58298,58299],{"class":476,"line":663},[474,58300,58301],{"class":503},"d3.selectAll(\".value\").style(\"right\", i == null ? null : context.size() - i +\n",[474,58303,58304],{"class":476,"line":694},[474,58305,58306],{"class":503},"\"px\"); });\n",[439,58308,58309,58310,58316,58317,58320],{},"This produces a time axis on top and the horizon charts. Additionally we catch a mouse-over which will show the\nconcrete values for each horizon chart below the cursor. The horizon chart of course has more\noptions, ",[1002,58311,58315],{"href":58312,"rel":58313,"title":58314},"https://github.com/square/cubism/wiki/Horizon#wiki-colors",[1006],"Cubism Horizon API","color"," only being one of them.\nThe d3 ",[471,58318,58319],{},"data()"," function takes an array of one or more metric definitions.",[439,58322,58323],{},"Now for the metricGenerator, it is not bug free but it works for me TM.",[464,58325,58327],{"className":29602,"code":58326,"language":29604,"meta":469,"style":469},"var metricGeneratorForHost = function (host) {\n if (!arguments.length) host = \"localhost:8080\";\n var source = {};\n var cubism_cubeFormatDate = d3.time.format.iso;\n source.metric = function (site, expression) {\n return context.metric(\n function (start, stop, step, callback) {\n var url =\n host +\n \"/1.0/metric\" +\n \"?site=\" +\n encodeURIComponent(site) +\n \"&expression=\" +\n encodeURIComponent(expression) +\n \"&start=\" +\n cubism_cubeFormatDate(start) +\n \"&stop=\" +\n cubism_cubeFormatDate(stop) +\n \"&step=\" +\n step;\n d3.json(url, function (data) {\n if (!data) return callback(new Error(\"unable to load data\"));\n data.forEach(function (d) {\n cubism_cubeFormatDate.parse(d.date);\n d.value = parseInt(d.value);\n });\n callback(\n null,\n data.map(function (d) {\n return d.value;\n }),\n );\n });\n },\n \"\" + site + \" \" + expression,\n );\n };\n // Returns the Cube host.\n source.toString = function () {\n return \"\" + site + \" \" + expression;\n };\n return source;\n};\n",[471,58328,58329,58346,58372,58384,58396,58419,58430,58454,58465,58472,58480,58487,58497,58504,58513,58520,58530,58537,58546,58553,58558,58577,58608,58626,58637,58650,58655,58662,58669,58686,58694,58699,58704,58709,58714,58733,58737,58741,58746,58758,58777,58781,58788],{"__ignoreMap":469},[474,58330,58331,58333,58335,58337,58339,58341,58344],{"class":476,"line":477},[474,58332,46757],{"class":810},[474,58334,58184],{"class":480},[474,58336,1661],{"class":810},[474,58338,29842],{"class":810},[474,58340,15250],{"class":503},[474,58342,58343],{"class":14037},"host",[474,58345,23418],{"class":503},[474,58347,58348,58350,58352,58354,58357,58359,58362,58365,58367,58370],{"class":476,"line":507},[474,58349,2980],{"class":810},[474,58351,15250],{"class":503},[474,58353,18773],{"class":810},[474,58355,58356],{"class":510},"arguments",[474,58358,1402],{"class":503},[474,58360,58361],{"class":510},"length",[474,58363,58364],{"class":503},") host ",[474,58366,811],{"class":810},[474,58368,58369],{"class":484}," \"localhost:8080\"",[474,58371,2139],{"class":503},[474,58373,58374,58376,58379,58381],{"class":476,"line":547},[474,58375,29627],{"class":810},[474,58377,58378],{"class":503}," source ",[474,58380,811],{"class":810},[474,58382,58383],{"class":503}," {};\n",[474,58385,58386,58388,58391,58393],{"class":476,"line":584},[474,58387,29627],{"class":810},[474,58389,58390],{"class":503}," cubism_cubeFormatDate ",[474,58392,811],{"class":810},[474,58394,58395],{"class":503}," d3.time.format.iso;\n",[474,58397,58398,58401,58403,58405,58407,58409,58412,58414,58417],{"class":476,"line":607},[474,58399,58400],{"class":503}," source.",[474,58402,58206],{"class":480},[474,58404,1661],{"class":810},[474,58406,29842],{"class":810},[474,58408,15250],{"class":503},[474,58410,58411],{"class":14037},"site",[474,58413,520],{"class":503},[474,58415,58416],{"class":14037},"expression",[474,58418,23418],{"class":503},[474,58420,58421,58423,58426,58428],{"class":476,"line":642},[474,58422,23104],{"class":810},[474,58424,58425],{"class":503}," context.",[474,58427,58206],{"class":480},[474,58429,1555],{"class":503},[474,58431,58432,58435,58437,58439,58441,58444,58446,58448,58450,58452],{"class":476,"line":663},[474,58433,58434],{"class":810}," function",[474,58436,15250],{"class":503},[474,58438,19137],{"class":14037},[474,58440,520],{"class":503},[474,58442,58443],{"class":14037},"stop",[474,58445,520],{"class":503},[474,58447,58126],{"class":14037},[474,58449,520],{"class":503},[474,58451,46458],{"class":14037},[474,58453,23418],{"class":503},[474,58455,58456,58459,58462],{"class":476,"line":694},[474,58457,58458],{"class":810}," var",[474,58460,58461],{"class":503}," url ",[474,58463,58464],{"class":810},"=\n",[474,58466,58467,58470],{"class":476,"line":700},[474,58468,58469],{"class":503}," host ",[474,58471,23452],{"class":810},[474,58473,58474,58477],{"class":476,"line":913},[474,58475,58476],{"class":484}," \"/1.0/metric\"",[474,58478,58479],{"class":810}," +\n",[474,58481,58482,58485],{"class":476,"line":920},[474,58483,58484],{"class":484}," \"?site=\"",[474,58486,58479],{"class":810},[474,58488,58489,58492,58495],{"class":476,"line":926},[474,58490,58491],{"class":480}," encodeURIComponent",[474,58493,58494],{"class":503},"(site) ",[474,58496,23452],{"class":810},[474,58498,58499,58502],{"class":476,"line":932},[474,58500,58501],{"class":484}," \"&expression=\"",[474,58503,58479],{"class":810},[474,58505,58506,58508,58511],{"class":476,"line":938},[474,58507,58491],{"class":480},[474,58509,58510],{"class":503},"(expression) ",[474,58512,23452],{"class":810},[474,58514,58515,58518],{"class":476,"line":944},[474,58516,58517],{"class":484}," \"&start=\"",[474,58519,58479],{"class":810},[474,58521,58522,58525,58528],{"class":476,"line":950},[474,58523,58524],{"class":480}," cubism_cubeFormatDate",[474,58526,58527],{"class":503},"(start) ",[474,58529,23452],{"class":810},[474,58531,58532,58535],{"class":476,"line":956},[474,58533,58534],{"class":484}," \"&stop=\"",[474,58536,58479],{"class":810},[474,58538,58539,58541,58544],{"class":476,"line":962},[474,58540,58524],{"class":480},[474,58542,58543],{"class":503},"(stop) ",[474,58545,23452],{"class":810},[474,58547,58548,58551],{"class":476,"line":4876},[474,58549,58550],{"class":484}," \"&step=\"",[474,58552,58479],{"class":810},[474,58554,58555],{"class":476,"line":4888},[474,58556,58557],{"class":503}," step;\n",[474,58559,58560,58563,58565,58568,58570,58572,58575],{"class":476,"line":4900},[474,58561,58562],{"class":503}," d3.",[474,58564,496],{"class":480},[474,58566,58567],{"class":503},"(url, ",[474,58569,14021],{"class":810},[474,58571,15250],{"class":503},[474,58573,58574],{"class":14037},"data",[474,58576,23418],{"class":503},[474,58578,58579,58582,58584,58586,58589,58591,58593,58595,58597,58600,58602,58605],{"class":476,"line":4913},[474,58580,58581],{"class":810}," if",[474,58583,15250],{"class":503},[474,58585,18773],{"class":810},[474,58587,58588],{"class":503},"data) ",[474,58590,30018],{"class":810},[474,58592,29642],{"class":480},[474,58594,1483],{"class":503},[474,58596,46528],{"class":810},[474,58598,58599],{"class":480}," Error",[474,58601,1483],{"class":503},[474,58603,58604],{"class":484},"\"unable to load data\"",[474,58606,58607],{"class":503},"));\n",[474,58609,58610,58613,58615,58617,58619,58621,58624],{"class":476,"line":4921},[474,58611,58612],{"class":503}," data.",[474,58614,29930],{"class":480},[474,58616,1483],{"class":503},[474,58618,14021],{"class":810},[474,58620,15250],{"class":503},[474,58622,58623],{"class":14037},"d",[474,58625,23418],{"class":503},[474,58627,58628,58631,58634],{"class":476,"line":4932},[474,58629,58630],{"class":503}," cubism_cubeFormatDate.",[474,58632,58633],{"class":480},"parse",[474,58635,58636],{"class":503},"(d.date);\n",[474,58638,58639,58642,58644,58647],{"class":476,"line":4938},[474,58640,58641],{"class":503}," d.value ",[474,58643,811],{"class":810},[474,58645,58646],{"class":480}," parseInt",[474,58648,58649],{"class":503},"(d.value);\n",[474,58651,58652],{"class":476,"line":4946},[474,58653,58654],{"class":503}," });\n",[474,58656,58657,58660],{"class":476,"line":4952},[474,58658,58659],{"class":480}," callback",[474,58661,1555],{"class":503},[474,58663,58664,58667],{"class":476,"line":4957},[474,58665,58666],{"class":510}," null",[474,58668,4715],{"class":503},[474,58670,58671,58674,58676,58678,58680,58682,58684],{"class":476,"line":4969},[474,58672,58673],{"class":503}," data.",[474,58675,25467],{"class":480},[474,58677,1483],{"class":503},[474,58679,14021],{"class":810},[474,58681,15250],{"class":503},[474,58683,58623],{"class":14037},[474,58685,23418],{"class":503},[474,58687,58688,58691],{"class":476,"line":4990},[474,58689,58690],{"class":810}," return",[474,58692,58693],{"class":503}," d.value;\n",[474,58695,58696],{"class":476,"line":5001},[474,58697,58698],{"class":503}," }),\n",[474,58700,58701],{"class":476,"line":5013},[474,58702,58703],{"class":503}," );\n",[474,58705,58706],{"class":476,"line":5024},[474,58707,58708],{"class":503}," });\n",[474,58710,58711],{"class":476,"line":5035},[474,58712,58713],{"class":503}," },\n",[474,58715,58716,58719,58721,58723,58725,58728,58730],{"class":476,"line":5047},[474,58717,58718],{"class":484}," \"\"",[474,58720,46303],{"class":810},[474,58722,58165],{"class":503},[474,58724,23407],{"class":810},[474,58726,58727],{"class":484}," \" \"",[474,58729,46303],{"class":810},[474,58731,58732],{"class":503}," expression,\n",[474,58734,58735],{"class":476,"line":5055},[474,58736,23309],{"class":503},[474,58738,58739],{"class":476,"line":5062},[474,58740,23217],{"class":503},[474,58742,58743],{"class":476,"line":5067},[474,58744,58745],{"class":2277}," // Returns the Cube host.\n",[474,58747,58748,58750,58752,58754,58756],{"class":476,"line":5072},[474,58749,58400],{"class":503},[474,58751,58232],{"class":480},[474,58753,1661],{"class":810},[474,58755,29842],{"class":810},[474,58757,29622],{"class":503},[474,58759,58760,58762,58764,58766,58768,58770,58772,58774],{"class":476,"line":5084},[474,58761,23104],{"class":810},[474,58763,2994],{"class":484},[474,58765,46303],{"class":810},[474,58767,58165],{"class":503},[474,58769,23407],{"class":810},[474,58771,58727],{"class":484},[474,58773,46303],{"class":810},[474,58775,58776],{"class":503}," expression;\n",[474,58778,58779],{"class":476,"line":5100},[474,58780,23217],{"class":503},[474,58782,58783,58785],{"class":476,"line":5111},[474,58784,23910],{"class":810},[474,58786,58787],{"class":503}," source;\n",[474,58789,58790],{"class":476,"line":5122},[474,58791,29914],{"class":503},[439,58793,58794,58795,34572,58798,58801],{},"This sets up the basic prototype of a metric which does background HTTP requests to pull data. We will deliver JSON from\nthe server, which gets parsed d3. Cubism does not care at all in which form it gets data, all it cares about are the\nfinal data-points in the callback function. The array with the data-points has to be the size of:\n",[471,58796,58797],{},"stop-start/stepping",[471,58799,58800],{},"d3.json(url, callback)"," gets the JSON, parses it and hands it over to the callback.",[464,58803,58805],{"className":16895,"code":58804,"language":16897,"meta":469,"style":469},"[{ site: \"TST\", date: \"2006-01-02T15:04:05.000Z\", value: \"12021\" }]\n",[471,58806,58807],{"__ignoreMap":469},[474,58808,58809],{"class":476,"line":477},[474,58810,58804],{},[439,58812,58813,58814,58817,58818,58820],{},"It takes each element of the array, converts the value to an int and finally (quite convoluted) hands an array of\nintegers to the ",[471,58815,58816],{},"callback(null, [12021])",". If it somehow fails to contact the server correctly it will call the\n",[471,58819,46458],{}," with an error.",[439,58822,58823],{},"This finishes up the client side of things. We just need a web-service which delivers the above JSON.",[439,58825,58826,58827],{},"Example Request:\n",[471,58828,58829],{},"http://localhost:8080/1.0/metric?site=TST&expression=maxRequestTime&start=2006-01-02T15:04:05.000Z&stop=2006-01-02T15:05:25.000Z&step=10000",[439,58831,58832],{},"A note on the behavior how Cubism will execute the metric callback. On site load, it will attempt to fill all\ndata-points. So it will generate a massive request for all missing information. Once it is loaded, I have observed (at\nleast for a stepping of 10 seconds) that it will ask for the last 70 seconds, not only for the last value.",[439,58834,58835],{},"The interpolation of the data is not done client side here, although it could be done here. Additionally there are a few\nbugs where I just didn’t have the time to investigate more. For example the last 7 data-points are always the same, and\nwhen asking for the full data-set it gets the time wrong (by the amount of daylight saving time). Not all too critical\nfor me so I didn’t investigate further.",[1065,58837,58839],{"id":58838},"server","Server",[439,58841,58842,58843,58848,58849,15250,58854,11288],{},"The server part is split into the web-server part and the information gathering part. A sqlite3 database is used to\nexchange data. As we are adventurous, we will use the new fancy ",[1002,58844,58847],{"href":58845,"rel":58846,"title":58847},"http://golang.org/",[1006],"Go"," language\nfrom ",[1002,58850,58853],{"href":58851,"rel":58852,"title":58853},"http://en.wikipedia.org/wiki/Rob_Pike",[1006],"Rob Pike",[1002,58855,48564],{"href":58856,"rel":58857,"title":58858},"http://google.com/",[1006],"Google",[439,58860,58861,58862,6562,58866],{},"To avoid all too much code show, I have pushed a (cleaned) version of my project\nto ",[1002,58863,22987],{"href":58864,"rel":58865,"title":22987},"https://github.com/",[1006],[1002,58867,58868],{"href":58868,"rel":58869,"title":58870},"https://github.com/BuJo/cubismsource",[1006],"cubismsource",[439,58872,58873,58874,58877],{},"There you can follow my ",[29523,58875,58876],{},"misadventures"," development path and learning process – my proficiency in Go is sadly lacking.\nBut, I have put up a (hopefully) decent README to get an interesting party started.",[439,58879,58880],{},"The code is split into",[994,58882,58883,58886,58889],{},[997,58884,58885],{},"cubismsource: web-application which delivers the HTML site and the metrics from the database to cubism",[997,58887,58888],{},"jboss2sqlite: application to periodically save the jboss status into the database",[997,58890,58891],{},"jbossinfo: small library for handling the jboss status xml",[439,58893,58894],{},[1002,58895,58898],{"href":58896,"rel":58897},"https://media.synyx.de/uploads//2013/02/Screenshot-from-2013-02-01-145206.png",[1006],[2205,58899],{"alt":58900,"src":58896},"Screenshot from 2013-02-01 14:52:06",[439,58902,58903],{},"This is a partial screenshot of three running JBoss instances running almost the same application. The observant reader\nwill of course spot oddities (like the maximum request time of the first instance climbing quite high there, like the\nfirst instance having a hiccup at the end).",[439,58905,58906,58907,58912],{},"The result is quite close to something\nlike ",[1002,58908,58911],{"href":58909,"rel":58910,"title":58911},"https://web.archive.org/web/20130325101848/http://square.github.com:80/cube/",[1006],"Cube"," or Jolokia – just a\nlittle less clever! Feel free to use the code in any way you want – it is “finished” in the sense that it is feature\ncomplete and unlikely to be extended by me.",[439,58914,58915],{},"So, what did we gain by putting the odd minute here and there into this?",[994,58917,58918,58921,58924,58927],{},[997,58919,58920],{},"The need of a third monitor",[997,58922,58923],{},"Beautiful forms and colors – the co-workers’ envy",[997,58925,58926],{},"Knowledge of visualizing things via Cubism",[997,58928,58929],{},"But, we loose points for the added complexity (~650 LoC) – which makes it less accessible from outside.",[439,58931,58932],{},"As with my previous blog articles – this is less about a “solution”, more a way to understand and learn a little more.",[1024,58934,58935],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":469,"searchDepth":507,"depth":507,"links":58937},[58938],{"id":57734,"depth":507,"text":57735,"children":58939},[58940,58941],{"id":57772,"depth":547,"text":57773},{"id":58838,"depth":547,"text":58839},[9045,1030],"2013-02-19T17:36:29","This expands on the idea in\\nthe first part of this blog series. We will still be\\nworking NIH style here – this time to improve the visuals, user-interface and information density.","https://synyx.de/blog/monitoring-nih-style-part-2/",{},"/blog/monitoring-nih-style-part-2",{"title":57712,"description":58949},"This expands on the idea in\nthe first part of this blog series. We will still be\nworking NIH style here – this time to improve the visuals, user-interface and information density.","blog/monitoring-nih-style-part-2",[58952,51447,14721,35140],"cubism","This expands on the idea in the first part of this blog series. We will still be working NIH style here – this time to improve the visuals, user-interface and…","lc5fXXX_RtAtEN5Nvs_Lxm9BfeHHBwprNf8KzlM6X90",{"id":58956,"title":58957,"author":58958,"body":58959,"category":59475,"date":59476,"description":59477,"extension":1034,"link":59478,"meta":59479,"navigation":916,"path":59480,"seo":59481,"slug":58963,"stem":59482,"tags":59483,"teaser":59485,"__hash__":59486},"blog/blog/monitoring-nih-style.md","Monitoring – NIH style",[157],{"type":432,"value":58960,"toc":59472},[58961,58964,58967,58970,59008,59011,59014,59028,59031,59034,59037,59045,59048,59050,59058,59069,59124,59127,59157,59160,59254,59263,59385,59388,59397,59406,59409,59425,59428,59431,59434,59437,59440,59443,59466,59469],[435,58962,58957],{"id":58963},"monitoring-nih-style",[439,58965,58966],{},"Not being a pure Developer but more of a DevOp brings all sorts of interesting problems. When you not only sell software\nbut are also included in the whole life-cycle you get a different view on things.",[439,58968,58969],{},"So there you have it – the production deployment of some software running at the customers site exhibits problems and\nwill be restarted by the support. This rarely leaves time to really analyze what went wrong, except a postmortem\nanalysis. This blog post is about ideas on how to be a little pro-active in gathering information before and after\nthings go horribly wrong.",[439,58971,58972,58973,58978,58979,58984,58985,58990,58991,58995,58996,59002,59003,59007],{},"There are many ways to monitor a system.",[1002,58974,58977],{"href":58975,"rel":58976,"title":58977},"http://www.nagios.org",[1006],"Nagios"," allows for monitoring the general\nhealth of a system. Something like ",[1002,58980,58983],{"href":58981,"rel":58982,"title":58983},"http://ganglia.sourceforge.net",[1006],"Ganglia"," can give an overview over\nperformance values. ",[1002,58986,58989],{"href":58987,"rel":58988,"title":58989},"http://graylog2.org/",[1006],"Graylog2"," can be used to monitor logs. In the Java\nworld ",[1002,58992,57762],{"href":58993,"rel":58994,"title":57762},"http://www.jolokia.org",[1006]," provides a good view to JMX and thus to the JVM. Specialized Software\nlike the ",[1002,58997,59001],{"href":58998,"rel":58999,"title":59000},"http://www.jboss.org/rhq",[1006],"RHQ","JBoss RHQ"," allows monitoring of the middleware and applications. Running\napplications on virtual servers provides additional management tools to monitor running virtual instances. A\nconfiguration management software like ",[1002,59004,51207],{"href":59005,"rel":59006,"title":51207},"http://puppetlabs.com/",[1006]," might provide additional information.\nAll this to detect irregularities in the system or to gain insight into a specific set of events.",[439,59009,59010],{},"So there is a huge variety in what to monitor, how to go about it, how notifications work, how much information can be\ngained.",[439,59012,59013],{},"So we have all those nice tools at our disposal – so – what is wrong?",[994,59015,59016,59019,59022,59025],{},[997,59017,59018],{},"No direct control over production systems.",[997,59020,59021],{},"Irregular failures.",[997,59023,59024],{},"Postmortem analysis hard/impossible due to missing information.",[997,59026,59027],{},"There is never enough time TM.",[439,59029,59030],{},"So, lets work with what we have – and do it with the least amount of work and hassle possible for everyone involved.",[3938,59032,59033],{"id":46493},"Prototype",[439,59035,59036],{},"The problem in the production deployment:",[994,59038,59039,59042],{},[997,59040,59041],{},"A memory leak in a Java application deployed in a JBoss 4 at (seemingly) random times at different sites.",[997,59043,59044],{},"No clue in the available information (logfiles) indicates a direct problem",[439,59046,59047],{},"We need more information. Optimally, it would be nice to see if we can detect the problem before the customer does and\nwarn the support staff of the production site. Failing that, we at least have more information to work on during\nanalysis.",[439,59049,57738],{},[994,59051,59052,59055],{},[997,59053,59054],{},"Get memory status from running application small intervals and save for later.",[997,59056,59057],{},"Plot the information.",[439,59059,59060,59061,59064,59065,59068],{},"With a base JBoss 4 we can get access to JVM information via ",[471,59062,59063],{},"/status?XML=true"," (in a few variants) and in\n",[471,59066,59067],{},"/web-console/ServerInfo.jsp",". Gathering the information for later is straight forward, write a 3 line shell script\nrunning in the background saving the output of JBoss to disk.",[464,59070,59072],{"className":466,"code":59071,"language":468,"meta":469,"style":469},"while true; do\ncurl -n $url -o \"html_src.$host/$(date --rfc-3339=seconds).html\"\nsleep $polltime\ndone\n",[471,59073,59074,59084,59112,59120],{"__ignoreMap":469},[474,59075,59076,59078,59080,59082],{"class":476,"line":477},[474,59077,13497],{"class":810},[474,59079,14224],{"class":510},[474,59081,55136],{"class":503},[474,59083,2701],{"class":810},[474,59085,59086,59088,59090,59093,59096,59099,59102,59104,59106,59109],{"class":476,"line":507},[474,59087,6692],{"class":480},[474,59089,6905],{"class":510},[474,59091,59092],{"class":503}," $url ",[474,59094,59095],{"class":510},"-o",[474,59097,59098],{"class":484}," \"html_src.",[474,59100,59101],{"class":503},"$host",[474,59103,55701],{"class":484},[474,59105,2756],{"class":480},[474,59107,59108],{"class":510}," --rfc-3339=seconds",[474,59110,59111],{"class":484},").html\"\n",[474,59113,59114,59117],{"class":476,"line":547},[474,59115,59116],{"class":480},"sleep",[474,59118,59119],{"class":503}," $polltime\n",[474,59121,59122],{"class":476,"line":584},[474,59123,2879],{"class":810},[439,59125,59126],{},"We pretend some time has passed and the problem on the production machine occurred again. We need to aggregate the\ncollected information. We iterate over a set of files and just grab the information we need to save it nicely structured\nin a CSV file for post-processing. We need a format our plotting application can read easily.",[464,59128,59130],{"className":16895,"code":59129,"language":16897,"meta":469,"style":469},"date,Free_Memory,Max_Memory,Total_Memory\n\"2013-01-10 00:00:09+01:00\",499292808,2067988480,714670080\n\"2013-01-10 00:00:19+01:00\",485999352,2067988480,714670080\n\"2013-01-10 00:00:29+01:00\",579714128,2067988480,714670080\n\"2013-01-10 00:00:39+01:00\",565887928,2067988480,714670080\n",[471,59131,59132,59137,59142,59147,59152],{"__ignoreMap":469},[474,59133,59134],{"class":476,"line":477},[474,59135,59136],{},"date,Free_Memory,Max_Memory,Total_Memory\n",[474,59138,59139],{"class":476,"line":507},[474,59140,59141],{},"\"2013-01-10 00:00:09+01:00\",499292808,2067988480,714670080\n",[474,59143,59144],{"class":476,"line":547},[474,59145,59146],{},"\"2013-01-10 00:00:19+01:00\",485999352,2067988480,714670080\n",[474,59148,59149],{"class":476,"line":584},[474,59150,59151],{},"\"2013-01-10 00:00:29+01:00\",579714128,2067988480,714670080\n",[474,59153,59154],{"class":476,"line":607},[474,59155,59156],{},"\"2013-01-10 00:00:39+01:00\",565887928,2067988480,714670080\n",[439,59158,59159],{},"And the conversion script from XML to CSV:",[464,59161,59163],{"className":466,"code":59162,"language":468,"meta":469,"style":469},"for filename in html_src.$host/*; do\nstat=\"$(xmllint --format $filename | sed -n\n's/.*memory.*free=\"\\([0-9]*\\)\".*total=\"\\([0-9]*\\)\".*max=\"\\([0-9]*\\)\".*/\\1,\\2,\\3/p')\"\necho \"\\\"$(basename -- $file .html)\\\",$stat\"\ndone\n",[471,59164,59165,59186,59211,59219,59250],{"__ignoreMap":469},[474,59166,59167,59169,59172,59174,59177,59179,59182,59184],{"class":476,"line":477},[474,59168,2674],{"class":810},[474,59170,59171],{"class":503}," filename ",[474,59173,2680],{"class":810},[474,59175,59176],{"class":484}," html_src.",[474,59178,59101],{"class":503},[474,59180,59181],{"class":484},"/*",[474,59183,55136],{"class":503},[474,59185,2701],{"class":810},[474,59187,59188,59191,59193,59195,59198,59201,59204,59206,59208],{"class":476,"line":507},[474,59189,59190],{"class":503},"stat",[474,59192,811],{"class":810},[474,59194,55099],{"class":484},[474,59196,59197],{"class":480},"xmllint",[474,59199,59200],{"class":510}," --format",[474,59202,59203],{"class":503}," $filename",[474,59205,14174],{"class":810},[474,59207,14177],{"class":480},[474,59209,59210],{"class":510}," -n\n",[474,59212,59213,59216],{"class":476,"line":547},[474,59214,59215],{"class":480},"'s/.*memory.*free=\"\\([0-9]*\\)\".*total=\"\\([0-9]*\\)\".*max=\"\\([0-9]*\\)\".*/\\1,\\2,\\3/p'",[474,59217,59218],{"class":484},")\"\n",[474,59220,59221,59223,59225,59227,59229,59232,59235,59238,59241,59243,59245,59248],{"class":476,"line":584},[474,59222,2470],{"class":510},[474,59224,2720],{"class":484},[474,59226,12616],{"class":510},[474,59228,2711],{"class":484},[474,59230,59231],{"class":480},"basename",[474,59233,59234],{"class":510}," --",[474,59236,59237],{"class":503}," $file",[474,59239,59240],{"class":484}," .html)",[474,59242,12616],{"class":510},[474,59244,1489],{"class":484},[474,59246,59247],{"class":503},"$stat",[474,59249,2744],{"class":484},[474,59251,59252],{"class":476,"line":607},[474,59253,2879],{"class":810},[439,59255,59256,59257,59262],{},"Feeding that into e.g. OpenOffice by hand isn’t hard but a little more hand-holding would be nice for convenience. Lets\ncreate a PDF document and let our document viewer handle the reload when the file changes. There are choices when it\ncomes to generating graphs – my personal favorite is ",[1002,59258,59261],{"href":59259,"rel":59260,"title":59261},"http://www.r-project.org/",[1006],"R",", a tool for statistical\ncomputing.",[464,59264,59268],{"className":59265,"code":59266,"language":59267,"meta":469,"style":469},"language-plain shiki shiki-themes github-light github-dark","!/usr/bin/R\nrequire(forecast)\noptions(digits.secs=6)\nargs \u003C- commandArgs(trailingOnly = TRUE)\nr2 \u003C- read.csv(file=args[1],head=TRUE,sep=\",\")\nr2$date = as.POSIXct(r2$date)\nr2$Free_Memory = as.numeric(r2$Free_Memory)\nr2$Max_Memory = as.numeric(r2$Max_Memory)\nr2$Total_Memory = as.numeric(r2$Total_Memory)\nr2$Free_Memory_avg \u003C- ma(r2$Free_Memory, 12)\nr2$Max_Memory_avg \u003C- ma(r2$Max_Memory, 12)\nr2$Total_Memory_avg \u003C- ma(r2$Total_Memory, 12)\nr2$used \u003C- r2$Total_Memory - r2$Free_Memory\nr2$used_avg \u003C- ma(r2$used, 3)\nmaxmem \u003C- (max(r2$Max_Memory))\npdf(\"ram.pdf\")\nplot(range(r2$date), range(r2$Max_Memory), type=\"n\",main=paste(args[2],\n\"JVM Used Mem\\n(\",args[3],\")\"),ylab=\"Memory\n(MB)\",xlab=\"Time\",ylim=c(50,maxmem))\nlines(r2$date, r2$Total_Memory_avg, type=\"l\",col=\"blue\")\nlines(r2$date, r2$Max_Memory_avg, type=\"l\",col=\"red\")\nlines(r2$date, r2$used_avg, type=\"l\",col=\"black\")\ndev.off()\n","plain",[471,59269,59270,59275,59280,59285,59290,59295,59300,59305,59310,59315,59320,59325,59330,59335,59340,59345,59350,59355,59360,59365,59370,59375,59380],{"__ignoreMap":469},[474,59271,59272],{"class":476,"line":477},[474,59273,59274],{},"!/usr/bin/R\n",[474,59276,59277],{"class":476,"line":507},[474,59278,59279],{},"require(forecast)\n",[474,59281,59282],{"class":476,"line":547},[474,59283,59284],{},"options(digits.secs=6)\n",[474,59286,59287],{"class":476,"line":584},[474,59288,59289],{},"args \u003C- commandArgs(trailingOnly = TRUE)\n",[474,59291,59292],{"class":476,"line":607},[474,59293,59294],{},"r2 \u003C- read.csv(file=args[1],head=TRUE,sep=\",\")\n",[474,59296,59297],{"class":476,"line":642},[474,59298,59299],{},"r2$date = as.POSIXct(r2$date)\n",[474,59301,59302],{"class":476,"line":663},[474,59303,59304],{},"r2$Free_Memory = as.numeric(r2$Free_Memory)\n",[474,59306,59307],{"class":476,"line":694},[474,59308,59309],{},"r2$Max_Memory = as.numeric(r2$Max_Memory)\n",[474,59311,59312],{"class":476,"line":700},[474,59313,59314],{},"r2$Total_Memory = as.numeric(r2$Total_Memory)\n",[474,59316,59317],{"class":476,"line":913},[474,59318,59319],{},"r2$Free_Memory_avg \u003C- ma(r2$Free_Memory, 12)\n",[474,59321,59322],{"class":476,"line":920},[474,59323,59324],{},"r2$Max_Memory_avg \u003C- ma(r2$Max_Memory, 12)\n",[474,59326,59327],{"class":476,"line":926},[474,59328,59329],{},"r2$Total_Memory_avg \u003C- ma(r2$Total_Memory, 12)\n",[474,59331,59332],{"class":476,"line":932},[474,59333,59334],{},"r2$used \u003C- r2$Total_Memory - r2$Free_Memory\n",[474,59336,59337],{"class":476,"line":938},[474,59338,59339],{},"r2$used_avg \u003C- ma(r2$used, 3)\n",[474,59341,59342],{"class":476,"line":944},[474,59343,59344],{},"maxmem \u003C- (max(r2$Max_Memory))\n",[474,59346,59347],{"class":476,"line":950},[474,59348,59349],{},"pdf(\"ram.pdf\")\n",[474,59351,59352],{"class":476,"line":956},[474,59353,59354],{},"plot(range(r2$date), range(r2$Max_Memory), type=\"n\",main=paste(args[2],\n",[474,59356,59357],{"class":476,"line":962},[474,59358,59359],{},"\"JVM Used Mem\\n(\",args[3],\")\"),ylab=\"Memory\n",[474,59361,59362],{"class":476,"line":4876},[474,59363,59364],{},"(MB)\",xlab=\"Time\",ylim=c(50,maxmem))\n",[474,59366,59367],{"class":476,"line":4888},[474,59368,59369],{},"lines(r2$date, r2$Total_Memory_avg, type=\"l\",col=\"blue\")\n",[474,59371,59372],{"class":476,"line":4900},[474,59373,59374],{},"lines(r2$date, r2$Max_Memory_avg, type=\"l\",col=\"red\")\n",[474,59376,59377],{"class":476,"line":4913},[474,59378,59379],{},"lines(r2$date, r2$used_avg, type=\"l\",col=\"black\")\n",[474,59381,59382],{"class":476,"line":4921},[474,59383,59384],{},"dev.off()\n",[439,59386,59387],{},"This does nothing more than read the CSV file, some data type conversions, figuring out some mins and maxes, calculating\na moving average so we get a smoother view and plot a few lines showing us the memory behavior.",[439,59389,59390],{},[1002,59391,59394],{"href":59392,"rel":59393},"https://media.synyx.de/uploads//2013/02/ram.png",[1006],[2205,59395],{"alt":59396,"src":59392},"ram",[439,59398,59399,59400,59405],{},"With a endless loop calling R with the above script… we now have replicated what other tools would already have done for\nus, collect information, plot it in real time. Again. As with other\ntools (",[1002,59401,59404],{"href":59402,"rel":59403,"title":59404},"http://graphite.wikidot.com/",[1006],"Graphite"," comes to mind) we can plot arbitrary time ranges, zooming in\nweird behavior, as shown above, merely by generating the CSV file differently. This is all very low-tech. We did not\nwrite any application code, we merely glued existing technology together.",[439,59407,59408],{},"So now we have more information:",[994,59410,59411],{},[997,59412,59413,59414],{},"We actually have a few different kinds of bad memory behavior:\n",[994,59415,59416,59419,59422],{},[997,59417,59418],{},"A case where over the course of 2-3 hours the memory fills up.",[997,59420,59421],{},"A spike where the memory instantly goes to max.",[997,59423,59424],{},"A continuous memory leak.",[439,59426,59427],{},"Having timings on when things actually start to go wrong is – quite plainly – awesome. Having Nagios alert on resource\nusage above a specified water mark can be quite valuable (in the first and third case mentioned). But does not as such\nhelp with the second one (except to tell you that the application is dead-ish).",[439,59429,59430],{},"The happy Ganglia/RHQ user will ask:But.. why again?",[439,59432,59433],{},"The programmer with a mind for patterns, elegance and performance will point to the mindless hundreds of megabytes\nfilling up my hard-drive space while this is running.(Not to speak of the lack of scheduling which leads to gaps in\ndata-collection when one JBoss stops responding in a timely manner.)",[439,59435,59436],{},"But that is exactly what I want. I not only want to know “uh oh.. I think your application is dead” (Nagios), not only\n“OK, this is how your memory looked the past 5 hours” (Ganglia) – I want enough context for later analysis.",[439,59438,59439],{},"In this case, the JBoss status page not only contains pure JVM information but also the HTTP requests, how long they are\nalready running and the (GET) request parameters. So I can also answer the question “how did the JBoss look like before\nit went all wonky” and have more data available to analyze later – such as hanging user-requests.",[439,59441,59442],{},"So what did we do here.",[994,59444,59445,59448,59451,59454,59457,59460,59463],{},[997,59446,59447],{},"Easily (for the creator) adaptable tool-chain for monitoring and data gathering",[997,59449,59450],{},"We used an obscure set of tools (Bash, curl, R, xmllint, filesystem as DB, pdf viewer with something like inotify).",[997,59452,59453],{},"There is “yet another tool”",[997,59455,59456],{},"The tool needs regular maintenance (truncating old data)",[997,59458,59459],{},"The tool is very “specific” – it is too small to even attempt to be “generic”",[997,59461,59462],{},"But, the same mechanisms can be used to monitor something completely different",[997,59464,59465],{},"And: does not only gather numeric data",[439,59467,59468],{},"This of course should not supplant your existing monitoring infrastructure, all insight gained should be properly\nintegrated. This is more a way of thinking, of developing an idea – if you have more, other, opposing ones, please do\nshare!",[1024,59470,59471],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":59473},[59474],{"id":46493,"depth":507,"text":59033},[9045,1030],"2013-02-11T19:10:40","Not being a pure Developer but more of a DevOp brings all sorts of interesting problems. When you not only sell software\\nbut are also included in the whole life-cycle you get a different view on things.","https://synyx.de/blog/monitoring-nih-style/",{},"/blog/monitoring-nih-style",{"title":58957,"description":58966},"blog/monitoring-nih-style",[51447,35140,59484],"r","Not being a pure Developer but more of a DevOp brings all sorts of interesting problems. When you not only sell software but are also included in the whole life-cycle…","YQNd1tX5dx3ikpSrAsquuIS4wGSpUO62fiqsITA-gBw",{"id":59488,"title":59489,"author":59490,"body":59491,"category":59856,"date":59857,"description":59498,"extension":1034,"link":59858,"meta":59859,"navigation":916,"path":59860,"seo":59861,"slug":59495,"stem":59862,"tags":59863,"teaser":59866,"__hash__":59867},"blog/blog/how-sysadmins-monitor-your-java-application-with-jmx.md","How Sysadmins monitor your Java application with JMX",[314],{"type":432,"value":59492,"toc":59847},[59493,59496,59499,59507,59511,59515,59518,59528,59538,59541,59544,59553,59556,59566,59575,59597,59601,59616,59620,59623,59647,59694,59699,59714,59731,59769,59774,59831,59836,59844],[435,59494,59489],{"id":59495},"how-sysadmins-monitor-your-java-application-with-jmx",[439,59497,59498],{},"some time ago Aljona showed",[439,59500,59501],{},[1002,59502,59506],{"href":59503,"rel":59504,"title":59505},"http://blog.synyx.de/2012/05/how-to-monitor-and-manage-your-java-application-with-jmx/",[1006],"JMX-Blog by Aljona ","how to monitor and manage your java application with jmx",[3938,59508,59510],{"id":59509},"im-going-to-show-how-you-can-make-use-of-jmx-from-the-viewpoint-of-a-sysadmin","I’m going to show, how you can make use of JMX from the viewpoint of a sysadmin.",[1065,59512,59514],{"id":59513},"initial-point","initial point:",[439,59516,59517],{},"You have a Java-application deployed in an applicationserver like JBoss or Tomcat and you want to monitor the health of\nthis application(including the applicationserver and the Java-virtual-machine it is running in) with a tool like\nNagios.",[1065,59519,59521,59522,59527],{"id":59520},"use-jolokia-on-the-server-side","Use ",[1002,59523,57762],{"href":59524,"rel":59525,"title":59526},"http://jolokia.org",[1006],"JMX on Capsaicin"," on the server-side!",[439,59529,59530],{},[1002,59531,59534],{"href":59532,"rel":59533},"https://media.synyx.de/uploads//2013/02/jolokia.png",[1006],[2205,59535],{"alt":59536,"src":59537},"jolokia","https://media.synyx.de/uploads//2013/02/jolokia-300x105.png",[439,59539,59540],{},"Why do i have to use an extra Agent for this? My applicationserver is able to use Mbeans.",[439,59542,59543],{},"Of course you don’t have to, but Jolokia is a HTTP/JSON bridge, which means jolokia can be accessed via HTTP and\nresponds with JSON, that is cool, isn’t it?",[439,59545,59546,59547,59552],{},"There are some more advantages like bulk-requests and it\nsupports ",[1002,59548,59551],{"href":57746,"rel":59549,"title":59550},[1006],"cubism on github","cubism.js","\nsince version 1.0.5 (Jonathan uses this to get some nicely visualized charts) … and it’s fast.",[439,59554,59555],{},"Just deploy it into your applicationserver it isn’t that big.",[439,59557,59558,59559,59565],{},"As a sysadmin i don’t want to code Java nor do i want to have a JVM running on my Nagios-server, i’m used to use perl (\njust to satisfy any prejudices), so i stumbled\nover ",[1002,59560,59564],{"href":59561,"rel":59562,"title":59563},"http://labs.consol.de/jmx4perl/",[1006],"Jmx4Perl on ConsolLabs ","jmx4perl"," and decided to make use of this.",[439,59567,59568,59569,59574],{},"I installed ",[1002,59570,59564],{"href":59571,"rel":59572,"title":59573},"http://search.cpan.org/~roland/jmx4perl/",[1006],"Jmx4Perl on CPAN"," and read the documentation.",[464,59576,59578],{"className":54685,"code":59577,"language":54687,"meta":469,"style":469},"\ncpan[1]> install JMX::Jmx4Perl\n\n",[471,59579,59580,59584],{"__ignoreMap":469},[474,59581,59582],{"class":476,"line":477},[474,59583,917],{"emptyLinePlaceholder":916},[474,59585,59586,59589,59591,59594],{"class":476,"line":507},[474,59587,59588],{"class":480},"cpan[1]",[474,59590,9010],{"class":503},[474,59592,59593],{"class":484},"install",[474,59595,59596],{"class":484}," JMX::Jmx4Perl\n",[1065,59598,59600],{"id":59599},"the-tools-brought-along-by-jmx4perl-are-really-very-useful","The tools brought along by jmx4perl are really very useful:",[8310,59602,59603,59610],{},[997,59604,59605,59606,59609],{},"The commandlinetool ",[448,59607,59608],{},"j4psh"," is awesome, you can browse the JMX Mbeans in color by using “cd” and “ls” with “cat”\nyou can get the value of an attribute… tab-completion as a matter of course.",[997,59611,59612,59613,59615],{},"With the commandlinetool ",[448,59614,59564],{}," you can easily test requests and get responses in various forms.",[1065,59617,59619],{"id":59618},"time-to-hack","Time to hack:",[439,59621,59622],{},"First Nagios-test we assemble is for monitoring heapspace of the JVM, not only because it’s often referenced in the\ndocumentation but rather because thats often a useful information to identify memory leaks and to know when it’s time to\nrestart the application(server). But you can follow most of the following steps for many other use cases too.",[8310,59624,59625,59628,59631,59641,59644],{},[997,59626,59627],{},"be sure you got the relevant permissions in jolokia (jolokia-access.xml)",[997,59629,59630],{},"search for older Nagios-Plugins and c’n’p the generic parts…",[997,59632,59633,59634],{},"search for the Mbean with j4psh or use type search of jolokia and look for interesting attributes/operations\n",[1002,59635,59638],{"href":59636,"rel":59637},"https://media.synyx.de/uploads//2013/02/j4psh_screenshot.png",[1006],[2205,59639],{"alt":59640,"src":59636},"j4psh_screenshot",[997,59642,59643],{},"test the request you have in your mind with e.g jmx4perl",[997,59645,59646],{},"canonicalize the request",[464,59648,59652],{"className":59649,"code":59650,"language":59651,"meta":469,"style":469},"language-perl shiki shiki-themes github-light github-dark","my $request = new JMX::Jmx4Perl::Request({\n type => READ,\n mbean => \"java.lang:type=Memory\",\n attribute => \"HeapMemoryUsage\",\n });\n#print(Dumper($request));\nmy $response = $jmx->request($request);\n#print(Dumper($response));\n","perl",[471,59653,59654,59659,59664,59669,59674,59679,59684,59689],{"__ignoreMap":469},[474,59655,59656],{"class":476,"line":477},[474,59657,59658],{},"my $request = new JMX::Jmx4Perl::Request({\n",[474,59660,59661],{"class":476,"line":507},[474,59662,59663],{}," type => READ,\n",[474,59665,59666],{"class":476,"line":547},[474,59667,59668],{}," mbean => \"java.lang:type=Memory\",\n",[474,59670,59671],{"class":476,"line":584},[474,59672,59673],{}," attribute => \"HeapMemoryUsage\",\n",[474,59675,59676],{"class":476,"line":607},[474,59677,59678],{}," });\n",[474,59680,59681],{"class":476,"line":642},[474,59682,59683],{},"#print(Dumper($request));\n",[474,59685,59686],{"class":476,"line":663},[474,59687,59688],{},"my $response = $jmx->request($request);\n",[474,59690,59691],{"class":476,"line":694},[474,59692,59693],{},"#print(Dumper($response));\n",[8310,59695,59696],{"start":694},[997,59697,59698],{},"dereference the response as you need",[464,59700,59702],{"className":59649,"code":59701,"language":59651,"meta":469,"style":469},"my $used_memory=$response->value()->{'used'};\nmy $max_memory=$response->value()->{'max'};\n",[471,59703,59704,59709],{"__ignoreMap":469},[474,59705,59706],{"class":476,"line":477},[474,59707,59708],{},"my $used_memory=$response->value()->{'used'};\n",[474,59710,59711],{"class":476,"line":507},[474,59712,59713],{},"my $max_memory=$response->value()->{'max'};\n",[8310,59715,59716,59719,59722,59725,59728],{"start":913},[997,59717,59718],{},"start a heated debate about sensible values for warning and critical",[997,59720,59721],{},"deploy your Nagios-plugin",[997,59723,59724],{},"copy the script into your plugin-folder of your Nagios-server",[997,59726,59727],{},"install all missing libraries on the Nagios-server",[997,59729,59730],{},"define the command in commands.cfg",[464,59732,59734],{"className":466,"code":59733,"language":468,"meta":469,"style":469},"define command{\n command_name check_jmx4perl_heapspace.pl\n command_line $USER1$/check_jmx4perl_heapspace.pl $HOSTADDRESS$ $ARG1$\n}\n",[471,59735,59736,59743,59751,59765],{"__ignoreMap":469},[474,59737,59738,59740],{"class":476,"line":477},[474,59739,15335],{"class":480},[474,59741,59742],{"class":484}," command{\n",[474,59744,59745,59748],{"class":476,"line":507},[474,59746,59747],{"class":480}," command_name",[474,59749,59750],{"class":484}," check_jmx4perl_heapspace.pl\n",[474,59752,59753,59756,59759,59762],{"class":476,"line":547},[474,59754,59755],{"class":480}," command_line",[474,59757,59758],{"class":503}," $USER1$",[474,59760,59761],{"class":484},"/check_jmx4perl_heapspace.pl",[474,59763,59764],{"class":503}," $HOSTADDRESS$ $ARG1$\n",[474,59766,59767],{"class":476,"line":584},[474,59768,703],{"class":503},[8310,59770,59771],{"start":607},[997,59772,59773],{},"make use of the new command in your services.cfg (or whatever you’ve called your file)",[464,59775,59777],{"className":466,"code":59776,"language":468,"meta":469,"style":469},"\ndefine service{\n use remote-service\n host_name jolokia-host\n service_description HeapSpace of JavaApp\n check_command check_jmx4perl_heapspace.pl!8084\n}\n\n",[471,59778,59779,59783,59790,59798,59806,59819,59827],{"__ignoreMap":469},[474,59780,59781],{"class":476,"line":477},[474,59782,917],{"emptyLinePlaceholder":916},[474,59784,59785,59787],{"class":476,"line":507},[474,59786,15335],{"class":480},[474,59788,59789],{"class":484}," service{\n",[474,59791,59792,59795],{"class":476,"line":547},[474,59793,59794],{"class":480}," use",[474,59796,59797],{"class":484}," remote-service\n",[474,59799,59800,59803],{"class":476,"line":584},[474,59801,59802],{"class":480}," host_name",[474,59804,59805],{"class":484}," jolokia-host\n",[474,59807,59808,59811,59814,59816],{"class":476,"line":607},[474,59809,59810],{"class":480}," service_description",[474,59812,59813],{"class":484}," HeapSpace",[474,59815,530],{"class":484},[474,59817,59818],{"class":484}," JavaApp\n",[474,59820,59821,59824],{"class":476,"line":642},[474,59822,59823],{"class":480}," check_command",[474,59825,59826],{"class":484}," check_jmx4perl_heapspace.pl!8084\n",[474,59828,59829],{"class":476,"line":663},[474,59830,703],{"class":503},[8310,59832,59833],{"start":938},[997,59834,59835],{},"lean back while watching the heapspace grow…",[439,59837,59838],{},[1002,59839,59843],{"href":59840,"rel":59841,"title":59842},"https://github.com/zivis/Scripts/blob/master/check_jmx4perl_heapspace.pl",[1006],"check_jmx4perl_heapspace.pl on github","get the complete Nagios-plugin from github",[1024,59845,59846],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":59848},[59849],{"id":59509,"depth":507,"text":59510,"children":59850},[59851,59852,59854,59855],{"id":59513,"depth":547,"text":59514},{"id":59520,"depth":547,"text":59853},"Use Jolokia on the server-side!",{"id":59599,"depth":547,"text":59600},{"id":59618,"depth":547,"text":59619},[9045],"2013-02-07T00:48:50","https://synyx.de/blog/how-sysadmins-monitor-your-java-application-with-jmx/",{},"/blog/how-sysadmins-monitor-your-java-application-with-jmx",{"title":59489,"description":59498},"blog/how-sysadmins-monitor-your-java-application-with-jmx",[15546,711,59864,59865,35140,35141,26636,59651,51154],"jboss","jmx","some time ago Aljona showed how to monitor and manage your java application with jmx I’m going to show, how you can make use of JMX from the viewpoint of…","uFeGEnJ9LXOx3WyMd1_4D3DRNsWW36ydcSvIrVYoiBA",{"id":59869,"title":59870,"author":59871,"body":59872,"category":60303,"date":60304,"description":60305,"extension":1034,"link":60306,"meta":60307,"navigation":916,"path":60308,"seo":60309,"slug":60311,"stem":60312,"tags":60313,"teaser":60316,"__hash__":60317},"blog/blog/setup-selenium-grid.md","Acceptance testing at synyx – Part 3",[166],{"type":432,"value":59873,"toc":60293},[59874,59877,59891,59895,59909,59912,59916,59919,59923,59926,60004,60007,60013,60020,60066,60072,60081,60085,60091,60098,60109,60138,60141,60207,60211,60214,60230,60239,60243,60249,60257,60261,60264,60270,60273,60280,60287,60290],[435,59875,59870],{"id":59876},"acceptance-testing-at-synyx-part-3",[439,59878,59879,59880,59884,59885,59890],{},"After showing you ",[1002,59881,59883],{"href":57521,"rel":59882},[1006],"how to request a remote browser"," from a Selenium Grid\nin the last part its time to put some effort in getting the grid running smoothly. Also, check out\nthe ",[1002,59886,59889],{"href":59887,"rel":59888},"http://blog.synyx.de/2013/01/atdd-at-synyx/",[1006],"first part of the series"," for the greater context of this blog post.",[1065,59892,59894],{"id":59893},"setting-up-the-grid","Setting up the Grid",[439,59896,59897,59898,59902,59903,59908],{},"As mentioned in ",[1002,59899,59901],{"href":57521,"rel":59900},[1006],"part 2",", its pretty straight forward to set up a\nselenium-grid by following the instructions at the ",[1002,59904,59907],{"href":59905,"rel":59906},"http://code.google.com/p/selenium/wiki/Grid2",[1006],"Selenium Wiki",". This\nworked out pretty well for us when setting up a simple hub plus node configuration on the dev-machines for evaluation\npurposes.",[439,59910,59911],{},"But as soon as you use selenium grid in production you probably want to wrap some professional services around this to\nmake your local system administrator happy. Currently we are running the explained setup on a hub-node (server) as well\nas two virtual machines running the browsers (one ubuntu-based one windows7). But the system is designed to add more\nnodes as soon as they’re needed. And they will be needed soon, especially because the beloved Internet Explorer comes in\nso many flavours and tests do not run very fast on them compared to Chrome or even Firefox ;).",[1065,59913,59915],{"id":59914},"the-linux-machines","The Linux machines",[439,59917,59918],{},"The (virtual) machine running the selenium hub and the one running browsers on ubuntu are linux based machines of\ncourse. If you run services on linux-based machines you usually want to have them run as a daemon in background,\nstarted automatically upon system boot. So of course this is what I did here. I describe the way this is done on\ndebian / ubuntu systems but this will probably work on other distributions with minimal adjustments.",[1065,59920,59922],{"id":59921},"starting-the-hub","Starting the Hub",[439,59924,59925],{},"Selenium provides a “runnable” jar file to start the server by something like",[464,59927,59929],{"className":54685,"code":59928,"language":54687,"meta":469,"style":469},"$ java -jar selenium-server.jar -role hub\nJan 16, 2013 2:14:39 PM org.openqa.grid.selenium.GridLauncher main\nInformation: Launching a selenium grid server\n2013-01-16 14:14:40.662:INFO:osjs.Server:jetty-7.x.y-SNAPSHOT\n...\n",[471,59930,59931,59951,59974,59992,60000],{"__ignoreMap":469},[474,59932,59933,59936,59939,59942,59945,59948],{"class":476,"line":477},[474,59934,59935],{"class":480},"$",[474,59937,59938],{"class":484}," java",[474,59940,59941],{"class":510}," -jar",[474,59943,59944],{"class":484}," selenium-server.jar",[474,59946,59947],{"class":510}," -role",[474,59949,59950],{"class":484}," hub\n",[474,59952,59953,59956,59959,59962,59965,59968,59971],{"class":476,"line":507},[474,59954,59955],{"class":480},"Jan",[474,59957,59958],{"class":484}," 16,",[474,59960,59961],{"class":510}," 2013",[474,59963,59964],{"class":484}," 2:14:39",[474,59966,59967],{"class":484}," PM",[474,59969,59970],{"class":484}," org.openqa.grid.selenium.GridLauncher",[474,59972,59973],{"class":484}," main\n",[474,59975,59976,59979,59982,59984,59987,59990],{"class":476,"line":547},[474,59977,59978],{"class":480},"Information:",[474,59980,59981],{"class":484}," Launching",[474,59983,20159],{"class":484},[474,59985,59986],{"class":484}," selenium",[474,59988,59989],{"class":484}," grid",[474,59991,10255],{"class":484},[474,59993,59994,59997],{"class":476,"line":584},[474,59995,59996],{"class":480},"2013-01-16",[474,59998,59999],{"class":484}," 14:14:40.662:INFO:osjs.Server:jetty-7.x.y-SNAPSHOT\n",[474,60001,60002],{"class":476,"line":607},[474,60003,14198],{"class":510},[439,60005,60006],{},"The server binds itself on the configured port and keeps running until you hit CTRL-C while printing out log statements\nto STDOUT.",[439,60008,60009,60010,1402],{},"So the first thing I did is wrapp this within a upstart-script that made some stuff configurable and readable (where to\nlog to, which java to use, where is the selenium-jar, the config and so on). I named this script ",[471,60011,60012],{},"selenium-hub",[439,60014,60015,60016,60019],{},"For the sake of simplicity I put all my files to ",[471,60017,60018],{},"/opt/selenium/",". This might not be the best “unix-way” but doing so\nhelps me find all the files easily.",[464,60021,60023],{"className":54685,"code":60022,"language":54687,"meta":469,"style":469},"$ ls /opt/selenium\nhubconfig.json\nupstart-selenium-hub\nselenium-hub\nselenium-server.jar -> selenium-server-standalone-2.26.0.jar\nselenium-server-standalone-2.26.0.jar\n",[471,60024,60025,60034,60039,60044,60049,60061],{"__ignoreMap":469},[474,60026,60027,60029,60031],{"class":476,"line":477},[474,60028,59935],{"class":480},[474,60030,56986],{"class":484},[474,60032,60033],{"class":484}," /opt/selenium\n",[474,60035,60036],{"class":476,"line":507},[474,60037,60038],{"class":480},"hubconfig.json\n",[474,60040,60041],{"class":476,"line":547},[474,60042,60043],{"class":480},"upstart-selenium-hub\n",[474,60045,60046],{"class":476,"line":584},[474,60047,60048],{"class":480},"selenium-hub\n",[474,60050,60051,60054,60056,60058],{"class":476,"line":607},[474,60052,60053],{"class":480},"selenium-server.jar",[474,60055,9007],{"class":503},[474,60057,1500],{"class":810},[474,60059,60060],{"class":484}," selenium-server-standalone-2.26.0.jar\n",[474,60062,60063],{"class":476,"line":642},[474,60064,60065],{"class":480},"selenium-server-standalone-2.26.0.jar\n",[439,60067,60068,60069,11288],{},"As you can see I also added some symlinks so that I dont need to adjust my scripts as soon as I upgrade selenium-server\njar to a newer version (",[471,60070,60071],{},"ln -s selenium-server-standalone-2.26.0.jar selenium-server.jar",[439,60073,60074,60075,60080],{},"If you want to have a detailed look at the files you can check out\nthe ",[1002,60076,60079],{"href":60077,"rel":60078},"https://media.synyx.de/uploads//2013/01/synyx-selenium-grid-scripts.tar.gz",[1006],"archive containing all scripts"," I\nwrote.",[1065,60082,60084],{"id":60083},"managing-the-hub-as-a-service","Managing the Hub as a service",[439,60086,60087,60088,1402],{},"So the next thing to be done is starting and stopping the services using init-scripts. This is usually done by adding\nthe scripts to ",[471,60089,60090],{},"/etc/init.d/$servicename",[439,60092,60093,60094,60097],{},"My upstart-scripts are basically copied from the skeleton-file ubuntu brings (",[471,60095,60096],{},"/etc/init.d/skeleton",") adjusted for my\nneeds.",[439,60099,60100,60101,60104,60105,60108],{},"This script takes care of managing the pid-file for the process and starting and stopping it correctly. So I put the\nupstart-script to ",[471,60102,60103],{},"/opt/selenium"," and symlinked them to ",[471,60106,60107],{},"/etc/init.d/",". Then the script can be added to the systems\nrunlevels (so that it will be started automatically upon boot).",[464,60110,60112],{"className":54685,"code":60111,"language":54687,"meta":469,"style":469},"ln -s /opt/selenium/upstart-selenium-hub /etc/init.d/selenium-hub\nupdate-rc.d selenium-hub defaults\n",[471,60113,60114,60127],{"__ignoreMap":469},[474,60115,60116,60119,60121,60124],{"class":476,"line":477},[474,60117,60118],{"class":480},"ln",[474,60120,14052],{"class":510},[474,60122,60123],{"class":484}," /opt/selenium/upstart-selenium-hub",[474,60125,60126],{"class":484}," /etc/init.d/selenium-hub\n",[474,60128,60129,60132,60135],{"class":476,"line":507},[474,60130,60131],{"class":480},"update-rc.d",[474,60133,60134],{"class":484}," selenium-hub",[474,60136,60137],{"class":484}," defaults\n",[439,60139,60140],{},"So the hub can be now managed using the “service” command.",[464,60142,60144],{"className":54685,"code":60143,"language":54687,"meta":469,"style":469},"$ sudo service selenium-hub status\n * selenium-hub is not running\n$ sudo service selenium-hub start\n$ sudo service selenium-hub status\n * selenium-hub is running\n",[471,60145,60146,60159,60172,60185,60197],{"__ignoreMap":469},[474,60147,60148,60150,60152,60155,60157],{"class":476,"line":477},[474,60149,59935],{"class":480},[474,60151,12665],{"class":484},[474,60153,60154],{"class":484}," service",[474,60156,60134],{"class":484},[474,60158,19592],{"class":484},[474,60160,60161,60163,60165,60167,60169],{"class":476,"line":507},[474,60162,1474],{"class":480},[474,60164,60134],{"class":484},[474,60166,15134],{"class":484},[474,60168,20146],{"class":484},[474,60170,60171],{"class":484}," running\n",[474,60173,60174,60176,60178,60180,60182],{"class":476,"line":547},[474,60175,59935],{"class":480},[474,60177,12665],{"class":484},[474,60179,60154],{"class":484},[474,60181,60134],{"class":484},[474,60183,60184],{"class":484}," start\n",[474,60186,60187,60189,60191,60193,60195],{"class":476,"line":584},[474,60188,59935],{"class":480},[474,60190,12665],{"class":484},[474,60192,60154],{"class":484},[474,60194,60134],{"class":484},[474,60196,19592],{"class":484},[474,60198,60199,60201,60203,60205],{"class":476,"line":607},[474,60200,1474],{"class":480},[474,60202,60134],{"class":484},[474,60204,15134],{"class":484},[474,60206,60171],{"class":484},[1065,60208,60210],{"id":60209},"doing-the-same-for-nodes","Doing the same for Nodes",[439,60212,60213],{},"The procedure for nodes is pretty much the same as for the hub but it has some smaller differences:",[439,60215,60216,60217,60220,60221,60226,60227,60229],{},"Our system administration set up the ubuntu machine so that it automatically starts X with unity while automatically\nlogging in the user “synyx” to the desktop. Since this should run the browsers the selenium-server process has to be\nable to access the display. This is usually done by exporting the correct information to the ",[471,60218,60219],{},"DISPLAY","\nenvironment-variable. Also selenium-server needs a system-property where to find the chromedriver\nbinary (",[1002,60222,60225],{"href":60223,"rel":60224},"http://code.google.com/p/selenium/wiki/ChromeDriver",[1006],"see documentation","). So I adjusted my start-script and my\nupstart-script (since it has to access the display its best to run the server as the same user the X-Session belongs\nto) and added everything new to ",[471,60228,60103],{}," and updated my runlevels.",[439,60231,60232,60233,60238],{},"Also we use ",[1002,60234,60237],{"href":60235,"rel":60236},"http://en.wikipedia.org/wiki/Vino_%28VNC_server%29",[1006],"Vino-server"," to be able to connect via VNC to the\nDesktop (and view the browsers working) in case you want to reproduce bugs and so on.",[1065,60240,60242],{"id":60241},"configuring-the-grid","Configuring the grid",[439,60244,60245,60246,60248],{},"Selenium-Server can be configured using command-line arguments or by JSON. I preferred the JSON way and added\nconfiguration-files to ",[471,60247,60018],{}," and added the -nodeConfig /-hubConfig parameter in my start-scripts. Here\nyou configure timeouts, urls and what kind of browsers the instance provides to the grid.",[439,60250,60251,60252,1402],{},"The available parameters as well as defaults can be best looked up at\nthe ",[1002,60253,60256],{"href":60254,"rel":60255},"http://code.google.com/p/selenium/source/browse/#git%2Fjava%2Fserver%2Fsrc%2Forg%2Fopenqa%2Fgrid%2Fcommon%2Fdefaults",[1006],"selenium svn",[1065,60258,60260],{"id":60259},"getting-some-browsers","Getting some browsers",[439,60262,60263],{},"As soon as the hub is running try to access it with a browser and navigate to its console. Here you can see how many\nhosts there are, what kind of browsers they supply and so on. Here is an example of a grid with one node providing\nFirefox and Chrome instances, one providing Internet Explorers.",[439,60265,60266],{},[2205,60267],{"alt":60268,"src":60269},"Console of a Selenium Hub","https://media.synyx.de/uploads//2013/01/grid-console.png",[1065,60271,60272],{"id":58066},"Context",[439,60274,60275,60276,1402],{},"You request these Browsers using the RemoteWebDriver\nas ",[1002,60277,60279],{"href":57521,"rel":60278},[1006],"described in the last post",[439,60281,60282,60283,1402],{},"In case you want do to something like this you can find all files I mentioned here to\ndownload: ",[1002,60284,60286],{"href":60077,"rel":60285},[1006],"synyx-selenium-grid-scripts.tar.gz",[439,60288,60289],{},"In the upcoming post I will describe how to add a Windows based node to the grid using a similar approach. Ah, and after\nthat one we’re done with the technical stuff and proceed to questions like how do we test, how do we report results and\nso on…",[1024,60291,60292],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":469,"searchDepth":507,"depth":507,"links":60294},[60295,60296,60297,60298,60299,60300,60301,60302],{"id":59893,"depth":547,"text":59894},{"id":59914,"depth":547,"text":59915},{"id":59921,"depth":547,"text":59922},{"id":60083,"depth":547,"text":60084},{"id":60209,"depth":547,"text":60210},{"id":60241,"depth":547,"text":60242},{"id":60259,"depth":547,"text":60260},{"id":58066,"depth":547,"text":60272},[1030],"2013-02-04T16:32:28","After showing you how to request a remote browser from a Selenium Grid\\nin the last part its time to put some effort in getting the grid running smoothly. Also, check out\\nthe first part of the series for the greater context of this blog post.","https://synyx.de/blog/setup-selenium-grid/",{},"/blog/setup-selenium-grid",{"title":59870,"description":60310},"After showing you how to request a remote browser from a Selenium Grid\nin the last part its time to put some effort in getting the grid running smoothly. Also, check out\nthe first part of the series for the greater context of this blog post.","setup-selenium-grid","blog/setup-selenium-grid",[60314,57705,20503,60315,20699,18497],"cloud","remotewebdriver","After showing you how to request a remote browser from a Selenium Grid in the last part its time to put some effort in getting the grid running smoothly. Also,…","kLH6OE-BvQ8fU_Pr6ifbublwFLiOlzW_UcpYQvMYOKg",{"id":60319,"title":60320,"author":60321,"body":60322,"category":60686,"date":60687,"description":60688,"extension":1034,"link":60689,"meta":60690,"navigation":916,"path":60691,"seo":60692,"slug":60694,"stem":60695,"tags":60696,"teaser":60698,"__hash__":60699},"blog/blog/remote-browsers.md","Acceptance testing at synyx – Part 2",[166],{"type":432,"value":60323,"toc":60678},[60324,60327,60333,60337,60346,60349,60352,60355,60364,60367,60370,60376,60380,60386,60392,60431,60435,60442,60485,60494,60633,60645,60648,60662,60669,60673,60676],[435,60325,60320],{"id":60326},"acceptance-testing-at-synyx-part-2",[439,60328,37673,60329,60332],{},[1002,60330,59889],{"href":59887,"rel":60331},[1006]," I gave some reasons why to do acceptance\ntesting (or webtests) as well as a rough overview how we do it at synyx. This part is rather technical and describes how\nto use Seleniums RemoteWebDriver to control browsers on a remote host.",[3938,60334,60336],{"id":60335},"running-browsers-elsewhere-selenium-grid","Running browsers elsewhere – Selenium GRID",[439,60338,60339,60340,60345],{},"One important thing for me is that the browsers that are used to execute the webtests should not run on the same host as\nthe tests. This way we can minimize setup for the tests on the developers machine. Also, the machine running our\nCI-System ",[1002,60341,60344],{"href":60342,"rel":60343},"http://jenkins-ci.org/",[1006],"Jenkins"," is a headless server where no browsers can be installed. Therefore we also\nneed the remoting capability for Jenkins.",[439,60347,60348],{},"And another important argument for running browsers on a remote host is that probably not all browsers you want to run\nyour tests on can be installed on a developers machine. For example my unix machine refuses to run Internet\nExplorer ;-).",[439,60350,60351],{},"So in my opinion the best choice to execute tests is using Seleniums RemoteWebdriver.",[1065,60353,60354],{"id":60315},"RemoteWebDriver",[439,60356,60357,60358,60363],{},"If you are familiar with Selenium you know that in order to run your test in a browser you usually instanciate the\ncorrect WebDriver for it (e.g. FirefoxWebDriver to run a Firefox). But there is also RemoteWebDriver that can be used to\nsteer browsers on some other host. So this RemoteWebDriver acts like a proxy, talks to a remote-service called\nselenium-server using HTTP. Then selenium-server actually controls the browser.\nSee ",[1002,60359,60362],{"href":60360,"rel":60361},"http://code.google.com/p/selenium/wiki/RemoteWebDriverServer",[1006],"Seleniums documentation"," for further details.",[439,60365,60366],{},"As mentioned you have to have a selenium-server instance to use RemoteWebDriver. Selenium-server can run in\nstandalone-mode or – if you want to scale up – as grid with a hub and many nodes. In this case you have one hub which\nonly matches requested capabilities and delegates clients to existing hosts on selenium nodes.",[439,60368,60369],{},"The following graphic shows you how a setup using Selenium Grid (hub and nodes) could look like:",[439,60371,60372],{},[2205,60373],{"alt":60374,"src":60375},"How selenium grid looks like","https://media.synyx.de/uploads//2013/01/selenium-grid.png",[1065,60377,60379],{"id":60378},"requesting-a-browser-from-the-grid","Requesting a browser from the grid",[439,60381,60382,60383,1402],{},"Once here is a server running you are able to request a browser from it by instanciating RemoteWebDriver with the URL of\nit. In addition you have to describe the browser you want using the class ",[471,60384,60385],{},"DesiredCapabilities",[439,60387,60388,60389],{},"The following code snippet shows how to request a chrome version 20 on any operating system from a selenium-server\nlistening to ",[471,60390,60391],{},"http://localhost:4444",[464,60393,60395],{"className":709,"code":60394,"language":711,"meta":469,"style":469},"DesiredCapabilities cap = new DesiredCapabilities(\"chrome\", \"20\", Platform.ANY);\n// url of selenium-hub/server\nURL url = new URL(\"http://localhost:4444\");\nWebDriver driver = new RemoteWebDriver(url, cap);\ndriver.open(targetSystemUrl);\ndriver.findElement(By.id(\"number\")).sendKeys(\"42\");\n...\n",[471,60396,60397,60402,60407,60412,60417,60422,60427],{"__ignoreMap":469},[474,60398,60399],{"class":476,"line":477},[474,60400,60401],{},"DesiredCapabilities cap = new DesiredCapabilities(\"chrome\", \"20\", Platform.ANY);\n",[474,60403,60404],{"class":476,"line":507},[474,60405,60406],{},"// url of selenium-hub/server\n",[474,60408,60409],{"class":476,"line":547},[474,60410,60411],{},"URL url = new URL(\"http://localhost:4444\");\n",[474,60413,60414],{"class":476,"line":584},[474,60415,60416],{},"WebDriver driver = new RemoteWebDriver(url, cap);\n",[474,60418,60419],{"class":476,"line":607},[474,60420,60421],{},"driver.open(targetSystemUrl);\n",[474,60423,60424],{"class":476,"line":642},[474,60425,60426],{},"driver.findElement(By.id(\"number\")).sendKeys(\"42\");\n",[474,60428,60429],{"class":476,"line":663},[474,60430,14198],{},[1065,60432,60434],{"id":60433},"execute-tests-with-different-browsers","Execute tests with different browsers",[439,60436,60437,60438,60441],{},"In order to be able to execute the same test in many browsers we decided to read browser-capabilities from a\n.properties file instead of defining them hardcoded within the test classes. The name of the file can be handed to the\ntest as system property to be able to control which browser to be used by adding\n",[471,60439,60440],{},"-DbrowserProperties=firefox.properties"," to the build commandline.",[464,60443,60445],{"className":709,"code":60444,"language":711,"meta":469,"style":469},"\nString fileName = System.getProperty(\"browserProperties\");\nProperties p = loadProperties(fileName);\nDesiredCapabilities c = new DesiredCapabilities(\n p.getProperty(\"browser.name\"),\n p.getProperty(\"browser.version\"),\n p.getProperty(\"browser.platform\")\n);\n\n",[471,60446,60447,60451,60456,60461,60466,60471,60476,60481],{"__ignoreMap":469},[474,60448,60449],{"class":476,"line":477},[474,60450,917],{"emptyLinePlaceholder":916},[474,60452,60453],{"class":476,"line":507},[474,60454,60455],{},"String fileName = System.getProperty(\"browserProperties\");\n",[474,60457,60458],{"class":476,"line":547},[474,60459,60460],{},"Properties p = loadProperties(fileName);\n",[474,60462,60463],{"class":476,"line":584},[474,60464,60465],{},"DesiredCapabilities c = new DesiredCapabilities(\n",[474,60467,60468],{"class":476,"line":607},[474,60469,60470],{}," p.getProperty(\"browser.name\"),\n",[474,60472,60473],{"class":476,"line":642},[474,60474,60475],{}," p.getProperty(\"browser.version\"),\n",[474,60477,60478],{"class":476,"line":663},[474,60479,60480],{}," p.getProperty(\"browser.platform\")\n",[474,60482,60483],{"class":476,"line":694},[474,60484,1495],{},[439,60486,60487,60488,60493],{},"I use this approach to bind the ",[1002,60489,60492],{"href":60490,"rel":60491},"http://maven.apache.org/surefire/maven-surefire-plugin/",[1006],"maven-surefire-plugin","\nmultiple times to the test lifecycle phase: once for each browser.",[464,60495,60497],{"className":6253,"code":60496,"language":6255,"meta":469,"style":469},"\u003Cplugin>\n \u003Cgroupid>org.apache.maven.plugins\u003C/groupid>\n \u003Cartifactid>maven-surefire-plugin\u003C/artifactid>\n \u003Cversion>2.12\u003C/version>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>default-test\u003C/id>\n \u003Cphase>test\u003C/phase>\n \u003Cgoals>\u003Cgoal>test\u003C/goal>\u003C/goals>\n \u003Cconfiguration>\n \u003Csystempropertyvariables>\n \u003Cbrowserproperties>chrome.properties\u003C/browserproperties>\n \u003C/systempropertyvariables>\n \u003Creportnamesuffix>chrome\u003C/reportnamesuffix>\n \u003C/configuration>\n \u003C/execution>\n \u003Cexecution>\n \u003Cid>test-firefox\u003C/id>\n \u003Cphase>test\u003C/phase>\n \u003Cgoals>\u003Cgoal>test\u003C/goal>\u003C/goals>\n \u003Cconfiguration>\n \u003Csystempropertyvariables>\n \u003Cbrowserproperties>firefox.properties\u003C/browserproperties>\n \u003C/systempropertyvariables>\n \u003Creportnamesuffix>firefox\u003C/reportnamesuffix>\n \u003C/configuration>\n \u003C/execution>\n \u003C!-- ... more browsers here ... -->\n \u003C/executions>\n\u003C/plugin>\n\n",[471,60498,60499,60503,60508,60513,60518,60522,60526,60531,60536,60541,60545,60550,60555,60560,60565,60569,60573,60577,60582,60586,60590,60594,60598,60603,60607,60612,60616,60620,60625,60629],{"__ignoreMap":469},[474,60500,60501],{"class":476,"line":477},[474,60502,44754],{},[474,60504,60505],{"class":476,"line":507},[474,60506,60507],{}," \u003Cgroupid>org.apache.maven.plugins\u003C/groupid>\n",[474,60509,60510],{"class":476,"line":547},[474,60511,60512],{}," \u003Cartifactid>maven-surefire-plugin\u003C/artifactid>\n",[474,60514,60515],{"class":476,"line":584},[474,60516,60517],{}," \u003Cversion>2.12\u003C/version>\n",[474,60519,60520],{"class":476,"line":607},[474,60521,44794],{},[474,60523,60524],{"class":476,"line":642},[474,60525,44799],{},[474,60527,60528],{"class":476,"line":663},[474,60529,60530],{}," \u003Cid>default-test\u003C/id>\n",[474,60532,60533],{"class":476,"line":694},[474,60534,60535],{}," \u003Cphase>test\u003C/phase>\n",[474,60537,60538],{"class":476,"line":700},[474,60539,60540],{}," \u003Cgoals>\u003Cgoal>test\u003C/goal>\u003C/goals>\n",[474,60542,60543],{"class":476,"line":913},[474,60544,54045],{},[474,60546,60547],{"class":476,"line":920},[474,60548,60549],{}," \u003Csystempropertyvariables>\n",[474,60551,60552],{"class":476,"line":926},[474,60553,60554],{}," \u003Cbrowserproperties>chrome.properties\u003C/browserproperties>\n",[474,60556,60557],{"class":476,"line":932},[474,60558,60559],{}," \u003C/systempropertyvariables>\n",[474,60561,60562],{"class":476,"line":938},[474,60563,60564],{}," \u003Creportnamesuffix>chrome\u003C/reportnamesuffix>\n",[474,60566,60567],{"class":476,"line":944},[474,60568,54070],{},[474,60570,60571],{"class":476,"line":950},[474,60572,44824],{},[474,60574,60575],{"class":476,"line":956},[474,60576,44799],{},[474,60578,60579],{"class":476,"line":962},[474,60580,60581],{}," \u003Cid>test-firefox\u003C/id>\n",[474,60583,60584],{"class":476,"line":4876},[474,60585,60535],{},[474,60587,60588],{"class":476,"line":4888},[474,60589,60540],{},[474,60591,60592],{"class":476,"line":4900},[474,60593,54045],{},[474,60595,60596],{"class":476,"line":4913},[474,60597,60549],{},[474,60599,60600],{"class":476,"line":4921},[474,60601,60602],{}," \u003Cbrowserproperties>firefox.properties\u003C/browserproperties>\n",[474,60604,60605],{"class":476,"line":4932},[474,60606,60559],{},[474,60608,60609],{"class":476,"line":4938},[474,60610,60611],{}," \u003Creportnamesuffix>firefox\u003C/reportnamesuffix>\n",[474,60613,60614],{"class":476,"line":4946},[474,60615,54070],{},[474,60617,60618],{"class":476,"line":4952},[474,60619,44824],{},[474,60621,60622],{"class":476,"line":4957},[474,60623,60624],{}," \u003C!-- ... more browsers here ... -->\n",[474,60626,60627],{"class":476,"line":4969},[474,60628,44829],{},[474,60630,60631],{"class":476,"line":4990},[474,60632,44834],{},[439,60634,60635,60636,60641,60642,57553],{},"An alternative to this could be to wrap the configuration\nwithin ",[1002,60637,60640],{"href":60638,"rel":60639},"http://maven.apache.org/guides/introduction/introduction-to-profiles.html",[1006],"Maven profiles"," so that you can\ncontrol which browsers should be used by ",[471,60643,60644],{},"mvn test -Pfirefox",[439,60646,60647],{},"Since the test-code that reads the properties has defaults that make sense, it’s still also possible to execute the\ntest using the IDE of your choice. For example simply default to requesting a chrome on any platform.",[439,60649,60650,60651,60654,60655,60658,60659,60661],{},"It worked out pretty well to have the infrastructural code within an abstract baseclass for all tests. This class uses\n",[471,60652,60653],{},"@Befor","e or ",[471,60656,60657],{},"@BeforeClass"," to read needed capabilities from a properties-file, set up ",[471,60660,60354],{}," and point it\nto the system under test. Of course something like a JUnit Rule would also work.",[439,60663,60664,60665,60668],{},"Either way the actual tests don’t have to care about how the WebDriver they use is instanciated: They just use the\nbaseclasses ",[471,60666,60667],{},"getDriver()"," and execute the real testing-code.",[1065,60670,60672],{"id":60671},"next-part","Next Part",[439,60674,60675],{},"In the next part I will show you how the Selenium Grid can be set up in good way so that you can make your local system\nadministration happy \\o/. So again… stay tuned.",[1024,60677,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":60679},[60680],{"id":60335,"depth":507,"text":60336,"children":60681},[60682,60683,60684,60685],{"id":60315,"depth":547,"text":60354},{"id":60378,"depth":547,"text":60379},{"id":60433,"depth":547,"text":60434},{"id":60671,"depth":547,"text":60672},[1030],"2013-01-29T08:44:29","In the first part of the series I gave some reasons why to do acceptance\\ntesting (or webtests) as well as a rough overview how we do it at synyx. This part is rather technical and describes how\\nto use Seleniums RemoteWebDriver to control browsers on a remote host.","https://synyx.de/blog/remote-browsers/",{},"/blog/remote-browsers",{"title":60320,"description":60693},"In the first part of the series I gave some reasons why to do acceptance\ntesting (or webtests) as well as a rough overview how we do it at synyx. This part is rather technical and describes how\nto use Seleniums RemoteWebDriver to control browsers on a remote host.","remote-browsers","blog/remote-browsers",[60314,57705,60315,20699,18497,60697],"webdriver","In the first part of the series I gave some reasons why to do acceptance testing (or webtests) as well as a rough overview how we do it at synyx.…","6B-fMcsfreVYZKAOIAxT3i_if0BMzXirTLyLNPrmkUM",{"id":60701,"title":60702,"author":60703,"body":60704,"category":60810,"date":60811,"description":469,"extension":1034,"link":60812,"meta":60813,"navigation":916,"path":60814,"seo":60815,"slug":60816,"stem":60817,"tags":60818,"teaser":60821,"__hash__":60822},"blog/blog/atdd-at-synyx.md","Acceptance testing at synyx – Part 1",[166],{"type":432,"value":60705,"toc":60804},[60706,60709,60713,60722,60725,60729,60739,60742,60745,60754,60757,60761,60764,60801],[435,60707,60702],{"id":60708},"acceptance-testing-at-synyx-part-1",[3938,60710,60712],{"id":60711},"overview-why-and-how-we-do-web-testing","Overview – Why and how we do web-testing",[439,60714,60715,60716,60721],{},"In my team at synyx we wrote a lot of tests in 2012. Most of the tests were unit-tests (as a consequence of TDD), some\nstuff is also tested as integration-tests (sometimes because the stuff was hard to test as unit-tests, sometimes as\naddition to them to verify that interactions of components work properly). I can\ntell ",[1002,60717,60720],{"href":60718,"rel":60719},"http://de.wikipedia.org/wiki/TDD",[1006],"TDD"," and the special focus on tests changed the way we work pretty much and of\ncourse boosted the quality of our applications even further. Its not that we did not write tests before, but once you\ndevelop test driven you can start to trust your code which makes refactorings (evolution) easy.",[439,60723,60724],{},"But there are always components that are hard to test. This includes code related to user interfaces, complete\nwork-flows and -sigh- Internet Explorer. So, at the end of 2012 we decided to give automated browser-tests another\nchange (we did evaluate and try this yeeeears ago but – for several reasons – we did not make good expieriences with\nit).",[1065,60726,60728],{"id":60727},"arguments-to-do-it","Arguments to do it",[439,60730,60731,60732,60738],{},"Testing backend-components has become easy as soon as you are practiced writing tests and follow some design principles\nlike dependency injection. But usually, easy testing stops as soon as you enter the web-layer. Yes, I know its possible\nto ",[1002,60733,60737],{"href":60734,"rel":60735,"title":60736},"http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/",[1006],"Spring MVC Test Framework","write tests for Spring MVC","\ncontrollers but going down this road always felt a bit weird. And even if you have these tests, you want to test the\nwhole thing (Controller, JSPs, Filters, Interceptors and what not) in an integrative way. So the best solution is\nrunning automated tests of the deployed application using a real browser.",[439,60740,60741],{},"In fact, since the browsers that display our applications differ in some details we even have to test the apps in many\nof them. Or, at least with those we want to ensure compability with our applications. For example, some of the bugs that\nwere reported for our last application only affect one of the browsers out there (mostly a particular version of\nInternet Explorer). These bugs were not detected early because developers/qa tend not to test everything in every\nbrowser – especially if they have to log on to one or more remote windows machines in order to do so. Lately, the amount\nof JavaScript that is used within our software increases, hence, this gets even more important.",[439,60743,60744],{},"The last and one of the most important arguments for webtests is that they are acceptance tests and live in another\nscope. In contrast, unit and integration tests are more like whitebox-tests: I tend to say that the latter are for us\ndevelopers. They give us confidence and the freedom to safely extend and change our application. These tests are testing\nfrom the inside and have knowledge of the system. They do not really affect the business people (besides from some\nstrange cases where they request a certain amount of test coverage).",[439,60746,60747,60748,60753],{},"But acceptance tests do really focus the business value of the application. They usually test complete workflows or\n“features” of an application. The product owners user stories should have acceptance criteria that can be expressed as\nacceptance tests. The tests should not care about how these criterias are met but if. So acceptance tests are testing\nfrom the “outside” as a ",[1002,60749,60752],{"href":60750,"rel":60751},"http://www.webopedia.com/TERM/B/Black_Box_Testing.html",[1006],"complete blackbox"," (without knowledge\nof the internals of the application) test.",[439,60755,60756],{},"Of course these tests can be executed continuously and by this it can be ensured that the user story or feature works as\nexpected – and always will. So these tests are not only for us developers, they are for our clients. By the way this\nalso makes good and colourful reporting even more important.",[1065,60758,60760],{"id":60759},"how-we-do-it-overview","How we do it – Overview",[439,60762,60763],{},"This post should be the beginning of a whole series that describes how we do web-testing at synyx. So after I gave a\nquick overview why we do it let me tell you how we do it in a high level overview. Afterwards there will be follow-up\nposts that describe the important aspects in more detail.",[994,60765,60766,60774,60777,60780,60783,60792,60795],{},[997,60767,60768,60769],{},"Tests are written in Java/JUnit using ",[1002,60770,60773],{"href":60771,"rel":60772},"http://seleniumhq.org/",[1006],"Selenium Webdriver",[997,60775,60776],{},"Seleniums RemoteWebdriver allows the browser to run on another host as the test",[997,60778,60779],{},"The grid functionality of selenium-server is used to be able to request a big variation of different browsers and\nversions using the same initialization-strategy and – of course – to scale up",[997,60781,60782],{},"The tests are executed automatically several times – once for each browser we want to ensure compability with",[997,60784,60785,60786,60791],{},"Tests are written in ",[1002,60787,60790],{"href":60788,"rel":60789},"http://dannorth.net/introducing-bdd/",[1006],"BDD-style"," and use abstractions of actions (Steps) and\npages",[997,60793,60794],{},"Tests are reported in a nice “manager-friendly” way including pie charts and screenshots",[997,60796,60797,60800],{},[1002,60798,60344],{"href":60342,"rel":60799},[1006]," executes these tests and generates the report continuously against a system that is\nautomatically deployed (continuous deployment)",[439,60802,60803],{},"So stay tuned for detailed information about ATDD / webtests at synyx during the next weeks.",{"title":469,"searchDepth":507,"depth":507,"links":60805},[60806],{"id":60711,"depth":507,"text":60712,"children":60807},[60808,60809],{"id":60727,"depth":547,"text":60728},{"id":60759,"depth":547,"text":60760},[1030],"2013-01-23T10:47:24","https://synyx.de/blog/atdd-at-synyx/",{},"/blog/atdd-at-synyx",{"title":60702,"description":469},"atdd-at-synyx","blog/atdd-at-synyx",[53608,60819,24886,20699,60820,18497,60697],"atdd","tdd","Overview – Why and how we do web-testing In my team at synyx we wrote a lot of tests in 2012. Most of the tests were unit-tests (as a consequence…","YNjddH_uDwOTD_ywq9wdz-CWj4psWVBGYIAR-kxli38",{"id":60824,"title":60825,"author":60826,"body":60827,"category":60923,"date":60924,"description":60925,"extension":1034,"link":60926,"meta":60927,"navigation":916,"path":60928,"seo":60929,"slug":60831,"stem":60930,"tags":60931,"teaser":60939,"__hash__":60940},"blog/blog/android-size-depending-orientation-lock.md","Android: size depending orientation lock",[190],{"type":432,"value":60828,"toc":60921},[60829,60832,60835,60838,60847,60850,60853,60907,60910,60913,60916,60919],[435,60830,60825],{"id":60831},"android-size-depending-orientation-lock",[439,60833,60834],{},"We had a case in an internal app, where on Phones only the Portrait mode should be possible and on Tablets only the\nLandscape mode. So I googled a bit and tried out some things, and here is the solution I found for this problem.",[439,60836,60837],{},"First, in each Activiy in the AndroidManifest (or each Activity that should have this behaviour, but I prefer a\nconsistent behaviour for the whole app), declare the following:",[464,60839,60841],{"className":6253,"code":60840,"language":6255,"meta":469,"style":469},"android:screenOrientation=\"nosensor\"\n",[471,60842,60843],{"__ignoreMap":469},[474,60844,60845],{"class":476,"line":477},[474,60846,60840],{},[439,60848,60849],{},"This will prevent the Activity to switch orientations if the user rotates the device.",[439,60851,60852],{},"Then create a BaseActivity that all your Activities will be extending. In the onCreate we’ll do the other part of the\ntrick.",[464,60854,60856],{"className":709,"code":60855,"language":711,"meta":469,"style":469},"\n@Override\n protected void onCreate(Bundle savedInstanceState) {\n if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE\n && (this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) \u003C Configuration.SCREENLAYOUT_SIZE_LARGE) {\n setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n }else if((this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE){\n setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n }\n super.onCreate(savedInstanceState);\n }\n\n",[471,60857,60858,60862,60866,60870,60875,60880,60885,60890,60895,60899,60903],{"__ignoreMap":469},[474,60859,60860],{"class":476,"line":477},[474,60861,917],{"emptyLinePlaceholder":916},[474,60863,60864],{"class":476,"line":507},[474,60865,29178],{},[474,60867,60868],{"class":476,"line":547},[474,60869,28602],{},[474,60871,60872],{"class":476,"line":584},[474,60873,60874],{}," if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE\n",[474,60876,60877],{"class":476,"line":607},[474,60878,60879],{}," && (this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) \u003C Configuration.SCREENLAYOUT_SIZE_LARGE) {\n",[474,60881,60882],{"class":476,"line":642},[474,60883,60884],{}," setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n",[474,60886,60887],{"class":476,"line":663},[474,60888,60889],{}," }else if((this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE){\n",[474,60891,60892],{"class":476,"line":694},[474,60893,60894],{}," setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n",[474,60896,60897],{"class":476,"line":700},[474,60898,5704],{},[474,60900,60901],{"class":476,"line":913},[474,60902,28607],{},[474,60904,60905],{"class":476,"line":920},[474,60906,1276],{},[439,60908,60909],{},"Here we check for the requested screen orientation and change it according to the screen size of the device. If it’s at\nleast a large (as in Configuration.SCREENLAYOUT_SIZE_LARGE) display, only allow Landscape, otherwise, only allow\nPortrait mode. We need this, because the Activity is normally started with the same orientation the device had on start\nof the Activity.",[439,60911,60912],{},"Now only make sure you have a Landscape layout (res/layout-land) and a Portait layout (res/layout-port) for all\nActivities that haven’t got a layout file that’s used for both orientations.",[439,60914,60915],{},"Also make sure to call the super.onCreate() in the child classes onCreate() first, otherwise it could load the wrong\nlayout!",[439,60917,60918],{},"For now this small solution worked for us, but we don’t have that many different devices. If you encounter any device\nthis doesn’t work with, or encounter some other issuese with it, please let us know!",[1024,60920,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":60922},[],[11122,1413],"2013-01-18T09:24:34","We had a case in an internal app, where on Phones only the Portrait mode should be possible and on Tablets only the\\nLandscape mode. So I googled a bit and tried out some things, and here is the solution I found for this problem.","https://synyx.de/blog/android-size-depending-orientation-lock/",{},"/blog/android-size-depending-orientation-lock",{"title":60825,"description":60834},"blog/android-size-depending-orientation-lock",[11132,60932,60933,60934,60935,60936,60937,60938],"landscape","layout","lock","orientation","portrait","screen-size","tablet","We had a case in an internal app, where on Phones only the Portrait mode should be possible and on Tablets only the Landscape mode. So I googled a bit…","jixMMHEhK8mmvfHwd351p6yWAaNQ9FQxySWBOP7RgxY",{"id":60942,"title":60943,"author":60944,"body":60945,"category":62083,"date":62084,"description":60952,"extension":1034,"link":62085,"meta":62086,"navigation":916,"path":62087,"seo":62088,"slug":60949,"stem":62089,"tags":62090,"teaser":62095,"__hash__":62096},"blog/blog/a-small-look-into-google-cloud-messages.md","A small look into Google Cloud Messages",[190],{"type":432,"value":60946,"toc":62079},[60947,60950,60953,60961,60966,60970,60973,60976,61041,61044,61099,61102,61111,61114,61117,61120,61148,61151,61160,61163,61178,61181,61251,61254,61257,61353,61356,61359,61362,61366,61369,61376,61379,61509,61512,61948,61951,62046,62049,62052,62062,62065,62068,62076],[435,60948,60943],{"id":60949},"a-small-look-into-google-cloud-messages",[439,60951,60952],{},"Within the scope of some Android R&D I took a look at Google’s Cloud Message Service, GCM.",[439,60954,60955,60956,60960],{},"Well, the starter guide at",[1002,60957,60958],{"href":60958,"rel":60959},"http://developer.android.com/google/gcm/gs.html",[1006]," is almost all you need to get started, so\nI’ll explain my setup and some further instructions for a small test case.",[439,60962,60963],{},[990,60964,60965],{},"In case you already decided to setup GCM for yourself: make sure to do the guide above until you reach the ‘Writing the\nAndroid Application’ part.",[3938,60967,60969],{"id":60968},"the-android-application","The Android Application",[439,60971,60972],{},"Start with creating a new Android Project with a minimum Android API version of 8 (2.2). I just named it\n‘CloudMessageTest’ with the MainActivity ‘CloudMessageTestActivity’. After you’ve done that, create a folder named ‘lib’\nin your app’s root directory and copy the android GCM library from\nYOUR_SDK_ROOT/extras/google/gcm/gcm-client/dist/gcm.jar to the created lib folder. Next, Add the library to your build\npath (go into the Project properties -> Java Build Path -> Tab Libraries and select add JARs to add the gcm.jar).",[439,60974,60975],{},"For GCM to work, we need to add some permissions to our AndroidManifest:",[464,60977,60979],{"className":6253,"code":60978,"language":6255,"meta":469,"style":469},"\u003Cpermission\nandroid:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\nandroid:protectionLevel=\"signature\" />\n\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\" />\n\u003C!-- App receives GCM messages. -->\n\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\" />\n\u003C!-- GCM connects to Google Services. -->\n\u003Cuses-permission android:name=\"android.permission.INTERNET\" />\n\u003C!-- GCM requires a Google account. -->\n\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n\u003C!-- Keeps the processor from sleeping when a message is received. -->\n\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\" />\n",[471,60980,60981,60986,60991,60996,61001,61006,61011,61016,61021,61026,61031,61036],{"__ignoreMap":469},[474,60982,60983],{"class":476,"line":477},[474,60984,60985],{},"\u003Cpermission\n",[474,60987,60988],{"class":476,"line":507},[474,60989,60990],{},"android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\n",[474,60992,60993],{"class":476,"line":547},[474,60994,60995],{},"android:protectionLevel=\"signature\" />\n",[474,60997,60998],{"class":476,"line":584},[474,60999,61000],{},"\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\" />\n",[474,61002,61003],{"class":476,"line":607},[474,61004,61005],{},"\u003C!-- App receives GCM messages. -->\n",[474,61007,61008],{"class":476,"line":642},[474,61009,61010],{},"\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\" />\n",[474,61012,61013],{"class":476,"line":663},[474,61014,61015],{},"\u003C!-- GCM connects to Google Services. -->\n",[474,61017,61018],{"class":476,"line":694},[474,61019,61020],{},"\u003Cuses-permission android:name=\"android.permission.INTERNET\" />\n",[474,61022,61023],{"class":476,"line":700},[474,61024,61025],{},"\u003C!-- GCM requires a Google account. -->\n",[474,61027,61028],{"class":476,"line":913},[474,61029,61030],{},"\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n",[474,61032,61033],{"class":476,"line":920},[474,61034,61035],{},"\u003C!-- Keeps the processor from sleeping when a message is received. -->\n",[474,61037,61038],{"class":476,"line":926},[474,61039,61040],{},"\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\" />\n",[439,61042,61043],{},"And declare a GCM broadcast receiver within the application tag:",[464,61045,61047],{"className":6253,"code":61046,"language":6255,"meta":469,"style":469},"\u003Creceiver\n android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n android:permission=\"com.google.android.c2dm.permission.SEND\" >\n \u003Cintent-filter>\n \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\" />\n \u003Ccategory android:name=\"com.synyx.cloudmessagetest\" />\n \u003C/intent-filter>\n\u003C/receiver>\n\u003Cservice android:name=\".GCMIntentService\" />\n",[471,61048,61049,61054,61059,61064,61069,61074,61079,61084,61089,61094],{"__ignoreMap":469},[474,61050,61051],{"class":476,"line":477},[474,61052,61053],{},"\u003Creceiver\n",[474,61055,61056],{"class":476,"line":507},[474,61057,61058],{}," android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n",[474,61060,61061],{"class":476,"line":547},[474,61062,61063],{}," android:permission=\"com.google.android.c2dm.permission.SEND\" >\n",[474,61065,61066],{"class":476,"line":584},[474,61067,61068],{}," \u003Cintent-filter>\n",[474,61070,61071],{"class":476,"line":607},[474,61072,61073],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n",[474,61075,61076],{"class":476,"line":642},[474,61077,61078],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\" />\n",[474,61080,61081],{"class":476,"line":663},[474,61082,61083],{}," \u003Ccategory android:name=\"com.synyx.cloudmessagetest\" />\n",[474,61085,61086],{"class":476,"line":694},[474,61087,61088],{}," \u003C/intent-filter>\n",[474,61090,61091],{"class":476,"line":700},[474,61092,61093],{},"\u003C/receiver>\n",[474,61095,61096],{"class":476,"line":913},[474,61097,61098],{},"\u003Cservice android:name=\".GCMIntentService\" />\n",[439,61100,61101],{},"The GCMIntentService has to be created by us. And that is what we’ll do now. First off, the GCMIntentService has to\nextend the class GCMBaseIntentService:",[464,61103,61105],{"className":709,"code":61104,"language":711,"meta":469,"style":469},"public class GCMIntentService extends GCMBaseIntentService {\n",[471,61106,61107],{"__ignoreMap":469},[474,61108,61109],{"class":476,"line":477},[474,61110,61104],{},[439,61112,61113],{},"Now implement all the necessary methods. The only method we will use for this little test is onMessage(). We want to\nquickly see if we get a message for this app, so that we can confirm that it works. So we just create a notification\nwith the Notification Builder.",[439,61115,61116],{},"Because we are on an older minimum version of Android, we need to add the support library to have access to the\nNotification Builder. Add it by right clicking the project -> Android tools -> Add Support Library.",[439,61118,61119],{},"First in the onMessage() method, we need to get access to the Main Thread of our App.",[464,61121,61123],{"className":709,"code":61122,"language":711,"meta":469,"style":469},"Handler h = new Handler(Looper.getMainLooper());\nh.post(new Runnable() {\n public void run() {\n }\n}\n",[471,61124,61125,61130,61135,61140,61144],{"__ignoreMap":469},[474,61126,61127],{"class":476,"line":477},[474,61128,61129],{},"Handler h = new Handler(Looper.getMainLooper());\n",[474,61131,61132],{"class":476,"line":507},[474,61133,61134],{},"h.post(new Runnable() {\n",[474,61136,61137],{"class":476,"line":547},[474,61138,61139],{}," public void run() {\n",[474,61141,61142],{"class":476,"line":584},[474,61143,1276],{},[474,61145,61146],{"class":476,"line":607},[474,61147,703],{},[439,61149,61150],{},"In the run() method, we get us an Intent from our MainActivity",[464,61152,61154],{"className":709,"code":61153,"language":711,"meta":469,"style":469},"Intent notificationIntent = new Intent(context, CloudMessageTestActivity.class);\n",[471,61155,61156],{"__ignoreMap":469},[474,61157,61158],{"class":476,"line":477},[474,61159,61153],{},[439,61161,61162],{},"And then wrap it with a PendingIntent for the NotificationBuilder",[464,61164,61166],{"className":709,"code":61165,"language":711,"meta":469,"style":469},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n notificationIntent, 0);\n",[471,61167,61168,61173],{"__ignoreMap":469},[474,61169,61170],{"class":476,"line":477},[474,61171,61172],{},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n",[474,61174,61175],{"class":476,"line":507},[474,61176,61177],{}," notificationIntent, 0);\n",[439,61179,61180],{},"Finally, use the NotificationBuilder to create and send the notification",[464,61182,61184],{"className":709,"code":61183,"language":711,"meta":469,"style":469},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n context);\nbuilder.setContentIntent(pendingIntent);\nbuilder.setAutoCancel(true);\nbuilder.setSmallIcon(R.drawable.ic_launcher);\n//this is added on the server side\nString text = intent.getStringExtra(\"text\");\nbuilder.setContentText(text);\nbuilder.setContentTitle(\"New message from the cloud!\");\nNotification noti = builder.build();\nNotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n//just set the mId to 1, because we don't care about it in this case\nmNotificationManager.notify(1, noti);\n",[471,61185,61186,61191,61196,61201,61206,61211,61216,61221,61226,61231,61236,61241,61246],{"__ignoreMap":469},[474,61187,61188],{"class":476,"line":477},[474,61189,61190],{},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n",[474,61192,61193],{"class":476,"line":507},[474,61194,61195],{}," context);\n",[474,61197,61198],{"class":476,"line":547},[474,61199,61200],{},"builder.setContentIntent(pendingIntent);\n",[474,61202,61203],{"class":476,"line":584},[474,61204,61205],{},"builder.setAutoCancel(true);\n",[474,61207,61208],{"class":476,"line":607},[474,61209,61210],{},"builder.setSmallIcon(R.drawable.ic_launcher);\n",[474,61212,61213],{"class":476,"line":642},[474,61214,61215],{},"//this is added on the server side\n",[474,61217,61218],{"class":476,"line":663},[474,61219,61220],{},"String text = intent.getStringExtra(\"text\");\n",[474,61222,61223],{"class":476,"line":694},[474,61224,61225],{},"builder.setContentText(text);\n",[474,61227,61228],{"class":476,"line":700},[474,61229,61230],{},"builder.setContentTitle(\"New message from the cloud!\");\n",[474,61232,61233],{"class":476,"line":913},[474,61234,61235],{},"Notification noti = builder.build();\n",[474,61237,61238],{"class":476,"line":920},[474,61239,61240],{},"NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n",[474,61242,61243],{"class":476,"line":926},[474,61244,61245],{},"//just set the mId to 1, because we don't care about it in this case\n",[474,61247,61248],{"class":476,"line":932},[474,61249,61250],{},"mNotificationManager.notify(1, noti);\n",[439,61252,61253],{},"That’ it with the GCMIntentService.",[439,61255,61256],{},"Now we let the app register itself with GCM in our MainActivity, which is fairly easy:",[464,61258,61260],{"className":709,"code":61259,"language":711,"meta":469,"style":469},"//set your senderId from the API here!\n private static final String SENDER_ID = \"1234567890\";\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n GCMRegistrar.checkDevice(this);\n GCMRegistrar.checkManifest(this);\n final String regId = GCMRegistrar.getRegistrationId(this);\n // if we don't have a regId yet, register at gcm\n if (regId.equals(\"\")) {\n GCMRegistrar.register(this, SENDER_ID);\n Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n // just log the registrationId for this test case.\n Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n } else {\n Log.i(this.getClass().getName(), \"Already registered\");\n Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n }\n}\n",[471,61261,61262,61267,61272,61276,61281,61286,61291,61296,61301,61306,61311,61316,61321,61326,61331,61336,61341,61345,61349],{"__ignoreMap":469},[474,61263,61264],{"class":476,"line":477},[474,61265,61266],{},"//set your senderId from the API here!\n",[474,61268,61269],{"class":476,"line":507},[474,61270,61271],{}," private static final String SENDER_ID = \"1234567890\";\n",[474,61273,61274],{"class":476,"line":547},[474,61275,29178],{},[474,61277,61278],{"class":476,"line":584},[474,61279,61280],{},"protected void onCreate(Bundle savedInstanceState) {\n",[474,61282,61283],{"class":476,"line":607},[474,61284,61285],{}," GCMRegistrar.checkDevice(this);\n",[474,61287,61288],{"class":476,"line":642},[474,61289,61290],{}," GCMRegistrar.checkManifest(this);\n",[474,61292,61293],{"class":476,"line":663},[474,61294,61295],{}," final String regId = GCMRegistrar.getRegistrationId(this);\n",[474,61297,61298],{"class":476,"line":694},[474,61299,61300],{}," // if we don't have a regId yet, register at gcm\n",[474,61302,61303],{"class":476,"line":700},[474,61304,61305],{}," if (regId.equals(\"\")) {\n",[474,61307,61308],{"class":476,"line":913},[474,61309,61310],{}," GCMRegistrar.register(this, SENDER_ID);\n",[474,61312,61313],{"class":476,"line":920},[474,61314,61315],{}," Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n",[474,61317,61318],{"class":476,"line":926},[474,61319,61320],{}," // just log the registrationId for this test case.\n",[474,61322,61323],{"class":476,"line":932},[474,61324,61325],{}," Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n",[474,61327,61328],{"class":476,"line":938},[474,61329,61330],{}," } else {\n",[474,61332,61333],{"class":476,"line":944},[474,61334,61335],{}," Log.i(this.getClass().getName(), \"Already registered\");\n",[474,61337,61338],{"class":476,"line":950},[474,61339,61340],{}," Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n",[474,61342,61343],{"class":476,"line":956},[474,61344,61325],{},[474,61346,61347],{"class":476,"line":962},[474,61348,23666],{},[474,61350,61351],{"class":476,"line":4876},[474,61352,703],{},[439,61354,61355],{},"Don’t forget to replace the SENDER_ID with yours!",[439,61357,61358],{},"I haven’t implemented a way to let the server know the registrationId, because reading it from the log seemed sufficient\nfor me in this case.",[439,61360,61361],{},"With this, we finished our small app and can begin implementing the server part.",[3938,61363,61365],{"id":61364},"the-web-application","The Web Application",[439,61367,61368],{},"For the Web Application, I created a maven web project with spring-webmvc and named it ‘GCMTestServer’. I’ll leave out\nthe config stuff for now, as everyone can use his/her favorite stack for this. The full sources with the configs are\nattached at the end of the blogpost.",[439,61370,61371,61372,17548],{},"Instead of just copying the GCM server library into the project, I searched a bit and found someone, who created a\nrepository for\nit. (",[1002,61373,61374],{"href":61374,"rel":61375},"https://github.com/slorber/gcm-server-repository",[1006],[439,61377,61378],{},"We start with creating the Sender class, which isn’t that hard either.",[464,61380,61382],{"className":709,"code":61381,"language":711,"meta":469,"style":469},"public class GCMSender {\n public String apiKey = null;\n public GCMSender(String apiKey) {\n this.apiKey = apiKey;\n }\n public String send(String text, String id) throws IOException {\n Sender sender = new Sender(apiKey);\n Builder builder = new Message.Builder();\n builder.addData(\"text\", text);\n Result result = sender.send(builder.build(), id, 5);\n if (result.getMessageId() != null) {\n String canonicalRegId = result.getCanonicalRegistrationId();\n if (canonicalRegId != null) {\n // same device has more than on registration ID: update database\n return \"same device has more than on registration ID: update database\";\n }\n } else {\n String error = result.getErrorCodeName();\n if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n // application has been removed from device - unregister database\n return \"application has been removed from device - unregister database\";\n }\n }\n return null;\n }\n}\n",[471,61383,61384,61389,61394,61399,61404,61408,61413,61418,61423,61428,61433,61438,61443,61448,61453,61458,61463,61468,61473,61478,61483,61488,61492,61496,61501,61505],{"__ignoreMap":469},[474,61385,61386],{"class":476,"line":477},[474,61387,61388],{},"public class GCMSender {\n",[474,61390,61391],{"class":476,"line":507},[474,61392,61393],{}," public String apiKey = null;\n",[474,61395,61396],{"class":476,"line":547},[474,61397,61398],{}," public GCMSender(String apiKey) {\n",[474,61400,61401],{"class":476,"line":584},[474,61402,61403],{}," this.apiKey = apiKey;\n",[474,61405,61406],{"class":476,"line":607},[474,61407,23680],{},[474,61409,61410],{"class":476,"line":642},[474,61411,61412],{}," public String send(String text, String id) throws IOException {\n",[474,61414,61415],{"class":476,"line":663},[474,61416,61417],{}," Sender sender = new Sender(apiKey);\n",[474,61419,61420],{"class":476,"line":694},[474,61421,61422],{}," Builder builder = new Message.Builder();\n",[474,61424,61425],{"class":476,"line":700},[474,61426,61427],{}," builder.addData(\"text\", text);\n",[474,61429,61430],{"class":476,"line":913},[474,61431,61432],{}," Result result = sender.send(builder.build(), id, 5);\n",[474,61434,61435],{"class":476,"line":920},[474,61436,61437],{}," if (result.getMessageId() != null) {\n",[474,61439,61440],{"class":476,"line":926},[474,61441,61442],{}," String canonicalRegId = result.getCanonicalRegistrationId();\n",[474,61444,61445],{"class":476,"line":932},[474,61446,61447],{}," if (canonicalRegId != null) {\n",[474,61449,61450],{"class":476,"line":938},[474,61451,61452],{}," // same device has more than on registration ID: update database\n",[474,61454,61455],{"class":476,"line":944},[474,61456,61457],{}," return \"same device has more than on registration ID: update database\";\n",[474,61459,61460],{"class":476,"line":950},[474,61461,61462],{}," }\n",[474,61464,61465],{"class":476,"line":956},[474,61466,61467],{}," } else {\n",[474,61469,61470],{"class":476,"line":962},[474,61471,61472],{}," String error = result.getErrorCodeName();\n",[474,61474,61475],{"class":476,"line":4876},[474,61476,61477],{}," if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n",[474,61479,61480],{"class":476,"line":4888},[474,61481,61482],{}," // application has been removed from device - unregister database\n",[474,61484,61485],{"class":476,"line":4900},[474,61486,61487],{}," return \"application has been removed from device - unregister database\";\n",[474,61489,61490],{"class":476,"line":4913},[474,61491,61462],{},[474,61493,61494],{"class":476,"line":4921},[474,61495,23666],{},[474,61497,61498],{"class":476,"line":4932},[474,61499,61500],{}," return null;\n",[474,61502,61503],{"class":476,"line":4938},[474,61504,23680],{},[474,61506,61507],{"class":476,"line":4946},[474,61508,703],{},[439,61510,61511],{},"To send messages from the server, I created a small jsp page where I can enter the text and the RegistrationId of the\nuser to send the message to:",[464,61513,61515],{"className":15039,"code":61514,"language":15041,"meta":469,"style":469},"\u003C%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> \u003C%@page\ncontentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n\u003C!DOCTYPE html>\n\u003Chtml>\n \u003Chead>\n \u003Cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n \u003Clink\n rel=\"stylesheet\"\n type=\"text/css\"\n href=\"/GCMTestServer/frontend_resources/style.css\"\n media=\"screen\"\n />\n \u003Ctitle>GCM Sender\u003C/title>\n \u003C/head>\n \u003Cbody>\n \u003Ch1>GCM Sender\u003C/h1>\n \u003Cc:if test=\"${success == true}\">\n \u003Cdiv class=\"success\">sending successful!\u003C/div>\n \u003C/c:if>\n \u003Cc:if test=\"${error == true}\">\n \u003Cdiv class=\"error\">\n Error at sending!\n \u003Cp>${errormessage}\u003C/p>\n \u003C/div>\n \u003C/c:if>\n \u003Cform method=\"POST\">\n \u003Clabel for=\"text\">Text\u003C/label\n >\u003Cinput name=\"text\" id=\"text\" type=\"text\" />\u003Cbr />\n \u003Clabel for=\"id\">Registration-Id\u003C/label\n >\u003Cinput name=\"id\" id=\"id\" type=\"text\" />\u003Cbr />\n \u003Cinput type=\"submit\" />\n \u003C/form>\n \u003C/body>\n\u003C/html>\n",[471,61516,61517,61529,61534,61547,61555,61564,61589,61596,61606,61616,61626,61636,61641,61655,61663,61672,61684,61701,61722,61731,61746,61761,61766,61780,61788,61796,61811,61830,61862,61880,61910,61924,61932,61940],{"__ignoreMap":469},[474,61518,61519,61521,61524,61526],{"class":476,"line":477},[474,61520,15048],{"class":523},[474,61522,61523],{"class":503},"%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> ",[474,61525,15048],{"class":523},[474,61527,61528],{"class":503},"%@page\n",[474,61530,61531],{"class":476,"line":507},[474,61532,61533],{"class":503},"contentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n",[474,61535,61536,61539,61542,61545],{"class":476,"line":547},[474,61537,61538],{"class":503},"\u003C!",[474,61540,61541],{"class":15051},"DOCTYPE",[474,61543,61544],{"class":480}," html",[474,61546,15070],{"class":503},[474,61548,61549,61551,61553],{"class":476,"line":584},[474,61550,15048],{"class":503},[474,61552,15041],{"class":15051},[474,61554,15070],{"class":503},[474,61556,61557,61559,61562],{"class":476,"line":607},[474,61558,15075],{"class":503},[474,61560,61561],{"class":15051},"head",[474,61563,15070],{"class":503},[474,61565,61566,61569,61572,61575,61577,61580,61582,61584,61587],{"class":476,"line":642},[474,61567,61568],{"class":503}," \u003C",[474,61570,61571],{"class":15051},"meta",[474,61573,61574],{"class":480}," http-equiv",[474,61576,811],{"class":503},[474,61578,61579],{"class":484},"\"Content-Type\"",[474,61581,1683],{"class":480},[474,61583,811],{"class":503},[474,61585,61586],{"class":484},"\"text/html; charset=UTF-8\"",[474,61588,15097],{"class":503},[474,61590,61591,61593],{"class":476,"line":663},[474,61592,61568],{"class":503},[474,61594,61595],{"class":15051},"link\n",[474,61597,61598,61601,61603],{"class":476,"line":694},[474,61599,61600],{"class":480}," rel",[474,61602,811],{"class":503},[474,61604,61605],{"class":484},"\"stylesheet\"\n",[474,61607,61608,61611,61613],{"class":476,"line":700},[474,61609,61610],{"class":480}," type",[474,61612,811],{"class":503},[474,61614,61615],{"class":484},"\"text/css\"\n",[474,61617,61618,61621,61623],{"class":476,"line":913},[474,61619,61620],{"class":480}," href",[474,61622,811],{"class":503},[474,61624,61625],{"class":484},"\"/GCMTestServer/frontend_resources/style.css\"\n",[474,61627,61628,61631,61633],{"class":476,"line":920},[474,61629,61630],{"class":480}," media",[474,61632,811],{"class":503},[474,61634,61635],{"class":484},"\"screen\"\n",[474,61637,61638],{"class":476,"line":926},[474,61639,61640],{"class":503}," />\n",[474,61642,61643,61645,61648,61651,61653],{"class":476,"line":932},[474,61644,61568],{"class":503},[474,61646,61647],{"class":15051},"title",[474,61649,61650],{"class":503},">GCM Sender\u003C/",[474,61652,61647],{"class":15051},[474,61654,15070],{"class":503},[474,61656,61657,61659,61661],{"class":476,"line":938},[474,61658,15159],{"class":503},[474,61660,61561],{"class":15051},[474,61662,15070],{"class":503},[474,61664,61665,61667,61670],{"class":476,"line":944},[474,61666,15075],{"class":503},[474,61668,61669],{"class":15051},"body",[474,61671,15070],{"class":503},[474,61673,61674,61676,61678,61680,61682],{"class":476,"line":950},[474,61675,61568],{"class":503},[474,61677,435],{"class":15051},[474,61679,61650],{"class":503},[474,61681,435],{"class":15051},[474,61683,15070],{"class":503},[474,61685,61686,61688,61691,61694,61696,61699],{"class":476,"line":956},[474,61687,61568],{"class":503},[474,61689,61690],{"class":523},"c:if",[474,61692,61693],{"class":480}," test",[474,61695,811],{"class":503},[474,61697,61698],{"class":484},"\"${success == true}\"",[474,61700,15070],{"class":503},[474,61702,61703,61705,61707,61710,61712,61715,61718,61720],{"class":476,"line":962},[474,61704,23272],{"class":503},[474,61706,15027],{"class":15051},[474,61708,61709],{"class":480}," class",[474,61711,811],{"class":503},[474,61713,61714],{"class":484},"\"success\"",[474,61716,61717],{"class":503},">sending successful!\u003C/",[474,61719,15027],{"class":15051},[474,61721,15070],{"class":503},[474,61723,61724,61727,61729],{"class":476,"line":4876},[474,61725,61726],{"class":503}," \u003C/",[474,61728,61690],{"class":523},[474,61730,15070],{"class":503},[474,61732,61733,61735,61737,61739,61741,61744],{"class":476,"line":4888},[474,61734,61568],{"class":503},[474,61736,61690],{"class":523},[474,61738,61693],{"class":480},[474,61740,811],{"class":503},[474,61742,61743],{"class":484},"\"${error == true}\"",[474,61745,15070],{"class":503},[474,61747,61748,61750,61752,61754,61756,61759],{"class":476,"line":4900},[474,61749,23272],{"class":503},[474,61751,15027],{"class":15051},[474,61753,61709],{"class":480},[474,61755,811],{"class":503},[474,61757,61758],{"class":484},"\"error\"",[474,61760,15070],{"class":503},[474,61762,61763],{"class":476,"line":4913},[474,61764,61765],{"class":503}," Error at sending!\n",[474,61767,61768,61771,61773,61776,61778],{"class":476,"line":4921},[474,61769,61770],{"class":503}," \u003C",[474,61772,439],{"class":15051},[474,61774,61775],{"class":503},">${errormessage}\u003C/",[474,61777,439],{"class":15051},[474,61779,15070],{"class":503},[474,61781,61782,61784,61786],{"class":476,"line":4932},[474,61783,23300],{"class":503},[474,61785,15027],{"class":15051},[474,61787,15070],{"class":503},[474,61789,61790,61792,61794],{"class":476,"line":4938},[474,61791,61726],{"class":503},[474,61793,61690],{"class":523},[474,61795,15070],{"class":503},[474,61797,61798,61800,61802,61804,61806,61809],{"class":476,"line":4946},[474,61799,61568],{"class":503},[474,61801,14954],{"class":15051},[474,61803,15062],{"class":480},[474,61805,811],{"class":503},[474,61807,61808],{"class":484},"\"POST\"",[474,61810,15070],{"class":503},[474,61812,61813,61815,61818,61820,61822,61824,61827],{"class":476,"line":4952},[474,61814,23272],{"class":503},[474,61816,61817],{"class":15051},"label",[474,61819,565],{"class":480},[474,61821,811],{"class":503},[474,61823,15086],{"class":484},[474,61825,61826],{"class":503},">Text\u003C/",[474,61828,61829],{"class":15051},"label\n",[474,61831,61832,61835,61837,61839,61841,61843,61845,61847,61849,61851,61853,61855,61858,61860],{"class":476,"line":4957},[474,61833,61834],{"class":503}," >\u003C",[474,61836,15078],{"class":15051},[474,61838,15089],{"class":480},[474,61840,811],{"class":503},[474,61842,15086],{"class":484},[474,61844,15185],{"class":480},[474,61846,811],{"class":503},[474,61848,15086],{"class":484},[474,61850,15081],{"class":480},[474,61852,811],{"class":503},[474,61854,15086],{"class":484},[474,61856,61857],{"class":503}," />\u003C",[474,61859,12024],{"class":15051},[474,61861,15097],{"class":503},[474,61863,61864,61866,61868,61870,61872,61875,61878],{"class":476,"line":4969},[474,61865,23272],{"class":503},[474,61867,61817],{"class":15051},[474,61869,565],{"class":480},[474,61871,811],{"class":503},[474,61873,61874],{"class":484},"\"id\"",[474,61876,61877],{"class":503},">Registration-Id\u003C/",[474,61879,61829],{"class":15051},[474,61881,61882,61884,61886,61888,61890,61892,61894,61896,61898,61900,61902,61904,61906,61908],{"class":476,"line":4990},[474,61883,61834],{"class":503},[474,61885,15078],{"class":15051},[474,61887,15089],{"class":480},[474,61889,811],{"class":503},[474,61891,61874],{"class":484},[474,61893,15185],{"class":480},[474,61895,811],{"class":503},[474,61897,61874],{"class":484},[474,61899,15081],{"class":480},[474,61901,811],{"class":503},[474,61903,15086],{"class":484},[474,61905,61857],{"class":503},[474,61907,12024],{"class":15051},[474,61909,15097],{"class":503},[474,61911,61912,61914,61916,61918,61920,61922],{"class":476,"line":5001},[474,61913,23272],{"class":503},[474,61915,15078],{"class":15051},[474,61917,15081],{"class":480},[474,61919,811],{"class":503},[474,61921,15131],{"class":484},[474,61923,15097],{"class":503},[474,61925,61926,61928,61930],{"class":476,"line":5013},[474,61927,61726],{"class":503},[474,61929,14954],{"class":15051},[474,61931,15070],{"class":503},[474,61933,61934,61936,61938],{"class":476,"line":5024},[474,61935,15159],{"class":503},[474,61937,61669],{"class":15051},[474,61939,15070],{"class":503},[474,61941,61942,61944,61946],{"class":476,"line":5035},[474,61943,15168],{"class":503},[474,61945,15041],{"class":15051},[474,61947,15070],{"class":503},[439,61949,61950],{},"In the corresponding Controller to process the request, send the message:",[464,61952,61954],{"className":709,"code":61953,"language":711,"meta":469,"style":469}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n public String send(Model model,\n @RequestParam(\"text\") String text,\n @RequestParam(\"id\") String id) {\n String error = null;\n try {\n error = gcmSender.send(text, id);\n } catch (IOException ex) {\n model.addAttribute(\"error\", true);\n model.addAttribute(\"errormessage\", ex.getMessage());\n }\n if (error == null) {\n model.addAttribute(\"success\", true);\n } else {\n model.addAttribute(\"error\", true);\n model.addAttribute(\"errormessage\", error);\n }\n return \"sender\";\n }\n",[471,61955,61956,61961,61966,61971,61976,61981,61986,61991,61996,62001,62006,62010,62015,62020,62024,62028,62033,62037,62042],{"__ignoreMap":469},[474,61957,61958],{"class":476,"line":477},[474,61959,61960],{}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n",[474,61962,61963],{"class":476,"line":507},[474,61964,61965],{}," public String send(Model model,\n",[474,61967,61968],{"class":476,"line":547},[474,61969,61970],{}," @RequestParam(\"text\") String text,\n",[474,61972,61973],{"class":476,"line":584},[474,61974,61975],{}," @RequestParam(\"id\") String id) {\n",[474,61977,61978],{"class":476,"line":607},[474,61979,61980],{}," String error = null;\n",[474,61982,61983],{"class":476,"line":642},[474,61984,61985],{}," try {\n",[474,61987,61988],{"class":476,"line":663},[474,61989,61990],{}," error = gcmSender.send(text, id);\n",[474,61992,61993],{"class":476,"line":694},[474,61994,61995],{}," } catch (IOException ex) {\n",[474,61997,61998],{"class":476,"line":700},[474,61999,62000],{}," model.addAttribute(\"error\", true);\n",[474,62002,62003],{"class":476,"line":913},[474,62004,62005],{}," model.addAttribute(\"errormessage\", ex.getMessage());\n",[474,62007,62008],{"class":476,"line":920},[474,62009,5704],{},[474,62011,62012],{"class":476,"line":926},[474,62013,62014],{}," if (error == null) {\n",[474,62016,62017],{"class":476,"line":932},[474,62018,62019],{}," model.addAttribute(\"success\", true);\n",[474,62021,62022],{"class":476,"line":938},[474,62023,21063],{},[474,62025,62026],{"class":476,"line":944},[474,62027,62000],{},[474,62029,62030],{"class":476,"line":950},[474,62031,62032],{}," model.addAttribute(\"errormessage\", error);\n",[474,62034,62035],{"class":476,"line":956},[474,62036,5704],{},[474,62038,62039],{"class":476,"line":962},[474,62040,62041],{}," return \"sender\";\n",[474,62043,62044],{"class":476,"line":4876},[474,62045,1276],{},[439,62047,62048],{},"Now we are ready to test it!",[439,62050,62051],{},"Start the app, copy the RegistrationId from the Logs, start the Server, enter the RegistrationId and a small text, and\nthere you go:",[439,62053,62054],{},[1002,62055,62058],{"href":62056,"rel":62057},"http://blog.synyx.de/2013/01/a-small-look-into-google-cloud-messages/notification/",[1006],[2205,62059],{"alt":62060,"src":62061},"notification","https://media.synyx.de/uploads//2012/12/notification-300x221.jpg",[439,62063,62064],{},"To get a better understanding, you can also read the rest of the starting guide and the other documentation.",[439,62066,62067],{},"All together, it’s really easy to use the GCM libraries and the messages are beeing sent to the user very fast (around\none second for me). I haven’t tried to use it in a greater context with more users yet, but I don’t think google will\nfail on this behalf 😛",[439,62069,62070,62071],{},"As promised, here are the full sources:",[1002,62072,62075],{"href":62073,"rel":62074},"https://media.synyx.de/uploads//2012/12/CloudMessageTest.zip",[1006],"CloudMessageTest",[1024,62077,62078],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":469,"searchDepth":507,"depth":507,"links":62080},[62081,62082],{"id":60968,"depth":507,"text":60969},{"id":61364,"depth":507,"text":61365},[11122,1413],"2013-01-08T08:21:24","https://synyx.de/blog/a-small-look-into-google-cloud-messages/",{},"/blog/a-small-look-into-google-cloud-messages",{"title":60943,"description":60952},"blog/a-small-look-into-google-cloud-messages",[11132,60314,62091,62092,62093,21245,62094],"gcm","google-cloud","messages","push-notification","Within the scope of some Android R&D I took a look at Google’s Cloud Message Service, GCM. Well, the starter guide at http://developer.android.com/google/gcm/gs.html is almost all you need to get started,…","SNvhUPzcxgotg8iPUfb-vwMd-SWI7AtSv7BLTJxy-9s",{"id":62098,"title":62099,"author":62100,"body":62101,"category":62348,"date":62349,"description":62108,"extension":1034,"link":62350,"meta":62351,"navigation":916,"path":62352,"seo":62353,"slug":62105,"stem":62354,"tags":62355,"teaser":62357,"__hash__":62358},"blog/blog/synyx-weihnachtsgedicht.md","synyx Weihnachtsgedicht",[262],{"type":432,"value":62102,"toc":62346},[62103,62106,62109,62112,62115,62118,62121,62124,62127,62130,62133,62136,62139,62142,62145,62148,62151,62154,62157,62160,62163,62166,62169,62172,62175,62178,62181,62184,62187,62190,62193,62196,62199,62202,62205,62208,62211,62214,62217,62220,62223,62226,62229,62232,62235,62238,62241,62244,62247,62250,62253,62256,62259,62262,62265,62268,62271,62274,62277,62280,62283,62286,62289,62292,62295,62298,62301,62304,62307,62310,62313,62316,62319,62322,62325,62328,62331,62334,62337,62340,62343],[435,62104,62099],{"id":62105},"synyx-weihnachtsgedicht",[439,62107,62108],{},"Vom weiten Nordpol komm ich her,",[439,62110,62111],{},"Ich muss euch sagen es buggte sehr,",[439,62113,62114],{},"Mein Navi versagte und auch mein Bord-PC,",[439,62116,62117],{},"Mein Schlitten stürzte in einen Haufen Schnee.",[439,62119,62120],{},"Gar hart ich auf meinen Allerwertesten knallte,",[439,62122,62123],{},"Der Knall noch in meinen Ohren schallte.",[439,62125,62126],{},"Zum Glück tat ich mir nichts, war nur verwirrt,",[439,62128,62129],{},"Wohin hatte ich mich bloß verirrt?",[439,62131,62132],{},"Vor Kälte schon ganz rote Ohren,",[439,62134,62135],{},"Die Nase fast schon abgefroren,",[439,62137,62138],{},"Sah ich ein Schild mit “Open Source”,",[439,62140,62141],{},"Das Schicksal führte mich auf den richtigen Kurs.",[439,62143,62144],{},"Die Dame mit den blonden Locken",[439,62146,62147],{},"Sah erstaunt drein – fast erschrocken.",[439,62149,62150],{},"Schließlich passiert’s nicht allzu oft,",[439,62152,62153],{},"Dass der Weihnachtsmann an der Türe klopft.",[439,62155,62156],{},"“Gnädiges Fräulein, ich hab ein Problem",[439,62158,62159],{},"Alles kaputt, das ganze System.",[439,62161,62162],{},"Viele Geschenke, die ich ausliefern muss:",[439,62164,62165],{},"Süßes, Spielzeug, viel Schönes im Überschuss.",[439,62167,62168],{},"Könntet ihr mir vielleicht helfen?",[439,62170,62171],{},"Ihr seid doch sowas wie Software-Elfen.",[439,62173,62174],{},"Habt sicher auch Ersatz-Hardware,",[439,62176,62177],{},"Die Reparatur ist bestimmt nicht schwer.”",[439,62179,62180],{},"“Sind Sie etwa”, sprach sie, “der Weihnachtsmann?",[439,62182,62183],{},"Ich kann’s nicht glauben, manno mann!",[439,62185,62186],{},"So kommen Sie doch erstmal rein,",[439,62188,62189],{},"Da draußen rumsteh’n muss nicht sein.",[439,62191,62192],{},"Und wärmen Sie sich bei uns auf.",[439,62194,62195],{},"Kaffee, Tee – Sie haben Lust worauf?",[439,62197,62198],{},"Folgen Sie mir einfach in die Küche.",[439,62200,62201],{},"Das hier sind übrigens uns’re Fische.”",[439,62203,62204],{},"Und in der Küche, oh welch ein Duft!",[439,62206,62207],{},"Ein Wohlgeruch hing in der Luft.",[439,62209,62210],{},"“Es ist”, sprach sie, “grad Mittagszeit,",[439,62212,62213],{},"Das heißt Kochmuddis Festmahl steht bereit.",[439,62215,62216],{},"Setzen Sie sich erstmal hin,",[439,62218,62219],{},"Ich informiere unser Team.",[439,62221,62222],{},"Lassen Sie es sich gut schmecken,",[439,62224,62225],{},"Das Essen ist zum Finger lecken.",[439,62227,62228],{},"Dr. Kill und die anderen Doktoren",[439,62230,62231],{},"Der Code Clinic haben für Sie offene Ohren.",[439,62233,62234],{},"Vielleicht lässt sich am Navi noch was machen",[439,62236,62237],{},"Und wir finden des Problems Ursachen.”",[439,62239,62240],{},"Gerade hatte ich aufgegessen,",[439,62242,62243],{},"Da kamen – man hatte mich nicht vergessen –",[439,62245,62246],{},"Schon Dr. Kill und die anderen Doktoren.",[439,62248,62249],{},"Und Sie ließen mich erfahren:",[439,62251,62252],{},"“Das ist ein wirklich schwerer Fall:",[439,62254,62255],{},"Unsaub’rer Code, Sonar Violations überall!",[439,62257,62258],{},"Von der mangelnden Testabdeckung will ich gar nicht sprechen,",[439,62260,62261],{},"Die ist geradezu ein Verbrechen.",[439,62263,62264],{},"Wenn Sie woll’n, tun wir das Beste.",[439,62266,62267],{},"Jedoch wär’s für Sie das Schönste,",[439,62269,62270],{},"Das Indy-Team mal anzuhauen,",[439,62272,62273],{},"Auf ihre Maßanfertigungen kann man bauen.",[439,62275,62276],{},"Vielleicht wär auch ein Smartphone nicht verkehrt.",[439,62278,62279],{},"Mit einer Navi-App die sich bewährt.",[439,62281,62282],{},"Auch Mobile Solutions zähl’n zu uns’rem Bereich.",[439,62284,62285],{},"Wenn Sie woll’n mach’n wir uns an die Arbeit gleich.",[439,62287,62288],{},"Damit alle Kinder ihre Geschenke bekommen,",[439,62290,62291],{},"Haben wir uns auch Ihrem alten Navi angenommen.",[439,62293,62294],{},"Und mit ein paar Tricks im Nullkommanix",[439,62296,62297],{},"Nun läuft es wieder dank uns’rem Quickfix.”",[439,62299,62300],{},"Und wie ich so in der Küche stand,",[439,62302,62303],{},"Näherte sich ein bärtiger Jemand.",[439,62305,62306],{},"“Hopp, hopp”, rief es, “alter Gesell,",[439,62308,62309],{},"Hebe die Beine und spute dich schnell.",[439,62311,62312],{},"Repariert ist nun dein Bord-PC,",[439,62314,62315],{},"Auch eingebaut ‘ne SSD.",[439,62317,62318],{},"Damit’s mit den Geschenken läuft,",[439,62320,62321],{},"Selbst wenn man mal ein Bierchen säuft.”",[439,62323,62324],{},"Mein Bord-PC war wieder fit,",[439,62326,62327],{},"Das Navi lotzste mich Schritt für Schritt.",[439,62329,62330],{},"So konnt’ ich fortsetzen meine Reise,",[439,62332,62333],{},"Alle Geschenke ausliefern glücklicherweise.",[439,62335,62336],{},"So rettete synyx das Weihnachtsfest,",[439,62338,62339],{},"Zumindest wenn man Phantasie walten lässt.",[439,62341,62342],{},"Wir wünschen ein schönes Weihnachtsfest und ganz klar:",[439,62344,62345],{},"Habt auch einen guten Rutsch ins neue Jahr!",{"title":469,"searchDepth":507,"depth":507,"links":62347},[],[1031],"2012-12-21T13:26:51","https://synyx.de/blog/synyx-weihnachtsgedicht/",{},"/blog/synyx-weihnachtsgedicht",{"title":62099,"description":62108},"blog/synyx-weihnachtsgedicht",[43637,389,62356],"weihnachten","Vom weiten Nordpol komm ich her, Ich muss euch sagen es buggte sehr, Mein Navi versagte und auch mein Bord-PC, Mein Schlitten stürzte in einen Haufen Schnee. Gar hart ich…","Lo6fXbP8epgQa2hZRl5qPphQGy6SFXL-Bgb7yJ1WIMs",{"id":62360,"title":62361,"author":62362,"body":62363,"category":62422,"date":62423,"description":62424,"extension":1034,"link":62425,"meta":62426,"navigation":916,"path":62427,"seo":62428,"slug":62367,"stem":62429,"tags":62430,"teaser":62433,"__hash__":62434},"blog/blog/raclette-essen-in-neuen-dimensionen.md","Raclette-Essen in neuen Dimensionen",[172],{"type":432,"value":62364,"toc":62420},[62365,62368,62371,62374,62377,62397,62400,62411,62414,62417],[435,62366,62361],{"id":62367},"raclette-essen-in-neuen-dimensionen",[439,62369,62370],{},"Wow, es ist echt irre, wie viele Leute wir nun bei synyx geworden sind. Wenn ich das mit dem letzten Raclette-Essen\nvergleiche, war es dieses Mal bedeutend mehr Aufwand. Nicht nur, dass Rebecca und ich einiges mehr einzukaufen und somit\nauch einiges mehr zu schleppen hatten. Nein, wir mussten sogar noch einen weiteren Tisch aufbauen. Aber nun mal langsam.\nUm was geht es hier überhaupt?",[439,62372,62373],{},"Also…..",[439,62375,62376],{},"Wie jedes Jahr im Dezember richten wir unseren monatlichen Stammtisch bei synyx im Büro aus und zwar mit einem\ngemütlichen Raclette-Essen. Wieder hatten sich einige fleißige Helferlein in der Küche eingefunden, um das ganze Gemüse\nund das Fleisch zu schnippeln, auf Platten anzurichten – alles was eben dazu gehört.",[439,62378,62379,62388],{},[1002,62380,62383],{"href":62381,"rel":62382},"https://media.synyx.de/uploads//2012/12/helfer.jpg",[1006],[2205,62384],{"alt":62385,"src":62386,"title":62387},"Helferlein","https://media.synyx.de/uploads//2012/12/helfer-300x225.jpg","helfer",[1002,62389,62392],{"href":62390,"rel":62391},"https://media.synyx.de/uploads//2012/12/zutaten.jpg",[1006],[2205,62393],{"alt":62394,"src":62395,"title":62396},"Jede Menge Essen","https://media.synyx.de/uploads//2012/12/zutaten-300x225.jpg","zutaten",[439,62398,62399],{},"Das war schon eine ganze Menge. Als alles fertig war, mussten nur noch die Tische hergerichtet werden. Tja, wir mussten\ndas erste Mal sogar das Admin-Zimmer, welches an die Küche angrenzt, in Beschlag nehmen. Der Platz hätte sonst nicht\nausgereicht. Insgesamt 5(!) Raclette-Geräte wurden benötigt, damit jeder zumindest ein Pfännchen sein Eigen nennen und\nmit allerlei Leckereien füllen konnte. Nachdem alle ihren Platz eingenommen haben, konnte das lustige Brutzeln beginnen.",[439,62401,62402],{},[1002,62403,62406],{"href":62404,"rel":62405},"https://media.synyx.de/uploads//2012/12/raclette_viele.jpg",[1006],[2205,62407],{"alt":62408,"src":62409,"title":62410},"synyx beim Futtern","https://media.synyx.de/uploads//2012/12/raclette_viele-300x225.jpg","raclette_viele",[439,62412,62413],{},"Ich habe mich natürlich an dem Tisch eingefunden, an dem das Motto hieß „mit Bacon geht alles besser“. Ich behaupte\neinfach mal, dass unser Tisch, mit den wenigsten Personen daran, den höchsten Bacon-Verbrauch hatte 😉 Aber natürlich\nwar auch der Rest extrem lecker.",[439,62415,62416],{},"Die Bäuche spannend ging es dann langsam in den gemütlichen Teil über. Ein paar Bierchen trinken, ein paar Brugal mit\nCola und viel Plaudern. Es waren ja auch ein paar Leute anwesend, welche nicht zum aktuellen synyx-Team gehören. Umso\nmehr gab es zu quatschen.",[439,62418,62419],{},"Die Aussagen, wann sich der Stammtisch aufgelöst hat, sind unterschiedlich 😉 Aber das macht ja nichts, denn Alles im\nAllem war der Abend absolut gelungen. Wir freuen uns schon auf das nächste Mal.",{"title":469,"searchDepth":507,"depth":507,"links":62421},[],[1031],"2012-12-14T13:00:01","Wow, es ist echt irre, wie viele Leute wir nun bei synyx geworden sind. Wenn ich das mit dem letzten Raclette-Essen\\nvergleiche, war es dieses Mal bedeutend mehr Aufwand. Nicht nur, dass Rebecca und ich einiges mehr einzukaufen und somit\\nauch einiges mehr zu schleppen hatten. Nein, wir mussten sogar noch einen weiteren Tisch aufbauen. Aber nun mal langsam.\\nUm was geht es hier überhaupt?","https://synyx.de/blog/raclette-essen-in-neuen-dimensionen/",{},"/blog/raclette-essen-in-neuen-dimensionen",{"title":62361,"description":62370},"blog/raclette-essen-in-neuen-dimensionen",[62431,62432,389],"feier","raclette","Wow, es ist echt irre, wie viele Leute wir nun bei synyx geworden sind. Wenn ich das mit dem letzten Raclette-Essen vergleiche, war es dieses Mal bedeutend mehr Aufwand. Nicht…","d4p83ASrj5OJf2LeadswbCmWvkqh-WuK9MiuLxKYXSc",{"id":62436,"title":62437,"author":62438,"body":62439,"category":63086,"date":63087,"description":63088,"extension":1034,"link":63089,"meta":63090,"navigation":916,"path":63091,"seo":63092,"slug":62443,"stem":63093,"tags":63094,"teaser":63102,"__hash__":63103},"blog/blog/android-expandable-listview.md","Android: Expandable ListView",[190],{"type":432,"value":62440,"toc":63084},[62441,62444,62447,62450,62636,62639,62704,62707,62768,62771,62774,62783,62786,62789,62826,62829,62844,62847,62894,62897,62968,62971,63019,63022,63047,63050,63053,63064,63074,63082],[435,62442,62437],{"id":62443},"android-expandable-listview",[439,62445,62446],{},"In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries.\nWith a button at the end of the list, the user can load more entries.",[439,62448,62449],{},"To achieve this goal, we first need to implement a basic Adapter that provides our ListView with the entries:",[464,62451,62453],{"className":709,"code":62452,"language":711,"meta":469,"style":469},"private class ExpandableListAdapter extends BaseAdapter {\n private List entries;\n private Context context;\n public ExpandableListAdapter(List entries) {\n this.entries = entries;\n }\n @Override\n public int getCount() {\n return entries.size();\n }\n @Override\n public Object getItem(int position) {\n if (position >= 0 && position \u003C entries.size())\n return this.entries.get(position);\n return null;\n }\n @Override\n public long getItemId(int position) {\n return position;\n }\n @Override\n public View getView(int position, View convertView, ViewGroup parent) {\n View view = null;\n //reuse the convertView\n if (convertView == null) {\n view = getLayoutInflater().inflate(R.layout.row, null);\n } else {\n view = convertView;\n }\n String entry = entries.get(position);\n view.setTag(position);\n fillView(view, entry);\n return view;\n }\n private void fillView(View view, String entry) {\n TextView text = (TextView) view.findViewById(R.id.text);\n text.setText(entry);\n }\n}\n",[471,62454,62455,62460,62465,62470,62475,62480,62484,62488,62493,62498,62502,62506,62511,62516,62521,62525,62529,62533,62538,62543,62547,62551,62556,62561,62566,62571,62576,62580,62585,62589,62594,62599,62604,62609,62613,62618,62623,62628,62632],{"__ignoreMap":469},[474,62456,62457],{"class":476,"line":477},[474,62458,62459],{},"private class ExpandableListAdapter extends BaseAdapter {\n",[474,62461,62462],{"class":476,"line":507},[474,62463,62464],{}," private List entries;\n",[474,62466,62467],{"class":476,"line":547},[474,62468,62469],{}," private Context context;\n",[474,62471,62472],{"class":476,"line":584},[474,62473,62474],{}," public ExpandableListAdapter(List entries) {\n",[474,62476,62477],{"class":476,"line":607},[474,62478,62479],{}," this.entries = entries;\n",[474,62481,62482],{"class":476,"line":642},[474,62483,1276],{},[474,62485,62486],{"class":476,"line":663},[474,62487,21043],{},[474,62489,62490],{"class":476,"line":694},[474,62491,62492],{}," public int getCount() {\n",[474,62494,62495],{"class":476,"line":700},[474,62496,62497],{}," return entries.size();\n",[474,62499,62500],{"class":476,"line":913},[474,62501,1276],{},[474,62503,62504],{"class":476,"line":920},[474,62505,21043],{},[474,62507,62508],{"class":476,"line":926},[474,62509,62510],{}," public Object getItem(int position) {\n",[474,62512,62513],{"class":476,"line":932},[474,62514,62515],{}," if (position >= 0 && position \u003C entries.size())\n",[474,62517,62518],{"class":476,"line":938},[474,62519,62520],{}," return this.entries.get(position);\n",[474,62522,62523],{"class":476,"line":944},[474,62524,46877],{},[474,62526,62527],{"class":476,"line":950},[474,62528,1276],{},[474,62530,62531],{"class":476,"line":956},[474,62532,21043],{},[474,62534,62535],{"class":476,"line":962},[474,62536,62537],{}," public long getItemId(int position) {\n",[474,62539,62540],{"class":476,"line":4876},[474,62541,62542],{}," return position;\n",[474,62544,62545],{"class":476,"line":4888},[474,62546,1276],{},[474,62548,62549],{"class":476,"line":4900},[474,62550,21043],{},[474,62552,62553],{"class":476,"line":4913},[474,62554,62555],{}," public View getView(int position, View convertView, ViewGroup parent) {\n",[474,62557,62558],{"class":476,"line":4921},[474,62559,62560],{}," View view = null;\n",[474,62562,62563],{"class":476,"line":4932},[474,62564,62565],{}," //reuse the convertView\n",[474,62567,62568],{"class":476,"line":4938},[474,62569,62570],{}," if (convertView == null) {\n",[474,62572,62573],{"class":476,"line":4946},[474,62574,62575],{}," view = getLayoutInflater().inflate(R.layout.row, null);\n",[474,62577,62578],{"class":476,"line":4952},[474,62579,21063],{},[474,62581,62582],{"class":476,"line":4957},[474,62583,62584],{}," view = convertView;\n",[474,62586,62587],{"class":476,"line":4969},[474,62588,5704],{},[474,62590,62591],{"class":476,"line":4990},[474,62592,62593],{}," String entry = entries.get(position);\n",[474,62595,62596],{"class":476,"line":5001},[474,62597,62598],{}," view.setTag(position);\n",[474,62600,62601],{"class":476,"line":5013},[474,62602,62603],{}," fillView(view, entry);\n",[474,62605,62606],{"class":476,"line":5024},[474,62607,62608],{}," return view;\n",[474,62610,62611],{"class":476,"line":5035},[474,62612,1276],{},[474,62614,62615],{"class":476,"line":5047},[474,62616,62617],{}," private void fillView(View view, String entry) {\n",[474,62619,62620],{"class":476,"line":5055},[474,62621,62622],{}," TextView text = (TextView) view.findViewById(R.id.text);\n",[474,62624,62625],{"class":476,"line":5062},[474,62626,62627],{}," text.setText(entry);\n",[474,62629,62630],{"class":476,"line":5067},[474,62631,1276],{},[474,62633,62634],{"class":476,"line":5072},[474,62635,703],{},[439,62637,62638],{},"For this example, I used a simple view for the rows, with just a TextView in it:",[464,62640,62642],{"className":6253,"code":62641,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:orientation=\"vertical\">\n \u003CTextView\n android:id=\"@+id/text\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"30dp\"\n android:gravity=\"center_vertical\"\n android:padding=\"5dp\"/>\n\u003C/LinearLayout>\n",[471,62643,62644,62649,62654,62659,62664,62669,62674,62679,62684,62689,62694,62699],{"__ignoreMap":469},[474,62645,62646],{"class":476,"line":477},[474,62647,62648],{},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n",[474,62650,62651],{"class":476,"line":507},[474,62652,62653],{},"\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n",[474,62655,62656],{"class":476,"line":547},[474,62657,62658],{}," android:layout_width=\"match_parent\"\n",[474,62660,62661],{"class":476,"line":584},[474,62662,62663],{}," android:layout_height=\"match_parent\"\n",[474,62665,62666],{"class":476,"line":607},[474,62667,62668],{}," android:orientation=\"vertical\">\n",[474,62670,62671],{"class":476,"line":642},[474,62672,62673],{}," \u003CTextView\n",[474,62675,62676],{"class":476,"line":663},[474,62677,62678],{}," android:id=\"@+id/text\"\n",[474,62680,62681],{"class":476,"line":694},[474,62682,62683],{}," android:layout_width=\"match_parent\"\n",[474,62685,62686],{"class":476,"line":700},[474,62687,62688],{}," android:layout_height=\"30dp\"\n",[474,62690,62691],{"class":476,"line":913},[474,62692,62693],{}," android:gravity=\"center_vertical\"\n",[474,62695,62696],{"class":476,"line":920},[474,62697,62698],{}," android:padding=\"5dp\"/>\n",[474,62700,62701],{"class":476,"line":926},[474,62702,62703],{},"\u003C/LinearLayout>\n",[439,62705,62706],{},"Then we add the created adapter to a ListView (or a ListActivity like here):",[464,62708,62710],{"className":709,"code":62709,"language":711,"meta":469,"style":469},"public class ExpandableListActivity extends ListActivity {\n@Override\npublic void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n List\u003CString> entries = new ArrayList\u003CString>();\n for (int i = 1; i \u003C= 101; i++) {\n entries.add(\"Entry \" + i);\n }\n setListAdapter(new ExpandableListAdapter(entries, this));\n}\n private class ExpandableListAdapter extends BaseAdapter{....}\n}\n",[471,62711,62712,62717,62721,62726,62731,62736,62741,62746,62750,62755,62759,62764],{"__ignoreMap":469},[474,62713,62714],{"class":476,"line":477},[474,62715,62716],{},"public class ExpandableListActivity extends ListActivity {\n",[474,62718,62719],{"class":476,"line":507},[474,62720,29178],{},[474,62722,62723],{"class":476,"line":547},[474,62724,62725],{},"public void onCreate(Bundle savedInstanceState) {\n",[474,62727,62728],{"class":476,"line":584},[474,62729,62730],{}," super.onCreate(savedInstanceState);\n",[474,62732,62733],{"class":476,"line":607},[474,62734,62735],{}," List\u003CString> entries = new ArrayList\u003CString>();\n",[474,62737,62738],{"class":476,"line":642},[474,62739,62740],{}," for (int i = 1; i \u003C= 101; i++) {\n",[474,62742,62743],{"class":476,"line":663},[474,62744,62745],{}," entries.add(\"Entry \" + i);\n",[474,62747,62748],{"class":476,"line":694},[474,62749,23666],{},[474,62751,62752],{"class":476,"line":700},[474,62753,62754],{}," setListAdapter(new ExpandableListAdapter(entries, this));\n",[474,62756,62757],{"class":476,"line":913},[474,62758,703],{},[474,62760,62761],{"class":476,"line":920},[474,62762,62763],{}," private class ExpandableListAdapter extends BaseAdapter{....}\n",[474,62765,62766],{"class":476,"line":926},[474,62767,703],{},[439,62769,62770],{},"The list as it is now shows all entries, so the next thing to do, is implementing the limiting function to the adapter.",[439,62772,62773],{},"Let’s start by giving our adapter a member variable for the limit:",[464,62775,62777],{"className":709,"code":62776,"language":711,"meta":469,"style":469},"private int limit = 20;\n",[471,62778,62779],{"__ignoreMap":469},[474,62780,62781],{"class":476,"line":477},[474,62782,62776],{},[439,62784,62785],{},"Next we need to modify some of the methods to get this working:",[439,62787,62788],{},"In getCount we need to either return the limit, or the size of the list, if the limit is greater than the list, because\nour list contains all entries and we only want to display a part of it.",[464,62790,62792],{"className":709,"code":62791,"language":711,"meta":469,"style":469},"@Override\npublic int getCount() {\n if (entries.size() \u003C= limit) {\n return entries.size();\n }\n return limit;\n}\n",[471,62793,62794,62798,62803,62808,62813,62817,62822],{"__ignoreMap":469},[474,62795,62796],{"class":476,"line":477},[474,62797,29178],{},[474,62799,62800],{"class":476,"line":507},[474,62801,62802],{},"public int getCount() {\n",[474,62804,62805],{"class":476,"line":547},[474,62806,62807],{}," if (entries.size() \u003C= limit) {\n",[474,62809,62810],{"class":476,"line":584},[474,62811,62812],{}," return entries.size();\n",[474,62814,62815],{"class":476,"line":607},[474,62816,23666],{},[474,62818,62819],{"class":476,"line":642},[474,62820,62821],{}," return limit;\n",[474,62823,62824],{"class":476,"line":663},[474,62825,703],{},[439,62827,62828],{},"Next, we need to adjust the getItem method with an further clause in the if",[464,62830,62832],{"className":709,"code":62831,"language":711,"meta":469,"style":469},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n return entries.get(position);\n",[471,62833,62834,62839],{"__ignoreMap":469},[474,62835,62836],{"class":476,"line":477},[474,62837,62838],{},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n",[474,62840,62841],{"class":476,"line":507},[474,62842,62843],{}," return entries.get(position);\n",[439,62845,62846],{},"Now for the biggest change for our new functionality: the implementation of the ‘more button’.",[464,62848,62850],{"className":709,"code":62849,"language":711,"meta":469,"style":469},"private LinearLayout getMoreView() {\n LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n moreView.setOnClickListener(new View.OnClickListener() {\n public void onClick(View v) {\n listAdapter.increaseLimit();\n }\n });\n return moreView;\n}\n",[471,62851,62852,62857,62862,62867,62872,62877,62881,62885,62890],{"__ignoreMap":469},[474,62853,62854],{"class":476,"line":477},[474,62855,62856],{},"private LinearLayout getMoreView() {\n",[474,62858,62859],{"class":476,"line":507},[474,62860,62861],{}," LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n",[474,62863,62864],{"class":476,"line":547},[474,62865,62866],{}," moreView.setOnClickListener(new View.OnClickListener() {\n",[474,62868,62869],{"class":476,"line":584},[474,62870,62871],{}," public void onClick(View v) {\n",[474,62873,62874],{"class":476,"line":607},[474,62875,62876],{}," listAdapter.increaseLimit();\n",[474,62878,62879],{"class":476,"line":642},[474,62880,5704],{},[474,62882,62883],{"class":476,"line":663},[474,62884,15314],{},[474,62886,62887],{"class":476,"line":694},[474,62888,62889],{}," return moreView;\n",[474,62891,62892],{"class":476,"line":700},[474,62893,703],{},[439,62895,62896],{},"Create another simple xml like this (disregarded i18n for this simple test case. Of course, Strings should normally be\ndeclared in the strings.xml):",[464,62898,62900],{"className":6253,"code":62899,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"wrap_content\"\n android:orientation=\"horizontal\"\n android:paddingLeft=\"20dp\"\n android:paddingRight=\"20dp\">\n \u003CTextView\n android:id=\"@+id/MoreRowText\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"40dp\"\n android:text=\"Load more...\"\n android:gravity=\"center\"/>\n\u003C/LinearLayout>\n",[471,62901,62902,62906,62910,62915,62920,62925,62930,62935,62939,62944,62949,62954,62959,62964],{"__ignoreMap":469},[474,62903,62904],{"class":476,"line":477},[474,62905,62648],{},[474,62907,62908],{"class":476,"line":507},[474,62909,62653],{},[474,62911,62912],{"class":476,"line":547},[474,62913,62914],{}," android:layout_width=\"fill_parent\"\n",[474,62916,62917],{"class":476,"line":584},[474,62918,62919],{}," android:layout_height=\"wrap_content\"\n",[474,62921,62922],{"class":476,"line":607},[474,62923,62924],{}," android:orientation=\"horizontal\"\n",[474,62926,62927],{"class":476,"line":642},[474,62928,62929],{}," android:paddingLeft=\"20dp\"\n",[474,62931,62932],{"class":476,"line":663},[474,62933,62934],{}," android:paddingRight=\"20dp\">\n",[474,62936,62937],{"class":476,"line":694},[474,62938,62673],{},[474,62940,62941],{"class":476,"line":700},[474,62942,62943],{}," android:id=\"@+id/MoreRowText\"\n",[474,62945,62946],{"class":476,"line":913},[474,62947,62948],{}," android:layout_width=\"fill_parent\"\n",[474,62950,62951],{"class":476,"line":920},[474,62952,62953],{}," android:layout_height=\"40dp\"\n",[474,62955,62956],{"class":476,"line":926},[474,62957,62958],{}," android:text=\"Load more...\"\n",[474,62960,62961],{"class":476,"line":932},[474,62962,62963],{}," android:gravity=\"center\"/>\n",[474,62965,62966],{"class":476,"line":938},[474,62967,62703],{},[439,62969,62970],{},"And add a method to the adapter to increase the limit:",[464,62972,62974],{"className":709,"code":62973,"language":711,"meta":469,"style":469}," public void increaseLimit() {\n limit+=20;\n //remove the button if we can no longer expand the list\n if (limit >= entries.size()) {\n getListView().removeFooterView(moreView);\n }\n //notify to redraw the list\n notifyDataSetChanged();\n }\n",[471,62975,62976,62981,62986,62991,62996,63001,63005,63010,63015],{"__ignoreMap":469},[474,62977,62978],{"class":476,"line":477},[474,62979,62980],{}," public void increaseLimit() {\n",[474,62982,62983],{"class":476,"line":507},[474,62984,62985],{}," limit+=20;\n",[474,62987,62988],{"class":476,"line":547},[474,62989,62990],{}," //remove the button if we can no longer expand the list\n",[474,62992,62993],{"class":476,"line":584},[474,62994,62995],{}," if (limit >= entries.size()) {\n",[474,62997,62998],{"class":476,"line":607},[474,62999,63000],{}," getListView().removeFooterView(moreView);\n",[474,63002,63003],{"class":476,"line":642},[474,63004,23666],{},[474,63006,63007],{"class":476,"line":663},[474,63008,63009],{}," //notify to redraw the list\n",[474,63011,63012],{"class":476,"line":694},[474,63013,63014],{}," notifyDataSetChanged();\n",[474,63016,63017],{"class":476,"line":700},[474,63018,23666],{},[439,63020,63021],{},"At last, we simply add the button as a footer view to our list (After creation of the ListView, but before setting the\nListAdapter to the ListView):",[464,63023,63025],{"className":709,"code":63024,"language":711,"meta":469,"style":469},"moreView = getMoreView();\nlistAdapter = new ExpandableListAdapter(entries);\ngetListView().addFooterView(moreView);\nsetListAdapter(listAdapter);\n",[471,63026,63027,63032,63037,63042],{"__ignoreMap":469},[474,63028,63029],{"class":476,"line":477},[474,63030,63031],{},"moreView = getMoreView();\n",[474,63033,63034],{"class":476,"line":507},[474,63035,63036],{},"listAdapter = new ExpandableListAdapter(entries);\n",[474,63038,63039],{"class":476,"line":547},[474,63040,63041],{},"getListView().addFooterView(moreView);\n",[474,63043,63044],{"class":476,"line":584},[474,63045,63046],{},"setListAdapter(listAdapter);\n",[439,63048,63049],{},"And that’s it for the simple case of a static list 🙂",[439,63051,63052],{},"If you want to do this a bit more dynamic or you don’t want to load whole database table (or the whole internet) into\nyour list, you have to do some extra work.",[8310,63054,63055,63058,63061],{},[997,63056,63057],{},"Load the next X entries with an AsyncTask (display a ProgressDialog while it is getting the data!) and add a method\nto your adapter, that adds this entries to the list. Call this method from within the onPostExecute method of the\nAsyncTask",[997,63059,63060],{},"You may want to keep track of the total number of entries you have to know when the user can’t load any more entries\nand to remove the button. (If the dataset is not bound to grow every few seconds. Otherwise, maybe let the user try\nto hit the button and see for himself, if theres new data)",[997,63062,63063],{},"Keep in mind to notify your users if anything fails, like the next entries could not have been loaded, because they\nhave no internet connection.",[439,63065,63066,63067,63073],{},"A Little homework for you: Combine it with\nthe ",[1002,63068,63072],{"href":63069,"rel":63070,"title":63071},"http://blog.synyx.de/2011/11/android-listview-with-rounded-corners/",[1006],"android listview with rounded corners","rounded corners example","\nto make it look better 😛",[439,63075,63076,63077],{},"Here’s the source, btw:",[1002,63078,63081],{"href":63079,"rel":63080},"https://media.synyx.de/uploads//2012/12/ExpandableListview.zip",[1006],"ExpandableListview",[1024,63083,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":63085},[],[11122,1413],"2012-12-07T09:39:26","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries.\\nWith a button at the end of the list, the user can load more entries.","https://synyx.de/blog/android-expandable-listview/",{},"/blog/android-expandable-listview",{"title":62437,"description":62446},"blog/android-expandable-listview",[11132,63095,63096,63097,63098,63099,63100,63101],"entries","entry","expandable","limit","limited","list","listview","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries. With a button at the end of the list,…","2cPxf8ntm24639cXpvDd1uvgjk37n-ulC-vUEA0BPZw",{"id":63105,"title":63106,"author":63107,"body":63108,"category":63525,"date":63526,"description":63527,"extension":1034,"link":63528,"meta":63529,"navigation":916,"path":63530,"seo":63531,"slug":63112,"stem":63533,"tags":63534,"teaser":63535,"__hash__":63536},"blog/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar-part-2.md","Visualize JavaScript code quality and code coverage with Sonar – part 2",[335],{"type":432,"value":63109,"toc":63521},[63110,63113,63123,63126,63132,63136,63139,63148,63151,63160,63163,63218,63221,63224,63227,63373,63376,63411,63414,63418,63421,63519],[435,63111,63106],{"id":63112},"visualize-javascript-code-quality-and-code-coverage-with-sonar-part-2",[439,63114,63115,63116,63122],{},"In\nmy ",[1002,63117,63121],{"href":63118,"rel":63119,"title":63120},"http://blog.synyx.de/2012/08/visualize-javascript-code-quality-and-code-coverage-with-sonar/",[1006],"Visualize JavaScript code quality and code coverage with Sonar","previous post","\nI wrote about the Sonar JavaScript-Plugin, JsTestDriver, jstd-maven-plugin and some problems with the configuration.\nMeanwhile we’ve got a working setup which I want to explain in this blog.",[439,63124,63125],{},"For the impatient ones among us, there is a sample project available on github:",[439,63127,63128],{},[1002,63129,63130],{"href":63130,"rel":63131},"https://github.com/synyx/JavaJsSonarDemo",[1006],[3938,63133,63135],{"id":63134},"run-javascript-tests-wit-jstestdriver","Run JavaScript-Tests wit JsTestDriver",[439,63137,63138],{},"The first problem was, that the opened browser-tab by the jstd runner was not closed anymore. In the previous setup\njstd was configured to start the test server during the maven test goal. Now we have a jstd server instance running on a\nremote computer with already captured browsers. Therefore you just have to execute",[464,63140,63142],{"className":16895,"code":63141,"language":16897,"meta":469,"style":469},"java -jar JsTestDriver.jar --port 9876\n",[471,63143,63144],{"__ignoreMap":469},[474,63145,63146],{"class":476,"line":477},[474,63147,63141],{},[439,63149,63150],{},"in the terminal, start the browsers of your choice and load the url",[464,63152,63154],{"className":16895,"code":63153,"language":16897,"meta":469,"style":469},"http://localhost:9876/capture\n",[471,63155,63156],{"__ignoreMap":469},[474,63157,63158],{"class":476,"line":477},[474,63159,63153],{},[439,63161,63162],{},"Now the browsers will listen to the jstd server to run the tests. No more tabs will be opened which we would have to\nclose manually. This works pretty well, as long as you use the JsTestDriver.jar to run your tests from the terminal. But\nif you use jstd-maven-plugin you will get following error:",[464,63164,63166],{"className":16895,"code":63165,"language":16897,"meta":469,"style":469},"java.lang.RuntimeException: Connection error on : sun.net.www.protocol.http.HttpURLConnection:http://10.0.15.24:9876/fileSet\n at com.google.jstestdriver.HttpServer.post(HttpServer.java:96)\n at com.google.jstestdriver.FileUploader.determineServerFileSet(FileUploader.java:174)\n at com.google.jstestdriver.action.UploadAction.run(UploadAction.java:38)\n at com.google.jstestdriver.ActionRunner.runActions(ActionRunner.java:64)\n at com.google.jstestdriver.JsTestDriver.main(JsTestDriver.java:86)\nCaused by: java.io.IOException: Server returned HTTP response code: 405 for URL: http://10.0.15.24:9876/fileSet at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1403)\n at com.google.jstestdriver.HttpServer.post(HttpServer.java:92)\n ... 4 more\nUnexpected Runner Condition: Connection error on: sun.net.www.protocol.http.HttpURLConnection:http://10.0.15.24:9876/fileSet\n",[471,63167,63168,63173,63178,63183,63188,63193,63198,63203,63208,63213],{"__ignoreMap":469},[474,63169,63170],{"class":476,"line":477},[474,63171,63172],{},"java.lang.RuntimeException: Connection error on : sun.net.www.protocol.http.HttpURLConnection:http://10.0.15.24:9876/fileSet\n",[474,63174,63175],{"class":476,"line":507},[474,63176,63177],{}," at com.google.jstestdriver.HttpServer.post(HttpServer.java:96)\n",[474,63179,63180],{"class":476,"line":547},[474,63181,63182],{}," at com.google.jstestdriver.FileUploader.determineServerFileSet(FileUploader.java:174)\n",[474,63184,63185],{"class":476,"line":584},[474,63186,63187],{}," at com.google.jstestdriver.action.UploadAction.run(UploadAction.java:38)\n",[474,63189,63190],{"class":476,"line":607},[474,63191,63192],{}," at com.google.jstestdriver.ActionRunner.runActions(ActionRunner.java:64)\n",[474,63194,63195],{"class":476,"line":642},[474,63196,63197],{}," at com.google.jstestdriver.JsTestDriver.main(JsTestDriver.java:86)\n",[474,63199,63200],{"class":476,"line":663},[474,63201,63202],{},"Caused by: java.io.IOException: Server returned HTTP response code: 405 for URL: http://10.0.15.24:9876/fileSet at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1403)\n",[474,63204,63205],{"class":476,"line":694},[474,63206,63207],{}," at com.google.jstestdriver.HttpServer.post(HttpServer.java:92)\n",[474,63209,63210],{"class":476,"line":700},[474,63211,63212],{}," ... 4 more\n",[474,63214,63215],{"class":476,"line":913},[474,63216,63217],{},"Unexpected Runner Condition: Connection error on: sun.net.www.protocol.http.HttpURLConnection:http://10.0.15.24:9876/fileSet\n",[439,63219,63220],{},"The problem is that jstd-maven-plugin uses an older version of jstd. With the current version 1.3.5 everything works\nas expected. But how can we tell jstd-maven-plugin to use the new version of jstd?",[439,63222,63223],{},"Well, you can simply add a property \u003Cjstd.jar> to your pom.xml properties and point to the jar file.",[439,63225,63226],{},"Since jstd-maven-plugin has a dependeny to jstd 1.3.2 this old jar will still be in our classpath. So we have to\ntell jstd-maven-plugin to use jstd 1.3.5 instead of the outdated one.",[464,63228,63230],{"className":16895,"code":63229,"language":16897,"meta":469,"style":469},"\u003Cdependencies>\n ...\n \u003Cdependency>\n \u003CgroupId>com.google.jstestdriver\u003C/groupId>\n \u003CartifactId>jstestdriver\u003C/artifactId>\n \u003Cversion>1.3.5\u003C/version>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n ...\n\u003C/dependencies>\n\u003Cbuild>\n ...\n \u003CpluginManagement>\n \u003Cplugins>\n \u003Cplugin>\n \u003CgroupId>com.googlecode.jstd-maven-plugin\u003C/groupId>\n \u003CartifactId>jstd-maven-plugin\u003C/artifactId>\n \u003Cversion>1.3.2.5\u003C/version>\n \u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>com.google.jstestdriver\u003C/groupId>\n \u003CartifactId>jstestdriver\u003C/artifactId>\n \u003Cversion>1.3.5\u003C/version>\n \u003C/dependency>\n \u003C/dependencies>\n \u003C/plugin>\n \u003C/plugins>\n \u003C/pluginManagement>\n ...\n\u003C/build>\n",[471,63231,63232,63236,63240,63245,63250,63255,63260,63265,63270,63274,63278,63282,63286,63291,63296,63301,63306,63311,63316,63320,63325,63330,63335,63340,63345,63350,63355,63360,63365,63369],{"__ignoreMap":469},[474,63233,63234],{"class":476,"line":477},[474,63235,6262],{},[474,63237,63238],{"class":476,"line":507},[474,63239,697],{},[474,63241,63242],{"class":476,"line":547},[474,63243,63244],{}," \u003Cdependency>\n",[474,63246,63247],{"class":476,"line":584},[474,63248,63249],{}," \u003CgroupId>com.google.jstestdriver\u003C/groupId>\n",[474,63251,63252],{"class":476,"line":607},[474,63253,63254],{}," \u003CartifactId>jstestdriver\u003C/artifactId>\n",[474,63256,63257],{"class":476,"line":642},[474,63258,63259],{}," \u003Cversion>1.3.5\u003C/version>\n",[474,63261,63262],{"class":476,"line":663},[474,63263,63264],{}," \u003Cscope>test\u003C/scope>\n",[474,63266,63267],{"class":476,"line":694},[474,63268,63269],{}," \u003C/dependency>\n",[474,63271,63272],{"class":476,"line":700},[474,63273,697],{},[474,63275,63276],{"class":476,"line":913},[474,63277,6386],{},[474,63279,63280],{"class":476,"line":920},[474,63281,54015],{},[474,63283,63284],{"class":476,"line":926},[474,63285,697],{},[474,63287,63288],{"class":476,"line":932},[474,63289,63290],{}," \u003CpluginManagement>\n",[474,63292,63293],{"class":476,"line":938},[474,63294,63295],{}," \u003Cplugins>\n",[474,63297,63298],{"class":476,"line":944},[474,63299,63300],{}," \u003Cplugin>\n",[474,63302,63303],{"class":476,"line":950},[474,63304,63305],{}," \u003CgroupId>com.googlecode.jstd-maven-plugin\u003C/groupId>\n",[474,63307,63308],{"class":476,"line":956},[474,63309,63310],{}," \u003CartifactId>jstd-maven-plugin\u003C/artifactId>\n",[474,63312,63313],{"class":476,"line":962},[474,63314,63315],{}," \u003Cversion>1.3.2.5\u003C/version>\n",[474,63317,63318],{"class":476,"line":4876},[474,63319,50155],{},[474,63321,63322],{"class":476,"line":4888},[474,63323,63324],{}," \u003Cdependency>\n",[474,63326,63327],{"class":476,"line":4900},[474,63328,63329],{}," \u003CgroupId>com.google.jstestdriver\u003C/groupId>\n",[474,63331,63332],{"class":476,"line":4913},[474,63333,63334],{}," \u003CartifactId>jstestdriver\u003C/artifactId>\n",[474,63336,63337],{"class":476,"line":4921},[474,63338,63339],{}," \u003Cversion>1.3.5\u003C/version>\n",[474,63341,63342],{"class":476,"line":4932},[474,63343,63344],{}," \u003C/dependency>\n",[474,63346,63347],{"class":476,"line":4938},[474,63348,63349],{}," \u003C/dependencies>\n",[474,63351,63352],{"class":476,"line":4946},[474,63353,63354],{}," \u003C/plugin>\n",[474,63356,63357],{"class":476,"line":4952},[474,63358,63359],{}," \u003C/plugins>\n",[474,63361,63362],{"class":476,"line":4957},[474,63363,63364],{}," \u003C/pluginManagement>\n",[474,63366,63367],{"class":476,"line":4969},[474,63368,697],{},[474,63370,63371],{"class":476,"line":4990},[474,63372,54188],{},[439,63374,63375],{},"If you try to build the maven project now, you will notice that jstd 1.3.5 can’t be found in the maven central\nrepository, unfortunately. So you have to add the jar to your local maven repo or to your nexus. To add it to your local\nrepo execute this command in the terminal:",[464,63377,63379],{"className":16895,"code":63378,"language":16897,"meta":469,"style":469},"mvn install:install-file \\\n -Dfile=\u003Cpath.to.jstd.jar> \\\n -DgroupId=com.google.jstestdriver \\\n -DartifactId=jstestdriver \\\n -Dversion=1.3.5 \\\n -Dpackaging=jar\n",[471,63380,63381,63386,63391,63396,63401,63406],{"__ignoreMap":469},[474,63382,63383],{"class":476,"line":477},[474,63384,63385],{},"mvn install:install-file \\\n",[474,63387,63388],{"class":476,"line":507},[474,63389,63390],{}," -Dfile=\u003Cpath.to.jstd.jar> \\\n",[474,63392,63393],{"class":476,"line":547},[474,63394,63395],{}," -DgroupId=com.google.jstestdriver \\\n",[474,63397,63398],{"class":476,"line":584},[474,63399,63400],{}," -DartifactId=jstestdriver \\\n",[474,63402,63403],{"class":476,"line":607},[474,63404,63405],{}," -Dversion=1.3.5 \\\n",[474,63407,63408],{"class":476,"line":642},[474,63409,63410],{}," -Dpackaging=jar\n",[439,63412,63413],{},"Now the JavaScript-Tests are automatically run by JsTestDriver during the maven build.",[3938,63415,63417],{"id":63416},"analyse-java-and-javascript-sources-with-sonar","Analyse Java and JavaScript sources with Sonar",[439,63419,63420],{},"The second problem I mentioned in my previous post was the specification of the sourceDirectory for Sonar to be able to\nfetch the JavaScript sources. Hereby the java source directory will be overridden, of course, and the maven build will\nfail. I solved this with a maven profile which sets the source directory, the language that sonar should analyse and a\nbranch name for the JavaScript analysis (otherwise the Java analysis will be overridden). Furthermore you have to tell\nmaven to skip the java tests since the sources cannot be found anymore due the sourceDirectory change.",[464,63422,63424],{"className":16895,"code":63423,"language":16897,"meta":469,"style":469},"\u003Cproperties>\n ...\n \u003CsrcDir>src/main/java\u003C/srcDir>\n ...\n\u003C/properties>\n\u003Cbuild>\n ...\n \u003CsourceDirectory>${srcDir}\u003C/sourceDirectory>\n ...\n\u003C/build>\n\u003Cprofiles>\n \u003Cprofile>\n \u003Cid>sonarJsEnabled\u003C/id>\n \u003Cproperties>\n \u003CsrcDir>src/main/webapp/js\u003C/srcDir>\n \u003Cmaven.test.skip>true\u003C/maven.test.skip>\n \u003Csonar.language>js\u003C/sonar.language>\n \u003Csonar.branch>js\u003C/sonar.branch>\n \u003C/properties>\n \u003C/profile>\n\u003C/profiles>\n",[471,63425,63426,63431,63435,63440,63444,63449,63453,63457,63462,63466,63470,63474,63478,63483,63487,63492,63497,63502,63507,63511,63515],{"__ignoreMap":469},[474,63427,63428],{"class":476,"line":477},[474,63429,63430],{},"\u003Cproperties>\n",[474,63432,63433],{"class":476,"line":507},[474,63434,697],{},[474,63436,63437],{"class":476,"line":547},[474,63438,63439],{}," \u003CsrcDir>src/main/java\u003C/srcDir>\n",[474,63441,63442],{"class":476,"line":584},[474,63443,697],{},[474,63445,63446],{"class":476,"line":607},[474,63447,63448],{},"\u003C/properties>\n",[474,63450,63451],{"class":476,"line":642},[474,63452,54015],{},[474,63454,63455],{"class":476,"line":663},[474,63456,697],{},[474,63458,63459],{"class":476,"line":694},[474,63460,63461],{}," \u003CsourceDirectory>${srcDir}\u003C/sourceDirectory>\n",[474,63463,63464],{"class":476,"line":700},[474,63465,697],{},[474,63467,63468],{"class":476,"line":913},[474,63469,54188],{},[474,63471,63472],{"class":476,"line":920},[474,63473,22290],{},[474,63475,63476],{"class":476,"line":926},[474,63477,54412],{},[474,63479,63480],{"class":476,"line":932},[474,63481,63482],{}," \u003Cid>sonarJsEnabled\u003C/id>\n",[474,63484,63485],{"class":476,"line":938},[474,63486,50130],{},[474,63488,63489],{"class":476,"line":944},[474,63490,63491],{}," \u003CsrcDir>src/main/webapp/js\u003C/srcDir>\n",[474,63493,63494],{"class":476,"line":950},[474,63495,63496],{}," \u003Cmaven.test.skip>true\u003C/maven.test.skip>\n",[474,63498,63499],{"class":476,"line":956},[474,63500,63501],{}," \u003Csonar.language>js\u003C/sonar.language>\n",[474,63503,63504],{"class":476,"line":962},[474,63505,63506],{}," \u003Csonar.branch>js\u003C/sonar.branch>\n",[474,63508,63509],{"class":476,"line":4876},[474,63510,50145],{},[474,63512,63513],{"class":476,"line":4888},[474,63514,54470],{},[474,63516,63517],{"class":476,"line":4900},[474,63518,22457],{},[1024,63520,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":63522},[63523,63524],{"id":63134,"depth":507,"text":63135},{"id":63416,"depth":507,"text":63417},[1030],"2012-11-22T17:00:57","In\\nmy previous post\\nI wrote about the Sonar JavaScript-Plugin, JsTestDriver, jstd-maven-plugin and some problems with the configuration.\\nMeanwhile we’ve got a working setup which I want to explain in this blog.","https://synyx.de/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar-part-2/",{},"/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar-part-2",{"title":63106,"description":63532},"In\nmy previous post\nI wrote about the Sonar JavaScript-Plugin, JsTestDriver, jstd-maven-plugin and some problems with the configuration.\nMeanwhile we’ve got a working setup which I want to explain in this blog.","blog/visualize-javascript-code-quality-and-code-coverage-with-sonar-part-2",[],"In my previous post I wrote about the Sonar JavaScript-Plugin, JsTestDriver, jstd-maven-plugin and some problems with the configuration. Meanwhile we’ve got a working setup which I want to explain in…","dZPmT4RqM5AjLvjCHcsD4nNol7MHvdL4Dy92jpUsG_g",{"id":63538,"title":63539,"author":63540,"body":63541,"category":63710,"date":63711,"description":63712,"extension":1034,"link":63713,"meta":63714,"navigation":916,"path":63715,"seo":63716,"slug":63545,"stem":63718,"tags":63719,"teaser":63720,"__hash__":63721},"blog/blog/urlaubsverwaltung-was-hat-sich-getan.md","Urlaubsverwaltung – was hat sich getan?",[30],{"type":432,"value":63542,"toc":63708},[63543,63546,63555,63560,63563,63568,63577,63582,63585,63592,63595,63600,63610,63615,63625,63628,63633,63643,63648,63658,63667,63672,63675,63678,63681,63688,63691,63694,63697,63702,63705],[435,63544,63539],{"id":63545},"urlaubsverwaltung-was-hat-sich-getan",[439,63547,63548,63549,63554],{},"Lang, lang ist es her seit dem\nersten ",[1002,63550,63553],{"href":41818,"rel":63551,"title":63552},[1006],"Blog Urlaubsverwaltung","Blog Post","\nüber die synyx’sche Urlaubsverwaltung.",[439,63556,63557],{},[448,63558,63559],{},"Und inzwischen?",[439,63561,63562],{},"Es hat sich einiges getan. Seit März dieses Jahres wird die Urlaubsverwaltung produktiv eingesetzt. In der Testphase war\ndas Prozedere zunächst einmal immer auf doppeltem Wege Urlaub zu beantragen – also sowohl schriftlich, als auch\nelektronisch. Inzwischen sind die schriftlichen Anträge glücklicherweise verschwunden, die Beantragung und Genehmigung\nvon Urlaub erfolgt nun ausschließlich elektronisch.",[439,63564,63565],{},[990,63566,63567],{},"Elektronischer Urlaubsantrag",[439,63569,63570],{},[1002,63571,63574],{"href":63572,"rel":63573},"https://media.synyx.de/uploads//2012/11/Urlaubsantrag.png",[1006],[2205,63575],{"alt":469,"src":63576,"title":41680},"https://media.synyx.de/uploads//2012/11/Urlaubsantrag-300x239.png",[439,63578,63579],{},[448,63580,63581],{},"Was ist sonst so passiert?",[439,63583,63584],{},"Durch das sehr umständliche Formular zum Editieren von Mitarbeitern und deren Urlaubsanspruch kam es leider zu\nMissverständnissen und somit immer wieder zu Verwirrungen bei den Mitarbeitern bzgl. ihres tatsächlichen\nUrlaubsanspruchs. In einem einwöchigen Sprint im September beschloss ich also, ein größeres Refactoring vorzunehmen, um\nsolche Probleme ein für alle Mal aus der Welt der Urlaubsverwaltung zu schaffen.",[439,63586,63587,63588,63591],{},"Mitarbeiter haben nun keine Urlaubskonten mehr, sondern nur noch Urlaubsanspruch pro Jahr. (vgl. ER-Diagramm im\nalten ",[1002,63589,63553],{"href":41818,"rel":63590,"title":63552},[1006],")\nWieviele Urlaubstage ein Mitarbeiter noch besitzt und ob er einen weiteren Urlaubsantrag stellen darf, wird nun quasi on\nthe fly berechnet – anhand Urlaubsanspruch pro Jahr und Anzahl der bereits genutzten Urlaubstage dieses Jahres. Diese\nInformation wird nicht mehr als Urlaubskonto in der Datenbank vorgehalten. Dies hat den Vorteil, dass der Code sehr\nvereinfacht wird, da bspw. keine Rollback-Methoden mehr notwendig sind.",[439,63593,63594],{},"Das Formular zum Editieren von Mitarbeitern hat durch das Refactoring sogar ein Zusatzfeature gewonnen. Urlaubsanspruch\nvergibt man nun nicht mehr für ein Jahr, sondern für einen Zeitraum. Dies hat den Vorteil, dass der tatsächliche\nUrlaubsanspruch für neue Mitarbeiter nicht mehr manuell berechnet werden muss, sondern durch die Anwendung erfolgt. Man\ngibt nur noch den Urlaubsanspruch für ein Jahr an (z.B. 28 Tage) und den Zeitraum (z.B. 01.10.2012 – 31.12.2012)\nund erhält dann den tatsächlichen Urlaubsanspruch. (in diesem Beispiel 7 Tage)",[439,63596,63597],{},[990,63598,63599],{},"Editieren eines Mitarbeiters (vor dem Refactoring)",[439,63601,63602],{},[1002,63603,63606],{"href":63604,"rel":63605},"https://media.synyx.de/uploads//2012/11/EditMitarbeiterLegacy.png",[1006],[2205,63607],{"alt":469,"src":63608,"title":63609},"https://media.synyx.de/uploads//2012/11/EditMitarbeiterLegacy-300x240.png","EditMitarbeiterLegacy",[439,63611,63612],{},[990,63613,63614],{},"Editieren eines Mitarbeiters (nach dem Refactoring)",[439,63616,63617],{},[1002,63618,63621],{"href":63619,"rel":63620},"https://media.synyx.de/uploads//2012/11/EditMitarbeiter.png",[1006],[2205,63622],{"alt":469,"src":63623,"title":63624},"https://media.synyx.de/uploads//2012/11/EditMitarbeiter-300x239.png","EditMitarbeiter",[439,63626,63627],{},"Auch die Detailansicht für den einzelnen Mitarbeiter hat sich verändert.",[439,63629,63630],{},[990,63631,63632],{},"Persönliche Übersicht (vor dem Refactoring)",[439,63634,63635],{},[1002,63636,63639],{"href":63637,"rel":63638},"https://media.synyx.de/uploads//2012/11/DetailansichtLegacy.png",[1006],[2205,63640],{"alt":469,"src":63641,"title":63642},"https://media.synyx.de/uploads//2012/11/DetailansichtLegacy-300x240.png","DetailansichtLegacy",[439,63644,63645],{},[990,63646,63647],{},"Persönliche Übersicht (nach dem Refactoring)",[439,63649,63650],{},[1002,63651,63654],{"href":63652,"rel":63653},"https://media.synyx.de/uploads//2012/11/Detailansicht.png",[1006],[2205,63655],{"alt":469,"src":63656,"title":63657},"https://media.synyx.de/uploads//2012/11/Detailansicht-300x239.png","Detailansicht",[439,63659,63660,63661,63666],{},"Wie man vielleicht erkennen kann, habe ich im Zuge des Refactorings ",[1002,63662,63665],{"href":63663,"rel":63664,"title":63665},"https://getbootstrap.com/",[1006],"Bootstrap","\neingebunden. Dieses Front-End Framework habe ich in einem Kundenprojekt kennenlernen dürfen und Gefallen daran\ngefunden.",[439,63668,63669],{},[448,63670,63671],{},"Was soll in naher Zukunft geschehen?",[439,63673,63674],{},"Neben diversen Bugfixes stehen noch einige wichtige Features aus.",[439,63676,63677],{},"Sehr wichtig ist es, endlich die Anbindung zum Google-Kalender zu realisieren, damit die Urlaubstage von Mitarbeitern\nnicht mehr händisch im synyx Kalender ein- bzw. ausgetragen werden müssen, sondern dies automatisch über die Anwendung\nerfolgt, sobald ein Urlaubsantrag genehmigt bzw. storniert wurde. Dieses Feature birgt nicht nur den Vorteil, dass\nArbeitszeit gespart wird, sondern auch dass Eintragungsfehler vermieden werden.",[439,63679,63680],{},"Bisher immer wieder nach hinten geschoben, nähert sich nun doch rasant die Notwendigkeit der Berechnung von\nResturlaubstagen zum Jahreswechsel. Per Cronjob soll zum 1. Januar eines jeden Jahres eine Methode ausgeführt werden,\ndie berechnet, wieviele Resturlaubstage ein Mitarbeiter ins neue Jahr mitnimmt.",[439,63682,63683,63684,63687],{},"Das Drucken von Urlaubsanträgen ist momentan realisiert durch ein simples ",[471,63685,63686],{},"window.print();"," und einigen Zeilen CSS für\ndie angepasste Druckausgabe. Für die Zukunft soll allerdings eine PDF-Generierung gerade unserer Office-Managerin\nRebecca das Leben erleichtern. Liegt ein neuer genehmigter Urlaubsantrag vor, erhält sie eine Email mit dem Link zum\nAntrag, um diesen drucken zu können. Dieser Schritt erübrigt sich, sobald die Möglichkeit besteht, Urlaubsanträge als\nPDF zu erzeugen und dieses PDF in eben jene Informations-Email anzuhängen.",[439,63689,63690],{},"Momentan wird davon ausgegangen, dass jeder Mitarbeiter fünf Tage die Woche arbeitet, nämlich Montag bis Freitag. Bei\nder Berechnung wieviele Urlaubstage für einen angegebenen Zeitraum abgezogen werden müssen, werden immer Samstag und\nSonntag als Nicht-Werktage gewertet.",[439,63692,63693],{},"Es soll jedoch irgendwann möglich sein, für jeden Mitarbeiter anzugeben, welche Tage Nicht-Werktage sind. Dies ist\nbspw. für unsere Kochmuddi relevant, die eben nicht fünf Tage die Woche arbeitet und dafür aber samstags.",[439,63695,63696],{},"Bisher hat ausschließlich Rebecca die Office-Rolle in der Urlaubsverwaltung inne. Die Urlaubsverwaltung soll jedoch in\nnaher Zukunft eine User-/Rechteverwaltung erhalten, damit z.B. Rebeccas Urlaubsvertretung die Office-Rolle übernehmen\nkann. Gleiches gilt für die Chef-Rolle zur Genehmigung bzw. Ablehnung von Urlaubsanträgen. Eventuell sollen die hierfür\nzuständigen Personen auch flexibel einstellbar sein.",[439,63698,63699],{},[448,63700,63701],{},"Wohin soll die Urlaubsverwaltung langfristig gehen?",[439,63703,63704],{},"Langfristig steht die Idee im Raum, die Urlaubsverwaltung als allgemeine Mitarbeiterverwaltung auszubauen. Eine konkrete\nmögliche Erweiterung wäre die Verwaltung von Krankheitstagen. Dies würde eine bessere Übersicht und leichtere Verwaltung\nfür das Office bedeuten, statt wie bisher nur über den Google-Kalender.",[439,63706,63707],{},"Doch das ist vorerst noch Zukunftsmusik…",{"title":469,"searchDepth":507,"depth":507,"links":63709},[],[13208],"2012-11-20T16:58:54","Lang, lang ist es her seit dem\\nersten Blog Post\\nüber die synyx’sche Urlaubsverwaltung.","https://synyx.de/blog/urlaubsverwaltung-was-hat-sich-getan/",{},"/blog/urlaubsverwaltung-was-hat-sich-getan",{"title":63539,"description":63717},"Lang, lang ist es her seit dem\nersten Blog Post\nüber die synyx’sche Urlaubsverwaltung.","blog/urlaubsverwaltung-was-hat-sich-getan",[389,26637,41847,26638],"Lang, lang ist es her seit dem ersten Blog Post über die synyx’sche Urlaubsverwaltung. Und inzwischen? Es hat sich einiges getan. Seit März dieses Jahres wird die Urlaubsverwaltung produktiv eingesetzt.…","0BL5XxmMxGb3bTNrOnevdFAZOUdhWKUyk6-kCnUpoKU",{"id":63723,"title":63724,"author":63725,"body":63726,"category":63811,"date":63812,"description":63813,"extension":1034,"link":63814,"meta":63815,"navigation":916,"path":63816,"seo":63817,"slug":63730,"stem":63819,"tags":63820,"teaser":63823,"__hash__":63824},"blog/blog/professional-scrum-master-schulung.md","Professional Scrum Master Schulung",[83],{"type":432,"value":63727,"toc":63808},[63728,63731,63740,63747,63750,63760,63763,63787,63790,63800,63803,63805],[435,63729,63724],{"id":63730},"professional-scrum-master-schulung",[439,63732,63733,63734,63739],{},"„Scrum ist ein Framework zur nachhaltigen Entwicklung komplexer Produkte“ (\nQuelle: ",[1002,63735,63738],{"href":63736,"rel":63737},"http://www.scrum.org/Scrum-Guides",[1006],"Scrum Guide",") und wird bei synyx nun schon seit einigen Jahren erfolgreich\neingesetzt. Bisher gab es in unserem Unternehmen mit Fabian Buch nur einen zertifizierten Scrum Master, der auch für die\nEinführung des Frameworks und Umsetzung der damit verbundenen Prinzipien im Unternehmen verantwortlich war. Durch das\nEntstehen von mehr und mehr Teams in denen Scrum eingesetzt wurde, wurden auch mehr Scrum Master für diese Teams\nbenötigt. Mein Kollege Michael Herbold und ich wurden von Fabian Buch in den Grundlagen von Scrum unterrichtet und\narbeiten nun schon seit einiger Zeit neben unserer Entwicklertätigkeit auch als Scrum Master in unterschiedlichen Teams.",[439,63741,63742,63743,63746],{},"Um unsere Grundlagenkenntnisse in Scrum zu vertiefen und natürlich auch um die Professional Scrum Master (PSM)\nZertifizierung zu machen, entschieden wir uns, den PSM Kurs mit Fahd Al-Fatish zu besuchen. Der Kurs wurde von\nder ",[1002,63744,38502],{"href":38500,"rel":63745},[1006]," veranstaltet und fand am 8. und 9. November in Frankfurt (Main)\nstatt. Da der Kurs in einem Hotel direkt am Bahnhof statt fand und es zudem eine sehr passende Verbindung gab, konnten\nwir ohne Probleme mit dem Zug pendeln.",[439,63748,63749],{},"Inhaltlich war der Kurs gut strukturiert. Als erste Grundlage sprachen wir über Agilität: wie sie definiert ist, was sie\nfür ein Unternehmen bedeutet und warum es wünschenswert ist, sie zu erreichen. Anschließend wurden die vier\ngrundlegenden Prinzipien von Scrum vorgestellt und diskutiert, wie durch die Einhaltung dieser Prinzipien Agilität\nerreicht werden kann.",[439,63751,63752],{},[1002,63753,63756],{"href":63754,"rel":63755},"https://media.synyx.de/uploads//2012/11/20121109_1731021.jpg",[1006],[2205,63757],{"alt":469,"src":63758,"title":63759},"https://media.synyx.de/uploads//2012/11/20121109_1731021-e1353320226448-225x300.jpg","Scrum Prinzipien",[439,63761,63762],{},"Alle folgenden Themen die sich detaillierter mit einzelnen Aspekten von Scrum befassten, wurden stets in Bezug zu diesen\nam Anfang definierten Grundlagen gesetzt, wodurch der Kurs ein sehr stimmiges Gesamtbild von Scrum vermittelte. Die\ndetaillierten Themen beinhalteten unter anderem:",[994,63764,63765,63772,63775,63778,63781,63784],{},[997,63766,63767,63768,63771],{},"Scrum Regeln (basierend auf dem ",[1002,63769,63738],{"href":63736,"rel":63770},[1006]," von Jeff Sutherland und Ken Schwaber)",[997,63773,63774],{},"Scrum Planung",[997,63776,63777],{},"Teams (Formierung, Motivation, Konfliktbewältigung)",[997,63779,63780],{},"‘Defintion of Done’",[997,63782,63783],{},"Skalierung von Scrum (Scrum of Scrum)",[997,63785,63786],{},"Rolle des Scrum Masters",[439,63788,63789],{},"Der Kursleiter, Fahd Al-Fatish, überzeugte von Anfang an durch seine detaillierten Fachkenntnisse und seinen angenehmen\nVortragsstil. Zusätzlich zu den sehr gut vorbereiteten Präsentationsfolien, skizzierte er grundlegende Prinzipien und\nTechniken auch auf Flipcharts und verteilte die Blätter im Verlauf der beiden Tage an den Wänden des Seminarraums. Somit\nhatte man als Teilnehmer die wichtigsten Aspekte der präsentierten Themen ständig vor Augen und konnte diese leicht\nverinnerlichen.",[439,63791,63792],{},[1002,63793,63796],{"href":63794,"rel":63795},"https://media.synyx.de/uploads//2012/11/20121109_111413.jpg",[1006],[2205,63797],{"alt":469,"src":63798,"title":63799},"https://media.synyx.de/uploads//2012/11/20121109_111413-300x225.jpg","PSM Schulung",[439,63801,63802],{},"Besonders vorteilhaft war dies für die zahlreichen ‘Excercises’, die Bestandteil des Kurses waren. Dabei handelte es\nsich um konkrete Fragestellungen oder Anwendungsbeispiele, die in 4er Teams bearbeitet wurden und dazu dienten, die\nerlernten Inhalte (vor allem die Scrum Grundlagen) zu reflektieren und direkt anzuwenden. Die Besprechung der Ergebnisse\nführte oft zu sehr ausführlichen Diskussionen, in denen Fahd sich viel Zeit nahm die Fragen der Kursteilnehmer zu\nbeantworten. Zusätzlich sorgten diese Übungen für eine angenehme Auflockerung und erleichterten es sich anschließend\nwieder zu konzentrieren.",[3938,63804,1385],{"id":1384},[439,63806,63807],{},"Obwohl wir mit vielen der im Kurs angesprochenen Themen schon vertraut waren, konnten wir doch einige wertvolle neue\nErkenntnisse aus den zwei Tagen mitnehmen. Vor allem die ausgiebige Auseinandersetzung mit den Grundprinzipien von Scrum\nund dem Prinzip der Agilität, bietet uns eine gute Grundlage für unsere Tätigkeit als Scrum Master. Viele Fragen, die\nich vor dem Kurs formuliert hatte und von denen ich hoffte, dass sie im Kurs beantwortet würden, wurden nicht direkt\nbehandelt. Allerdings fühle ich mich nun in der Lage diese Fragen selbst zu beantworten, indem ich mich an den\nPrinzipien orientiere und mir verdeutliche, welches Ziel erreicht werden soll (Agilität). Genau das ist es schließlich\nauch, was Fahd selbst in seinem Kurs beschreibt: Ein guter Coach löst keine Probleme, sondern gibt einem die Werkzeuge\nin die Hand um seine Probleme selbst zu lösen.",{"title":469,"searchDepth":507,"depth":507,"links":63809},[63810],{"id":1384,"depth":507,"text":1385},[1031],"2012-11-19T14:05:09","„Scrum ist ein Framework zur nachhaltigen Entwicklung komplexer Produkte“ (\\nQuelle: Scrum Guide) und wird bei synyx nun schon seit einigen Jahren erfolgreich\\neingesetzt. Bisher gab es in unserem Unternehmen mit Fabian Buch nur einen zertifizierten Scrum Master, der auch für die\\nEinführung des Frameworks und Umsetzung der damit verbundenen Prinzipien im Unternehmen verantwortlich war. Durch das\\nEntstehen von mehr und mehr Teams in denen Scrum eingesetzt wurde, wurden auch mehr Scrum Master für diese Teams\\nbenötigt. Mein Kollege Michael Herbold und ich wurden von Fabian Buch in den Grundlagen von Scrum unterrichtet und\\narbeiten nun schon seit einiger Zeit neben unserer Entwicklertätigkeit auch als Scrum Master in unterschiedlichen Teams.","https://synyx.de/blog/professional-scrum-master-schulung/",{},"/blog/professional-scrum-master-schulung",{"title":63724,"description":63818},"„Scrum ist ein Framework zur nachhaltigen Entwicklung komplexer Produkte“ (\nQuelle: Scrum Guide) und wird bei synyx nun schon seit einigen Jahren erfolgreich\neingesetzt. Bisher gab es in unserem Unternehmen mit Fabian Buch nur einen zertifizierten Scrum Master, der auch für die\nEinführung des Frameworks und Umsetzung der damit verbundenen Prinzipien im Unternehmen verantwortlich war. Durch das\nEntstehen von mehr und mehr Teams in denen Scrum eingesetzt wurde, wurden auch mehr Scrum Master für diese Teams\nbenötigt. Mein Kollege Michael Herbold und ich wurden von Fabian Buch in den Grundlagen von Scrum unterrichtet und\narbeiten nun schon seit einiger Zeit neben unserer Entwicklertätigkeit auch als Scrum Master in unterschiedlichen Teams.","blog/professional-scrum-master-schulung",[38031,9555,63821,9557,389,63822],"psm","zertifizierung","„Scrum ist ein Framework zur nachhaltigen Entwicklung komplexer Produkte“ (Quelle: Scrum Guide) und wird bei synyx nun schon seit einigen Jahren erfolgreich eingesetzt. Bisher gab es in unserem Unternehmen mit…","FGwOElRYY85MAw8aNjB6oRd1_4tlZzRovDnp4fUWjNs",{"id":63826,"title":63827,"author":63828,"body":63829,"category":63936,"date":63937,"description":63938,"extension":1034,"link":63939,"meta":63940,"navigation":916,"path":63941,"seo":63942,"slug":63833,"stem":63943,"tags":63944,"teaser":63950,"__hash__":63951},"blog/blog/synyx-besucht-die-mdc-2012-in-stuttgart.md","synyx besucht die MDC 2012 in Stuttgart",[15],{"type":432,"value":63830,"toc":63927},[63831,63834,63837,63846,63849,63853,63856,63859,63862,63865,63868,63871,63875,63879,63882,63885,63888,63892,63895,63898,63902,63905,63908,63911,63915,63918,63921,63924],[435,63832,63827],{"id":63833},"synyx-besucht-die-mdc-2012-in-stuttgart",[439,63835,63836],{},"Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in\nStuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus kündigte sich mit Robert\nVirkus, dem Gründer eines unserer Partnerunternehmens, Enough Software aus Bremen, ein spannender Talk über das , meiner\nMeinung nach derzeit heißeste Thema im Mobile Bereich, an. Doch dazu später mehr.",[439,63838,63839,63840,63845],{},"Weitere spannende Talks versprachen Markus Jungingers GreenDAO Talk und natürlich auch ",[1002,63841,63844],{"href":63842,"rel":63843},"http://vogella.de",[1006],"Lars Vogel","\nseine angekündigte Übersicht über die Neuerungen von Android 4(.2)",[439,63847,63848],{},"Da unsere Gruppe aus iOS als auch Android Developern bestand war schnell klar, dass man sich unterschiedliche Talks\nanschauen möchte.",[3938,63850,63852],{"id":63851},"_1tag","1.Tag",[439,63854,63855],{},"Los gings am ersten Tag nach der Keynote direkt mit der Einführung von Lars Vogel in die Neuerungen von Android 4.2.\nWie üblich war der Vortrag gespickt mit technischen Neuerungen und auch unsere absoluten Experten in der Android\nEntwicklung konnten doch das eine oder andere noch mitnehmen: beispielsweise die Tatsache, dass man sich mit den\naktuellsten ADT Tools versorgen lassen kann, wenn man weiss wo die Checkbox versteckt ist, die einem Zugriff darauf\nermöglicht.",[439,63857,63858],{},"Es war, wie meist bei Lars, ein rundum gelungener Vortrag. Man spürt bei ihm einfach diesen gewissen Enthusiasmus,\nwelchen man wohl braucht um solche, nicht unbedingt einfachen, Themen dennoch spannend zu vermitteln.",[439,63860,63861],{},"Nach einer kurzen Pause, auf das Catering gehe ich separat ein :-), war schnell das nächste Thema gefunden und mit\nData-Handling bei Offline-Apps ging es weiter",[439,63863,63864],{},"Über diesen Ausflug möchte ich nicht allzu viele Worte verlieren, ich würde mich wiederholen. Wieder kam ich mir fehl am\nPlatz vor. Um dies zu untermauern möchte ich nur ein paar kurze Zitate wiedergeben:",[439,63866,63867],{},"“Leute beachtet bitte, MD5 ist kein gutes Verschlüsselungsverfahren” worauf aus dem Publikum mehrfach die Frage kam\n“Warum denn nicht?”.",[439,63869,63870],{},"“Hat hier wer schon mal mit SQL gearbeitet?” Ich würde mal sagen, ca 25% der Teilnehmer haben sich hierauf gemeldet…",[3938,63872,63874],{"id":63873},"_2-tag","2. Tag",[1065,63876,63878],{"id":63877},"_1-robert-virkus","1. Robert Virkus",[439,63880,63881],{},"Roberts Vortrag war für uns insbesondere deshalb spannend, da wir mit ihm schon einige Projekte realisiert haben und wir\nauf die Statements bzgl. Hybrid Apps und dem HTML5-Hype gespannt waren. Der Vortrag war sehr spannend gehalten, es\nwaren wohl die besten Folien der gesamten Konferenz, da einfach unterhaltsam. Robert zeigte gekonnt auf, warum HTML5\nwohl nicht die Lösung aller Appentwicklungsprobleme ist, zeigte die Grenzen auf und hatte durch den riesigen\nErfahrungsschatz von Enough auch einige spannende Anekdoten auf Lager.",[439,63883,63884],{},"Generell teilt er unsere Einschätzung zu HTML5 Apps, bei den Hybrid-Apps sah dies etwas anders aus. Letztlich wurde\naber deutlich klar dass man aufwendige, performance oder speicherkritische Dinge auf jeden Fall nativ implementieren\nwill, während man Handbücher, Tutorials oder dergleichen mittels HTML abbilden kann.",[439,63886,63887],{},"Vielen Dank an Robert für den kontroversen Vortrag, da er hiermit bestimmt den einen oder anderen Berater gegen sich\naufgebracht hat 🙂 Schliesslich wird mit HTML5 Beratung im Mobilen Bereich derzeit ordentlich Geld verdient.",[1065,63889,63891],{"id":63890},"_2-testdriven-ios-development","2. Testdriven iOS Development",[439,63893,63894],{},"Für mich als Android Developer war spannend zu sehen, wie komplett anders die Entwicklung unter iOS wohl wahrgenommen\nwird. Die Toolchain wirkt irgendwie kopiert, einige Dinge wurden aus der Java Welt nachgebaut. Alles wirkte ein wenig\n“under engineered” und deutlich mehr visuell getrieben. Die Softwaretechnik wird deutlich weniger wahrgenommen. Ich kam\nmir teilweise fehl am Platz vor, was aber nicht am Vortrag selbst lag sondern viel mehr an der Tatsache, das die meisten\num mich herum wirklich vieles noch nicht gehört hatten bzw. auch nicht wirklich verstanden, warum man denn Testdriven\narbeiten will.",[439,63896,63897],{},"Wüsste ich es nicht besser da wir selbst bei synyx auch iOS Developer haben würde ich hier mehr… nein ich lasse es 🙂",[1065,63899,63901],{"id":63900},"_3-greendao-von-markus-junginger","3. GreenDAO von Markus Junginger",[439,63903,63904],{},"Dieser Vortrag wurde mit Spannung erwartet aufgrund der Tatsache das wir sehen wollten, wie eine DAO Implementierung\nsich innerhalb der Android Entwicklung auswirkt. Bislang hatten wir noch nicht das Bedürfnis solche Techniken innerhalb\nunserer Projekte zu nutzen. Zum einen da es eine Menge Flexibilität und Möglichkeiten zunichte macht, zum anderen da wir\nnoch keine solch großen Datenmengen Clientseitig speichern mussten.",[439,63906,63907],{},"Markus verstand es hervorragend die seiner Meinung nach spannenden Einsatzgebiete aufzuzeigen und Vergleiche mit anderen\nDAO-Implementierungen zu ziehen. Was uns leider fehlte, war ein Vergleich der Performance, wenn man KEINE derartige\nImplementierung nutzt. Ansonsten wusste er auf die meisten aufkommenden Fragen eine Antwort und war auch nach dem\neigentlichen Talk noch zu einer kurzen Diskussionsrunde zu haben. Danke dafür!",[439,63909,63910],{},"Danach ging es mit einer sehr spannend gehaltenen Keynote zu Ende und es wurde Zeit sich auf den Weg nach Hause zu\nmachen.",[3938,63912,63914],{"id":63913},"blicke-über-den-tellerrand","Blicke über den Tellerrand:",[439,63916,63917],{},"Allgemein war die Orga bis auf wenige Kleinigkeiten hervorragend, die Technik klappte fast ausnahmslos sehr gut (am\n2.ten Tag hatte ich kleine WLAN Aussetzer im hintersten Konferenzraum und ein Beamer meinte ab und an er müsse abheben,\nzumindest hörte er sich so an) und auch das Catering war geschmacklich erste Wahl!",[439,63919,63920],{},"Als einzigen Kritikpunkt am Catering möchte ich dennoch anbringen dass es mir als Vegetarier nicht möglich war alleine\nherauszufinden, was denn wohl für mich geeignet ist und was nicht. Auch das Personal, welches reichlich vorhanden war,\nkonnte mir diese Fragen nicht beantworten, so dass ein Koch!! gerufen wurde, welcher es dann exakt bestimmt hat. Hier\nkönnte man doch einfach Schildchen machen, oder aber es wie bei der Devoxx einfach die Essensausgaben separieren.",[439,63922,63923],{},"Ich denke wir sind 2013 wieder am Start, diesmal dann eventuell mit eigenen Vorträgen um bestimmte Themen gezielt\nanspruchsvoller zu gestalten…",[439,63925,63926],{},"Das war die MDC 2012, vielen Dank an Florian Bender und sein Team!!!",{"title":469,"searchDepth":507,"depth":507,"links":63928},[63929,63930,63935],{"id":63851,"depth":507,"text":63852},{"id":63873,"depth":507,"text":63874,"children":63931},[63932,63933,63934],{"id":63877,"depth":547,"text":63878},{"id":63890,"depth":547,"text":63891},{"id":63900,"depth":547,"text":63901},{"id":63913,"depth":507,"text":63914},[1031],"2012-11-08T18:47:07","Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in\\nStuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus kündigte sich mit Robert\\nVirkus, dem Gründer eines unserer Partnerunternehmens, Enough Software aus Bremen, ein spannender Talk über das , meiner\\nMeinung nach derzeit heißeste Thema im Mobile Bereich, an. Doch dazu später mehr.","https://synyx.de/blog/synyx-besucht-die-mdc-2012-in-stuttgart/",{},"/blog/synyx-besucht-die-mdc-2012-in-stuttgart",{"title":63827,"description":63836},"blog/synyx-besucht-die-mdc-2012-in-stuttgart",[11132,63945,13219,8276,63946,63947,63948,18496,63949,389],"apple","html5","ios","mdc","objective-c","Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in Stuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus…","zC6fwExJgqo4oUeUk3333-6HnFJZSw5LAHTK2ushCto",{"id":63953,"title":63954,"author":63955,"body":63956,"category":64046,"date":64047,"description":63963,"extension":1034,"link":64048,"meta":64049,"navigation":916,"path":64050,"seo":64051,"slug":63960,"stem":64052,"tags":64053,"teaser":64056,"__hash__":64057},"blog/blog/problem-mit-maven-3-dependency-resolution.md","Problem mit Maven 3 Dependency Resolution",[389],{"type":432,"value":63957,"toc":64041},[63958,63961,63964,63967,63973,63982,63985,63989,63994,64003,64010,64013,64017,64023,64026,64031,64036,64039],[435,63959,63954],{"id":63960},"problem-mit-maven-3-dependency-resolution",[439,63962,63963],{},"Im Zuge meiner Bachelorarbeit habe ich ein Projekt von Maven 2 nach Maven 3 migriert.",[439,63965,63966],{},"Dabei bin ich beim bauen des Projekts mit Maven 3 ein paar mal über eine etwas verwirrende Fehlermeldung gestolpert.",[1065,63968,63970],{"id":63969},"das-problem",[448,63971,63972],{},"Das Problem:",[464,63974,63976],{"className":16895,"code":63975,"language":16897,"meta":469,"style":469},"Could not find artifact XXX in nexus.synyx.de (http://nexus.synyx.de)\n",[471,63977,63978],{"__ignoreMap":469},[474,63979,63980],{"class":476,"line":477},[474,63981,63975],{},[439,63983,63984],{},"…und das obwohl das Artefakt in der richtigen Version sowohl lokal als auch im Remote-Repository vorhanden ist.",[1065,63986,63988],{"id":63987},"die-ursache","Die Ursache:",[439,63990,63991],{},[990,63992,63993],{},"It’s not a bug, it’s a feature!",[439,63995,63996,63997,64002],{},"Nach etwas Recherche hat sich dann herausgestellt, dass es sich dabei um\nein ",[1002,63998,64001],{"href":63999,"rel":64000},"https://cwiki.apache.org/confluence/display/MAVEN/Maven+3.x+Compatibility+Notes#Maven3.xCompatibilityNotes-ResolutionfromLocalRepository",[1006],"Maven-3-Feature","\nhandelt mit – sagen wir – unglücklich gewählter Fehlermeldung.",[439,64004,64005,64006,64009],{},"Seit Maven 3 merkt sich Maven in den ",[990,64007,64008],{},"_maven.repositories","-Dateien für jedes Artefakt auch das Repo aus dem es\nruntergeladen wurde. Das stellt unter anderem sicher, dass auch wirklich das richtige Artefakt benutzt wird. Außerdem\nwerden so auch nicht eingetragene Repositories aufgedeckt.",[439,64011,64012],{},"Allerdings kann es durch die wenig aussagekräftige Fehlermeldung bei Benutzern leicht zur Verwirrung kommen.",[1065,64014,64016],{"id":64015},"die-lösung","Die Lösung:",[439,64018,64019,64020,64022],{},"Tritt dieses Probelm auf sollte man als erstes kontrollieren ob alle Repositories in der POM und der ",[990,64021,22532],{},"\nrichtig angegeben sind und dies dann gegebenenfalls nachholen.",[439,64024,64025],{},"Sollte die Fehlermeldung immer noch auftreten hilft ein:",[439,64027,64028],{},[471,64029,64030],{},"cd ~/.m2/repository/PFAD/ZUM/ARTEFAKT/",[439,64032,64033],{},[471,64034,64035],{},"rm _maven.repositories",[439,64037,64038],{},"Ein weiterer Grund die Datei zu löschen wäre wenn man z.B. explizit wünscht, dass das Artefakt aus dem lokalen\nMaven-Repository benutzt wird.",[1024,64040,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":64042},[64043,64044,64045],{"id":63969,"depth":547,"text":63972},{"id":63987,"depth":547,"text":63988},{"id":64015,"depth":547,"text":64016},[1030],"2012-11-05T17:16:49","https://synyx.de/blog/problem-mit-maven-3-dependency-resolution/",{},"/blog/problem-mit-maven-3-dependency-resolution",{"title":63954,"description":63963},"blog/problem-mit-maven-3-dependency-resolution",[64054,64055],"dependency-resolution","maven-3","Im Zuge meiner Bachelorarbeit habe ich ein Projekt von Maven 2 nach Maven 3 migriert. Dabei bin ich beim bauen des Projekts mit Maven 3 ein paar mal über eine…","34Dgg2ZOw4ePnLujcBv-vf8ajxYoBBvKGdyJJOIDKj4",{"id":64059,"title":64060,"author":64061,"body":64062,"category":64446,"date":64447,"description":64448,"extension":1034,"link":64449,"meta":64450,"navigation":916,"path":64451,"seo":64452,"slug":64066,"stem":64454,"tags":64455,"teaser":64458,"__hash__":64459},"blog/blog/properly-calculating-time-differences-in-javascript.md","Properly calculating time differences in JavaScript",[9],{"type":432,"value":64063,"toc":64444},[64064,64067,64074,64094,64123,64126,64161,64164,64178,64186,64225,64228,64248,64259,64268,64287,64302,64363,64369,64406,64419,64431,64442],[435,64065,64060],{"id":64066},"properly-calculating-time-differences-in-javascript",[439,64068,64069,64070,64073],{},"Let me tell you a tale about a fat-client application that has nice some time-related logic written in JavaScript. We\nwant to calculate the difference between two dates, measured in days. Easy, you say, just use the ",[990,64071,64072],{},"Date","object and do\nsome calculating.",[439,64075,64076,64077,64080,64081,64084,64085,64087,64088,64090,64091,64093],{},"As a JavaScript veteran you know that you have to use ",[471,64078,64079],{},"new Date()"," instead of ",[471,64082,64083],{},"Date()"," because the second one returns a\nstring for some reason, you recall that the month of October is identified by the number ",[471,64086,42271],{}," because we start counting\nthe months starting at ",[471,64089,4745],{}," and quickly figure out that subtracting two ",[471,64092,64072],{}," objects results in a number which is the\namount of milliseconds passed between two moments.",[464,64095,64097],{"className":16895,"code":64096,"language":16897,"meta":469,"style":469},"\nvar DAY_IN_MS = 24 * 60 * 60 * 1000;\nvar d1 = new Date(2012, 9, 27);\nvar d2 = new Date(2012, 9, 28);\nconsole.log((d2 - d1) / DAY_IN_MS); // yields 1\n\n",[471,64098,64099,64103,64108,64113,64118],{"__ignoreMap":469},[474,64100,64101],{"class":476,"line":477},[474,64102,917],{"emptyLinePlaceholder":916},[474,64104,64105],{"class":476,"line":507},[474,64106,64107],{},"var DAY_IN_MS = 24 * 60 * 60 * 1000;\n",[474,64109,64110],{"class":476,"line":547},[474,64111,64112],{},"var d1 = new Date(2012, 9, 27);\n",[474,64114,64115],{"class":476,"line":584},[474,64116,64117],{},"var d2 = new Date(2012, 9, 28);\n",[474,64119,64120],{"class":476,"line":607},[474,64121,64122],{},"console.log((d2 - d1) / DAY_IN_MS); // yields 1\n",[439,64124,64125],{},"Looks fine, doesn’t it? So just wrap it in a function, unit-test it and be done with it? Not so fast there. Let’s just\nchange the dates ever so slightly",[464,64127,64129],{"className":16895,"code":64128,"language":16897,"meta":469,"style":469},"\nvar DAY_IN_MS = 24 * 60 * 60 * 1000;\nvar d1 = new Date(2012, 9, 27);\nvar d2 = new Date(2012, 9, 28);\nvar d3 = new Date(2012, 9, 29);\nconsole.log((d2 - d1) / DAY_IN_MS); // yields 1\nconsole.log((d3 - d2) / DAY_IN_MS); // yields 1.0416666666666667\n\n",[471,64130,64131,64135,64139,64143,64147,64152,64156],{"__ignoreMap":469},[474,64132,64133],{"class":476,"line":477},[474,64134,917],{"emptyLinePlaceholder":916},[474,64136,64137],{"class":476,"line":507},[474,64138,64107],{},[474,64140,64141],{"class":476,"line":547},[474,64142,64112],{},[474,64144,64145],{"class":476,"line":584},[474,64146,64117],{},[474,64148,64149],{"class":476,"line":607},[474,64150,64151],{},"var d3 = new Date(2012, 9, 29);\n",[474,64153,64154],{"class":476,"line":642},[474,64155,64122],{},[474,64157,64158],{"class":476,"line":663},[474,64159,64160],{},"console.log((d3 - d2) / DAY_IN_MS); // yields 1.0416666666666667\n",[439,64162,64163],{},"This is the point where most developers start cursing. Is this a new way in which JavaScript is broken? It isn’t,\nbecause the number is completely accurate.",[439,64165,64166,64167,64170,64171,520,64174,64177],{},"The JavaScript object created by ",[471,64168,64169],{},"new Date(2012, 9, 28)"," represents midnight on the 28th of October, 2012 ",[990,64172,64173],{},"in your local\ntime zone",[471,64175,64176],{},"new Date(2012, 9, 29)"," represents midnight the following day.",[439,64179,64180,64181,1402],{},"Subtracting the first from the seconds yields the number of milliseconds that have passed between those two moments,\nwhich, as you probably have guessed, includes the extra hour put in because\nof ",[1002,64182,64185],{"href":64183,"rel":64184},"http://www.timeanddate.com/worldclock/clockchange.html?n=37",[1006],"daylight savings time",[464,64187,64189],{"className":16895,"code":64188,"language":16897,"meta":469,"style":469},"\n> new Date(2012, 9, 29);\nMon Oct 29 2012 00:00:00 GMT+0100 (CET)\n> new Date(2012, 9, 28);\nSun Oct 28 2012 00:00:00 GMT+0200 (CEST)\n> (new Date(2012, 9, 29) - new Date(2012, 9, 28)) / 60 / 60 / 100\n25\n\n",[471,64190,64191,64195,64200,64205,64210,64215,64220],{"__ignoreMap":469},[474,64192,64193],{"class":476,"line":477},[474,64194,917],{"emptyLinePlaceholder":916},[474,64196,64197],{"class":476,"line":507},[474,64198,64199],{},"> new Date(2012, 9, 29);\n",[474,64201,64202],{"class":476,"line":547},[474,64203,64204],{},"Mon Oct 29 2012 00:00:00 GMT+0100 (CET)\n",[474,64206,64207],{"class":476,"line":584},[474,64208,64209],{},"> new Date(2012, 9, 28);\n",[474,64211,64212],{"class":476,"line":607},[474,64213,64214],{},"Sun Oct 28 2012 00:00:00 GMT+0200 (CEST)\n",[474,64216,64217],{"class":476,"line":642},[474,64218,64219],{},"> (new Date(2012, 9, 29) - new Date(2012, 9, 28)) / 60 / 60 / 100\n",[474,64221,64222],{"class":476,"line":663},[474,64223,64224],{},"25\n",[439,64226,64227],{},"So where is the error? The error is in our assumption that a day has 24 hours, because depending on how you define a\nday, it hasn’t – October 28th 2012 has 25 hours.",[439,64229,64230,64231,15250,64234,64238,64239,8351,64243,64247],{},"If you Google “JavaScript time difference”, most people just use\n",[471,64232,64233],{},"Math.round",[1002,64235,5008],{"href":64236,"rel":64237},"http://psoug.org/snippet/Javascript-Calculate-time-difference-between-two-dates_116.htm",[1006],") or simply\nuse flat-out buggy\ncode (",[1002,64240,5008],{"href":64241,"rel":64242},"https://web.archive.org/web/20151119010127/http://www-10.lotus.com:80/ldd/ddwiki.nsf/dx/Various_Time_Differences_in_JavaScript",[1006],[1002,64244,4871],{"href":64245,"rel":64246},"http://www.javascriptkit.com/javatutors/datedifference.shtml",[1006],")\nand call it a day (pun intended), but that is not how we roll here.",[439,64249,64250,64251,64254,64255,64258],{},"What do we really mean when we ask ",[990,64252,64253],{},"“How many days have passed between two dates in the calendar”","? We usually mean\n",[990,64256,64257],{},"“How many midnights have happened between these two dates?”",". Unfortunately, because of DST, you can’t just use the\nnumber of milliseconds between two dates at midnight to calculate how many midnights have happened, because some of them\nare more or less than 24 hours apart. If only there was a magical place that doesn’t have this madness going on…",[439,64260,64261,64262,64267],{},"Luckily, there is, and that place is ",[1002,64263,64266],{"href":64264,"rel":64265},"http://en.wikipedia.org/wiki/Coordinated_Universal_Time",[1006],"UTC",". UTC is a time\nmeasuring system that does not have daylight savings time.",[439,64269,64270,8351,64273],{},[448,64271,64272],{},"Edit:",[990,64274,64275,64276,64281,64282,1402],{},"as pointed out in the comments, the rabbit hole goes down even further – officially, even in UTC, a day might\nhave more than 24 hours because of leap seconds. Fortunately for us, the ECMA-262\nspecification ",[1002,64277,64280],{"href":64278,"rel":64279},"http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.1",[1006],"explicitly ignores leap seconds"," and we can\ngo about our business. If JavaScript would implement UTC correctly, we would have to account for leap seconds or\nuse ",[1002,64283,64286],{"href":64284,"rel":64285},"http://en.wikipedia.org/wiki/Universal_Time#Versions",[1006],"UT1",[439,64288,64289,64290,64292,64293,64295,64296,8351,64298,64301],{},"The JavaScript Date API is just as beautiful as most other JavaScript APIs: While the only useful use of the ",[471,64291,64072],{},"\nobject is by using it as a constructor (with ",[471,64294,46528],{},"), the way to use UTC is by using the ",[990,64297,14021],{},[471,64299,64300],{},"Date.UTC"," which\nreturns a unix timestamp. This is the JavaScript time API in a nutshell:",[464,64303,64305],{"className":16895,"code":64304,"language":16897,"meta":469,"style":469},"\n> new Date(2012, 9, 29);\nMon Oct 29 2012 00:00:00 GMT+0100 (CET) // (a somewhat useful object)\n> Date(2012, 9, 29);\n'Mon Nov 05 2012 16:18:12 GMT+0100 (CET)' // (a string - no relation to the parameters)\n> Date.UTC(2012, 9, 29);\n1351468800000 // (unix time in milliseconds)\n> new Date.UTC(2012, 9, 29); // failure\nTypeError: function UTC() { [native code] } is not a constructor\n at repl:1:9\n [....]\n\n",[471,64306,64307,64311,64315,64320,64325,64333,64338,64343,64348,64353,64358],{"__ignoreMap":469},[474,64308,64309],{"class":476,"line":477},[474,64310,917],{"emptyLinePlaceholder":916},[474,64312,64313],{"class":476,"line":507},[474,64314,64199],{},[474,64316,64317],{"class":476,"line":547},[474,64318,64319],{},"Mon Oct 29 2012 00:00:00 GMT+0100 (CET) // (a somewhat useful object)\n",[474,64321,64322],{"class":476,"line":584},[474,64323,64324],{},"> Date(2012, 9, 29);\n",[474,64326,64327,64330],{"class":476,"line":607},[474,64328,64329],{},"'Mon Nov 05 2012 16:18:12 GMT+0100 (CET)'",[474,64331,64332],{}," // (a string - no relation to the parameters)\n",[474,64334,64335],{"class":476,"line":642},[474,64336,64337],{},"> Date.UTC(2012, 9, 29);\n",[474,64339,64340],{"class":476,"line":663},[474,64341,64342],{},"1351468800000 // (unix time in milliseconds)\n",[474,64344,64345],{"class":476,"line":694},[474,64346,64347],{},"> new Date.UTC(2012, 9, 29); // failure\n",[474,64349,64350],{"class":476,"line":700},[474,64351,64352],{},"TypeError: function UTC() { [native code] } is not a constructor\n",[474,64354,64355],{"class":476,"line":913},[474,64356,64357],{}," at repl:1:9\n",[474,64359,64360],{"class":476,"line":920},[474,64361,64362],{}," [....]\n",[439,64364,64365,64366,64368],{},"The correct calculation, without using ",[471,64367,64233],{}," or other hacks therefore is",[464,64370,64372],{"className":16895,"code":64371,"language":16897,"meta":469,"style":469},"\nvar DAY_IN_MS = 24 * 60 * 60 * 1000;\nvar d1 = Date.UTC(2012, 9, 27);\nvar d2 = Date.UTC(2012, 9, 28);\nvar d3 = Date.UTC(2012, 9, 29);\nconsole.log((d2 - d1) / DAY_IN_MS); // yields 1\nconsole.log((d3 - d2) / DAY_IN_MS); // yields 1\n\n",[471,64373,64374,64378,64382,64387,64392,64397,64401],{"__ignoreMap":469},[474,64375,64376],{"class":476,"line":477},[474,64377,917],{"emptyLinePlaceholder":916},[474,64379,64380],{"class":476,"line":507},[474,64381,64107],{},[474,64383,64384],{"class":476,"line":547},[474,64385,64386],{},"var d1 = Date.UTC(2012, 9, 27);\n",[474,64388,64389],{"class":476,"line":584},[474,64390,64391],{},"var d2 = Date.UTC(2012, 9, 28);\n",[474,64393,64394],{"class":476,"line":607},[474,64395,64396],{},"var d3 = Date.UTC(2012, 9, 29);\n",[474,64398,64399],{"class":476,"line":642},[474,64400,64122],{},[474,64402,64403],{"class":476,"line":663},[474,64404,64405],{},"console.log((d3 - d2) / DAY_IN_MS); // yields 1\n",[439,64407,64408,64409,64412,64413,64418],{},"These kinds of bugs are sneaky because they only show up for certain input values (I wonder if I would have noticed it\nif I hadn’t tested the code last week around the DST change) and usually don’t show up in unit tests unless you happen\nto know what you are looking for. The results are often ",[990,64410,64411],{},"nearly"," correct, and we are not used to thinking about time\nzones and\nwe ",[1002,64414,64417],{"href":64415,"rel":64416},"http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time",[1006],"often hold invalid assumptions about time",".\nAlways using UTC isn’t a solution either, because sometimes we want the local time zone to be considered.",[439,64420,64421,64422,64427,64428,64430],{},"Libraries like ",[1002,64423,64426],{"href":64424,"rel":64425},"http://momentjs.com/",[1006],"Moment.js"," help, but the real protection against these kinds of bugs is to know\nabout time zones, time measurement system and thinking about what you are actually calculating instead of simply\nthrowing a ",[471,64429,64233],{}," in there to make it all work.",[439,64432,64433,64434,64437,64438,64441],{},"Just as anybody that has had the pleasure of seeing ",[990,64435,64436],{},"Rent"," will tell you: while a year has ",[990,64439,64440],{},"five hundred twenty-five\nthousand six hundred minutes",", it still is difficult to measure the time of the year.",[1024,64443,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":64445},[],[1030],"2012-11-05T17:05:23","Let me tell you a tale about a fat-client application that has nice some time-related logic written in JavaScript. We\\nwant to calculate the difference between two dates, measured in days. Easy, you say, just use the Dateobject and do\\nsome calculating.","https://synyx.de/blog/properly-calculating-time-differences-in-javascript/",{},"/blog/properly-calculating-time-differences-in-javascript",{"title":64060,"description":64453},"Let me tell you a tale about a fat-client application that has nice some time-related logic written in JavaScript. We\nwant to calculate the difference between two dates, measured in days. Easy, you say, just use the Dateobject and do\nsome calculating.","blog/properly-calculating-time-differences-in-javascript",[2756,15201,64456,64457],"time","utc","Let me tell you a tale about a fat-client application that has nice some time-related logic written in JavaScript. We want to calculate the difference between two dates, measured in…","ni8qXO9StrZax4AqNEra8EirfvOftQEpRiXb9e9ojTo",{"id":64461,"title":64462,"author":64463,"body":64464,"category":64507,"date":64508,"description":64471,"extension":1034,"link":64509,"meta":64510,"navigation":916,"path":64511,"seo":64512,"slug":64468,"stem":64513,"tags":64514,"teaser":64520,"__hash__":64521},"blog/blog/opencms-days-2012.md","OpenCms-Days 2012",[51],{"type":432,"value":64465,"toc":64505},[64466,64469,64472,64475,64485,64488,64496,64499,64502],[435,64467,64462],{"id":64468},"opencms-days-2012",[439,64470,64471],{},"Von Montag, den 24.09 und Dienstag, den 25.09, fanden in Köln die OpenCms-Days 2012 statt.",[439,64473,64474],{},"Gemeinsam mit dem ehemaligen synyxer Florian Hopf haben sich Oliver Messner, Fabian Buch und ich schon am Sonntagabend\nnach Köln begeben.",[439,64476,64477,64478,64484],{},"Am Montagmorgen eröffnete Alexander Kandzior von Alkacon die Veranstaltung mit der Vorstellung der\nnagelneuen ",[1002,64479,64483],{"href":64480,"rel":64481,"title":64482},"http://www.opencms.org/en/news/120924-opencms-v85-releasenotes.html",[1006],"OpenCms Release Notes Version 8.5","Version 8.5","\nund demonstrierte hierbei direkt den neuen Editor, der das Arbeiten vereinfachen und beschleunigen wird. In diesem\nZusammenhang wurde auch eine Solr-Integration für mehr Suchmöglichkeiten und eine CMIS-Anbindung zum Zugriff auf\nContent durch diesen Standard vorgestellt.",[439,64486,64487],{},"Es folgten Vorträge zum Thema OpenCms und zur Entwicklung mit OpenCms bis hin zur High Availability und zum Continouos\nIntegration.",[439,64489,64490,64491],{},"Am Ende des ersten Tages durfte ich einen Vortrag über die Migration eines Ant-Builds hin zu einem Gradle-Build\nhalten. In diesem habe ich beschrieben wie ein bestehender Ant-Build mit Hilfe von Gradle um Dependency-Management\nerweitert werden kann, ohne das Build-File von Ant verändern zu müssen. Die Folien hierzu gibt es auch\nonline: ",[1002,64492,64495],{"href":64493,"rel":64494,"title":64495},"http://www.opencms-days.org/news/Developing-OpenCms-with-Gradle/",[1006],"Developing OpenCms with Gradle",[439,64497,64498],{},"Der krönende Abschluss des ersten Tages war dann das “Come together”, bei welchem Alkacon 200 leckere Kölsch spendiert\nhat 🙂 und viele interessante sowie lustige Gespräche zu Stande kamen.",[439,64500,64501],{},"Der zweite Tag wurde mit einer Keynote aus einem Vorschungsprojekt mit dem Thema Semantic Web begonnenen und endete über\nVorträge über die Solr-Integration und Cloud-Technologien mit einer Diskussionsrunde einiger OpenCms-Experten über\nmögliche zukünftige Features und diverse Möglichkeiten der Zusammenarbeit innerhalb der Community.",[439,64503,64504],{},"Es war sehr informativ und hat dazu noch jede Menge Spaß gemacht 🙂 Ich freue mich schon auf die nächsten OpenCms-Days,\nnächstes Jahr?",{"title":469,"searchDepth":507,"depth":507,"links":64506},[],[1412],"2012-10-09T11:45:08","https://synyx.de/blog/opencms-days-2012/",{},"/blog/opencms-days-2012",{"title":64462,"description":64471},"blog/opencms-days-2012",[64515,64516,64517,64518,64519],"cmis","gradle","opencms","opencmsdays","solr","Von Montag, den 24.09 und Dienstag, den 25.09, fanden in Köln die OpenCms-Days 2012 statt. Gemeinsam mit dem ehemaligen synyxer Florian Hopf haben sich Oliver Messner, Fabian Buch und ich…","YPpyh-ohkY9tUtP_OQYCYgiOkOOYgq_yQ9fIVHPNvME",{"id":64523,"title":64524,"author":64525,"body":64526,"category":64545,"date":64546,"description":64547,"extension":1034,"link":64548,"meta":64549,"navigation":916,"path":64550,"seo":64551,"slug":64552,"stem":64553,"tags":64554,"teaser":64556,"__hash__":64557},"blog/blog/die-verruckten-kommen.md","Die Verrückten kommen",[133],{"type":432,"value":64527,"toc":64543},[64528,64531,64534,64537],[435,64529,64524],{"id":64530},"die-verrückten-kommen",[439,64532,64533],{},"Seit etwas über fünf Jahren arbeite ich nun bei synyx. In dieser Zeit verändert sich innerhalb der Firma natürlich auch\nso manches. So mancher Mitarbeiter geht, aber dafür kommen auch immer wieder neue hinzu. Und über die Zeit gesehen ist\nein stetiger Anstieg der Mitarbeiterzahl zu beobachten. Dies spiegelt sich natürlich auch in den Büroräumen wieder. Zum\nBegin meiner Arbeit bei synyx gab es in der Karlstraße 68 ein Stockwerk in welchem sich alle Mitarbeiter befanden.\nInnerhalb diesem wurde es dann allerdings recht schnell sehr “kuschlig”. Wir brauchten einfach mehr Platz! Als\nZwischenlösung gab es dann mal ein weiteres Zimmer im 5. Stock – das entschärfte die Platzproblematik etwas, bei\nmehrmaligem auf und ab am Tag war die dazwischen liegende Treppe dann aber doch recht sportlich. Besser wurde dies als\ninnerhalb des Hauses das zweite Stockwerk hinzu genommen werden konnte. Diese auch heute noch vorhandene Lösung\nfunktioniert recht gut, da die zwei Stockwerke direkt übereinander liegen. Allerdings wird es so langsam auch schon\nwieder eng, und so sind wir auf der Suche nach einer neuen Bleibe.",[439,64535,64536],{},"Synyx wäre nicht synyx, wenn das hier nicht auch etwas anders funktionieren würde wie in einer “normalen” Firma. So gibt\nes viele Anforderungen an die neuen Räume. Darunter finden sich neben solchen wie ‘genügend Platz’, ‘hell’, ‘ruhig’ auch\nsolche wie ‘schnelles Internet’, ‘gemütlich’, ‘gute Rauchermöglichkeiten’, ‘Grillmöglichkeit’ und ‘gute öffentliche\nVerkehrsanbindung bei zentraler Lage’. Leider gibt es dadurch schon recht wenige Büros welche überhaupt die\n“Einstiegshürde” nehmen. Das macht die Suche natürlich etwas schwerer, aber letztendlich sind wir auf der Suche nach\nUNSEREM Büro, dort wo wir in Zukunft die meiste unserer Zeit verbringen werden. Und damit dies nicht nur den Chefs\ngefällt, werden die Mitarbeiter in den “Bürofindungsprozess” mit einbezogen. Neben den Anforderungen haben wir auch die\nMöglichkeit, uns die potenziellen neuen Büroräume im Voraus anzuschauen, um anschließend auch unsere Meinung dazu zu\näußern. So waren wir in den letzten Monaten schon mit “den ganzen Verrückten” zu verschiedenen Besichtigungen. Besonders\ngut ist dabei, dass jeder an etwas anderes denkt, auf verschiedenes Wert legt, und wir somit recht Effektiv die Vor-\nund Nachteile der jeweiligen Optionen erfassen können.",[439,64538,64539],{},[2205,64540],{"alt":469,"src":64541,"title":64542},"https://media.synyx.de/uploads//2012/09/buero.jpg","Bürobesichtigung",{"title":469,"searchDepth":507,"depth":507,"links":64544},[],[1031],"2012-10-08T11:07:41","Seit etwas über fünf Jahren arbeite ich nun bei synyx. In dieser Zeit verändert sich innerhalb der Firma natürlich auch\\nso manches. So mancher Mitarbeiter geht, aber dafür kommen auch immer wieder neue hinzu. Und über die Zeit gesehen ist\\nein stetiger Anstieg der Mitarbeiterzahl zu beobachten. Dies spiegelt sich natürlich auch in den Büroräumen wieder. Zum\\nBegin meiner Arbeit bei synyx gab es in der Karlstraße 68 ein Stockwerk in welchem sich alle Mitarbeiter befanden.\\nInnerhalb diesem wurde es dann allerdings recht schnell sehr “kuschlig”. Wir brauchten einfach mehr Platz! Als\\nZwischenlösung gab es dann mal ein weiteres Zimmer im 5. Stock – das entschärfte die Platzproblematik etwas, bei\\nmehrmaligem auf und ab am Tag war die dazwischen liegende Treppe dann aber doch recht sportlich. Besser wurde dies als\\ninnerhalb des Hauses das zweite Stockwerk hinzu genommen werden konnte. Diese auch heute noch vorhandene Lösung\\nfunktioniert recht gut, da die zwei Stockwerke direkt übereinander liegen. Allerdings wird es so langsam auch schon\\nwieder eng, und so sind wir auf der Suche nach einer neuen Bleibe.","https://synyx.de/blog/die-verruckten-kommen/",{},"/blog/die-verruckten-kommen",{"title":64524,"description":64533},"die-verruckten-kommen","blog/die-verruckten-kommen",[64555,389],"buro","Seit etwas über fünf Jahren arbeite ich nun bei synyx. In dieser Zeit verändert sich innerhalb der Firma natürlich auch so manches. So mancher Mitarbeiter geht, aber dafür kommen auch…","dqOg9zlkDT_M-d3EeqEpyr3L1_cDmJmnEGpySOEpQBc",{"id":64559,"title":64560,"author":64561,"body":64562,"category":64678,"date":64679,"description":64680,"extension":1034,"link":64681,"meta":64682,"navigation":916,"path":64683,"seo":64684,"slug":64566,"stem":64685,"tags":64686,"teaser":64687,"__hash__":64688},"blog/blog/database-migration-using-flyway-and-spring-and-existing-data.md","Database Migration using Flyway and Spring (and existing Data)",[166],{"type":432,"value":64563,"toc":64676},[64564,64567,64570,64577,64580,64583,64591,64594,64599,64602,64605,64610,64614,64618,64622,64625,64631],[435,64565,64560],{"id":64566},"database-migration-using-flyway-and-spring-and-existing-data",[439,64568,64569],{},"My team and I are currently working on an project we first started in early 2010. The application is in production\nsince sometime late 2010 and there has been no active development except for minor enhancements and bugfixes since then.\nEven if our code, processes and tools were good in 2010 we’ve improved a lot since then. Working on my old projects is\none of the occasions, where this becomes most evident.",[439,64571,64572,64573,64576],{},"When we start a new project today we usually use the database migration tool ",[1002,64574,53946],{"href":53944,"rel":64575},[1006],"\nright from the beginning. The tool keeps code and the database schema in sync and usually takes care of automatic\nmigration during application startup.",[439,64578,64579],{},"Back then we usually used SQL scripts, which had to be executed manually during deployment, to keep the database up to\ndate. Out of laziness or lack of time, this was also the first approach we took this week to handle database changes.\nThese scripts are checked into version control along with any code changes.",[439,64581,64582],{},"This may work pretty well in the beginning, but can also become annoying very fast: Everything you’ve to do manually is\ndestined to fail some time. ALL THE TIME! It fails on my colleagues working machines, it fails on our continuous\nintegration server (Jenkins) and it will probably fail hard on production, if you don’t pay enough attention during a\ndeployment.",[439,64584,64585,64586,1402],{},"So there we were, about 60 minutes ago, standing there with a database dump from production and a bunch of SQL scripts,\nwhich accumulated during this week of development. Well, it is friday and I wanted to test something new so I remembered\na talk I attended earlier this year about an alternative to Liquibase: ",[1002,64587,64590],{"href":64588,"rel":64589},"http://code.google.com/p/flyway/",[1006],"flyway",[439,64592,64593],{},"What it basically does, is to execute a bunch of SQL scripts it hasn’t already executed on the given database. To get\nstarted I saved the dump of the production system into the db/migration/ package of our web application:",[439,64595,64596],{},[471,64597,64598],{},"mkdir -p src/main/respources/db/migration/ && cp prod_dump.sql src/main/resources/db/migration/V1_initial_import.sql",[439,64600,64601],{},"As many of our applications, this one too is based on Spring and Maven. So I added the flyway dependency to our pom.xml\nand also some XML to the bean configuration.",[439,64603,64604],{},"pom.xml:",[439,64606,8990,64607],{},[64608,64609],"dependency",{},[64611,64612,64613],"group-id",{},"\ncom.googlecode.flyway\n",[64615,64616,64617],"artifact-id",{},"\nflyway-core\n",[64619,64620,64621],"version",{},"\n1.7\n",[439,64623,64624],{},"Even if there is a Maven plugin to execute the migration scripts, we got used to migrating the database during the\napplication boot process (because you will never have to think about it again, it simply migrates…). So we add the\nflyway bean to our bean configuration file. It is important that the flyway bean is instantiated early because it has to\nmigrate the database before anyone else uses it. In our case “anyone” is actually the EntityManager, so i configured the\npersistenceUnitManager to depend on flyway (which means flyway is running first):",[439,64626,8990,64627],{},[64628,64629],"bean",{"id":64590,"init-method":64630},"migrate",[64632,64633,64635,64638,64641],"property",{"name":64634,"ref":64634},"dataSource",[439,64636,64637],{},"\u003Cbean id=\"persistenceUnitManager\" depends-on=\"flyway\"",[439,64639,64640],{},"class=\"org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager\">",[64632,64642,64644,64645,64652,64655,64658,64661,64664,64667,64670,64673],{"name":64643,"ref":64634},"defaultDataSource","\n\n`\n",[439,64646,64647,64648,60363],{},"Of course there are several configuration options for the flyway object. You can refer to\nthe ",[1002,64649,22273],{"href":64650,"rel":64651},"http://code.google.com/p/flyway/wiki/ApplicationIntegration",[1006],[439,64653,64654],{},"By default, flyway will now search for SQL scripts in your classpath. It expects the scripts in the db.migration\npackage, following a particular naming scheme: Vxxx__description.sql, just like the one we already created earlier (\nV1_initial_import.sql). It will also remember at which version the database currently is and will only execute scripts\nit has not executed so far. So when we start our application flyway will find our script and will execute it. Afterwards\nit will know, that the database is at version 1 and will not execute the V1__ file again. This will only work on an\nempty database so you should drop and create your local database at this point.",[439,64656,64657],{},"`5:29:11,362 INFO .flyway.core.metadatatable.MetaDataTable: 111 - Creating Metadata table: schema_version (Schema: mydb)",[439,64659,64660],{},"15:29:11,408 INFO glecode.flyway.core.migration.DbMigrator: 120 - Current schema version: null",[439,64662,64663],{},"15:29:11,412 INFO glecode.flyway.core.migration.DbMigrator: 205 - Migrating to version 1",[439,64665,64666],{},"15:29:24,694 INFO glecode.flyway.core.migration.DbMigrator: 191 - Successfully applied 1 migrations (execution time 00:\n1.290s).`",[439,64668,64669],{},"Now if I have database changes, I simply add a new SQL file containing the change with the prefix V2__ and so on. If\nmy colleagues update their working copy they will also get my SQL changes and flyway will execute it during application\nbootup (or integration-test) and nobody has to do this manually anymore.",[439,64671,64672],{},"Ok, nice. But what about production? When we deploy the new version of the app we also want the scripts to be executed\nbut not the initial import, right? I dont want to execute “drop database dbname; create database dbname;” there. Flyway\ninitializes itself on the first start but only if the database it writes to is empty. So the migration will fail on\nproduction.",[439,64674,64675],{},"For this case flyway also comes with a goal that creates the metadata tables. It comes with the ability, to initialize\nyour metadata tables at any given version. You can accomplish this by code (call init() on the flyway object), via the\nMaven plugin (flyway:init) or on the commandline. Because I did not want to install any extra software on the production\nmachine, I simply prepared an SQL dump of the metadata table (schema_version) right after the initial import was\nexecuted. This will now be executed against the production database right before the next deployment. Yes, manually… but\nfor the last time ;).",{"title":469,"searchDepth":507,"depth":507,"links":64677},[],[1030],"2012-10-05T18:37:36","My team and I are currently working on an project we first started in early 2010. The application is in production\\nsince sometime late 2010 and there has been no active development except for minor enhancements and bugfixes since then.\\nEven if our code, processes and tools were good in 2010 we’ve improved a lot since then. Working on my old projects is\\none of the occasions, where this becomes most evident.","https://synyx.de/blog/database-migration-using-flyway-and-spring-and-existing-data/",{},"/blog/database-migration-using-flyway-and-spring-and-existing-data",{"title":64560,"description":64569},"blog/database-migration-using-flyway-and-spring-and-existing-data",[54575,64590,54578,1426],"My team and I are currently working on an project we first started in early 2010. The application is in production since sometime late 2010 and there has been no…","_mfAJ9hiqujLo3w9GVXpqEnEeyqP2HZ9aIPFhY0WVkg",{"id":64690,"title":64691,"author":64692,"body":64693,"category":64754,"date":64755,"description":64700,"extension":1034,"link":64756,"meta":64757,"navigation":916,"path":64758,"seo":64759,"slug":64697,"stem":64760,"tags":64761,"teaser":64766,"__hash__":64767},"blog/blog/synyx-goes-to-europa-park.md","synyx goes to Europa Park",[262],{"type":432,"value":64694,"toc":64752},[64695,64698,64701,64704,64707,64716,64719,64722,64725,64728,64731,64734,64743,64746,64749],[435,64696,64691],{"id":64697},"synyx-goes-to-europa-park",[439,64699,64700],{},"Freitag früh, um kurz nach 7 in Karlsruhe…",[439,64702,64703],{},"Noch ziemlich verschlafen und müde schlagen die ersten Mitarbeiter im synyx-Büro auf. Eine fast unmenschliche Zeit für\nunsere Verhältnisse, denn normalerweise fangen wir doch etwas später an zu arbeiten. Aber was tut man nicht alles für\neinen Betriebsausflug? So mancher hatte schon die Vermutung, es könne nur ein Trick sein und man versuche die\nMitarbeiter so zu einer früheren Zeit ans Arbeiten zu bringen. Es ist der erste Betriebsausflug ausserhalb unserer\nSommer- und Winterfeiern. Auch wenn noch alle sehr müde waren, so spürte man doch schon eine gewisse Vorfreude auf den\nAusflug in den Europa Park.",[439,64705,64706],{},"Zunächst durfte unsere Kaffeemaschine ersteinmal ihren Dienst verrichten und uns mit Kaffee versorgen. Wie gut, dass wir\nauch „Coffee-to-go“-Becher haben. Nach der ersten Dosis Koffein wurden wir auch so langsam wach und konnten in einem\neinigermaßen fitten Zusatand das Büro verlassen. Um 7.30 Uhr stand, pünktlich zur vereinbarten Abfahrtszeit, der Bus\nfür uns bereit. Die frühe Abfahrt war geplant, damit wir sofort zur Eröffnung des Parks da sein konnten. Wir wollten ja\nschließlich jede Minute, die der Europa Park geöffnet hat auskosten.",[439,64708,64709],{},[1002,64710,64713],{"href":64711,"rel":64712},"https://media.synyx.de/uploads//2012/10/europapark.jpg",[1006],[2205,64714],{"alt":469,"src":64711,"title":64715},"europapark",[439,64717,64718],{},"Nachdem jeder seinen Platz gefunden hatte, ging es gut gelaunt gen Rust. Dort angekommen stürmten wir sofort in den Park\nin dem unser erstes Ziel gleich die Silver Star war. So war konnten wir gleich zu Beginn unseren Adrenalinpegel so\nrichtig pushen. So voller Adrenalin machten wir zunächst noch in der großen Gruppe ein oder zwei weitere Achterbahnen\nunsicher. Danach teilte sich die Gruppe dann so langsam in immer kleinere Gruppen auf. Es bekommt ja auch nicht jedem\njede Achterbahn 😉",[439,64720,64721],{},"Die Truppe mit der ich unterwegs war machte dann so ziemlich jede Achterbahn unsicher. Natürlich durften Blue Fire und\ndie neue Holzachterbahn dabei nicht fehlen. Große Freude konnten wir Thomas bereiten, indem wir zur Schiffsschaukel\ngingen. Dort hielt es ihn gleich mehrere Fahrten fest. Weiß noch jemand, wie oft hintereinander Thomas Vindjammer\ngefahren ist?",[439,64723,64724],{},"So langsam stellte sich bei den meisten der Hunger ein und wir machten uns auf die Suche nach etwas Essbarem, an Auswahl\nmangelte es dort ja nicht. Gut gesättigt ging es dann gleich wieder los zu den nächsten Fahrgeschäften. Eurosat und\n-MIR, Posseidon, Cassandra, Matterhorn-Blitz, Schweizer Bobbahn, Alpenexpress und viele andere bekannte Achterbahnen\nund Fahrgeschäfte durften dabei natürlich nicht fehlen.",[439,64726,64727],{},"Da uns auch das Wetter gut gesonnen war, nutzten wir die Gelegenheit zum Fjord-Rafting. Denn klar ist, dass es immer\nirgendwen treffen wird, der hier nass wird. Klar, gibt es ja auch Ponchos zu kaufen. Aber das war ja etwas für Weicheier\n😉 Unsere Truppe wurde hier diesmal so richtig nass. Es traf nicht nur einen, sondern wirklich uns alle. Wir waren froh,\ndass es wirklich warm genug war um schnell wieder zu trocknen. Das wir so nass waren hielt uns auch nicht davon ab\nweiter zu gehen und uns eine Portion „Gruselschocker“ in der Geisterbahn abzuholen.",[439,64729,64730],{},"Mittlerweile war unsere Truppe auf 4 Leute geschrumpft, trotzdem hatten wir wahnsinnig viel Spaß. Lustig war auch, egal\nwo man hinkam, ein bekanntes Gesicht hat man immer irgendwo gesehen. Und sobald einem synyxler über den Weg liefen gab\nes ein großes „Hallo“, man tauschte sich kurz aus, was man gerade gemacht hat oder wo es als nächstes hinging und dann\nzog man weiter seines Weges.",[439,64732,64733],{},"Zum entspannen nutzte dann der Ein oder Andere den Monorail-Express, den kleinen Bummelzug oder so wie wir das Volo da\nVinci. So langsam war jeder schon ziemlich geschafft von der ganzen rumlauferei und dem vielen Achterbahnfahren. Zum\nEnde hin mobilisierten einige nochmal ihre Kräfte und holten sich einen letzten Adrenalinkick in der Blue Fire oder\nSilver-Star.",[439,64735,64736],{},[1002,64737,64740],{"href":64738,"rel":64739},"https://media.synyx.de/uploads//2012/10/europapark_02.jpg",[1006],[2205,64741],{"alt":469,"src":64738,"title":64742},"europapark_02",[439,64744,64745],{},"Treffpunkt war dann um 18.30 Uhr wieder am Bus, um wieder gemeinsam zurück zum Büro zu fahren. Hier merkten wir dann so\nrichtig, wie fertig wir alle waren. Zwar wurden zu Beginn noch rege Erlebnisse ausgetauscht, aber es wurde mit der Zeit\nmerklich ruhiger im Bus und man ertappte so manchen dabei, wie er während der Fahrt ein Nickerchen hielt.",[439,64747,64748],{},"Im Büro angekommen traf man sich dann noch kurz in der Küche, hielt nochmal einen kurzen Plausch über den Ausflug und so\nganz langsam löste sich die Gruppe dann auf. Ziemlich kaputt haben wir uns dann doch gefreut, endlich nach Hause zu\nkommen und nur noch alle Viere von sich zu Strecken.",[439,64750,64751],{},"Fazit: Es war ein gelungener Ausflug, an den wir uns gerne Erinnern. Vielen Dank an unsere Chefs, die uns den schönen\nTag ermöglicht haben. Auch wenn man im Europa Park nicht ständig mit allen etwas gemeinsam gemacht hat, war es doch so,\ndass es das Gemeinschaftsgefühl gestärkt hat. Wir freuen uns schon auf den nächsten Betriebsausflug und sind gespannt\nwohin es dann geht 🙂",{"title":469,"searchDepth":507,"depth":507,"links":64753},[],[1031],"2012-10-01T16:35:30","https://synyx.de/blog/synyx-goes-to-europa-park/",{},"/blog/synyx-goes-to-europa-park",{"title":64691,"description":64700},"blog/synyx-goes-to-europa-park",[64762,64763,64764,64765,389],"achterbahn","betriebsausflug","europa-park","rust","Freitag früh, um kurz nach 7 in Karlsruhe… Noch ziemlich verschlafen und müde schlagen die ersten Mitarbeiter im synyx-Büro auf. Eine fast unmenschliche Zeit für unsere Verhältnisse, denn normalerweise fangen…","ka_im10F6L4hZBeEo7FsAD6Wn2Y-32FzBnDeBlrbAiA",{"id":64769,"title":64770,"author":64771,"body":64772,"category":65141,"date":65142,"description":65143,"extension":1034,"link":65144,"meta":65145,"navigation":916,"path":65146,"seo":65147,"slug":64776,"stem":65148,"tags":65149,"teaser":65152,"__hash__":65153},"blog/blog/studien-projektarbeit-mit-grails.md","Studien-Projektarbeit mit Grails",[235],{"type":432,"value":64773,"toc":65139},[64774,64777,64780,64783,64786,64789,64798,64807,64812,64826,64835,64838,64847,64850,64869,64872,64875,64884,64887,64890,64899,64902,64911,65008,65011,65014,65061,65064,65074,65077,65086,65089,65099,65134,65137],[435,64775,64770],{"id":64776},"studien-projektarbeit-mit-grails",[439,64778,64779],{},"Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang\nOktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt für\ndas 5te Semester zu bekommen.",[439,64781,64782],{},"Das Gedränge kann durchaus groß sein, da hierbei die goldene Regel gilt: „wer zuerst kommt, malt zuerst“. Zudem ist der\nBegriff ‘passend’ auf den jeweiligen Studenten gemünzt. Manche Studenten suchen nach einem für Ihn möglichst\ninteressanten Thema, manche wollen von ihrem meist-geschätzten Professor betreut werden und wiederum andere wollen\nlieber eine ruhige Kugel schieben. Soweit so gut, nun gibt es allerdings noch eine Alternative zu den aufgeführten\nMöglichkeiten an eine Projektarbeit zu kommen. Der Student überlegt sich ein Thema und spricht einen Professor an, ob\ndieser das ausgewählte Thema betreuen möchte.",[439,64784,64785],{},"Da ich zu Beginn des Semesters noch nichts von der letzten Möglichkeit gewusst hatte und ich ansonsten wenig begeistert\nvon den angebotenen Themen war, musste ich mir etwas einfallen lassen. Nach einem aufschlussreichen Gespräch in der\nFachschaft, wusste ich nun auch von der Möglichkeit ein Projekt direkt mit einem Professor auszuhandeln.",[439,64787,64788],{},"Da ich die „neue“ Welt der dynamischen Sprachen im Java-Umfeld schon immer sehr reizvoll gefunden hatte legte ich mich\nauf das Schreiben einer einfachen Webapplikation mit dem Grails-Framework fest. Die Überlegung dahinter war recht\neinfach, es musste neu und spannend sein (ansonsten würde der betreuende Professor das Projekt abweisen) und ich hatte\nschon länger eine Projektidee, die durch das Studium nun vorangetrieben wurde. Nun ist es aber bekanntermaßen so, dass\nbei einer Studienarbeit der Anteil der Dokumentation und Planung recht hoch sein kann. Dementsprechend wählte ich auch\nkeine brandneue Technologie. Die Dokumentation und die erhältlichen Tutorials würden den Einsteig sicherlich stark\nvereinfachen.",[439,64790,64791,64792,64797],{},"Es stellt sich nun die Frage wie ich überhaupt zum Thema ",[1002,64793,64796],{"href":64794,"rel":64795,"title":64796},"http://grails.org/",[1006],"Grails"," bekommen bin. Die Antwort\nist ziemlich simpel – Mundpropaganda. Ich kann mich noch gut an einen Abend in der Kneipe erinnern. Ich saß mit\nbefreundeten ehemaligen Arbeitskollegen bei einem kühlen Bier und wir debattierten wie so des öfteren über die\nWebentwicklung mit Grails. “Wir können zu dritt in einem Monat das Projekt an dem wir nun schon jahrelang mit einem\nvollen Entwicklerteam schrauben nachbauen“. Mit diesem Satz im Hinterkopf ist mein Studienprojekt entstanden.",[439,64799,64800,64801,64806],{},"Das sollte genug zur Vorgeschichte des Studien-Projekts sein,. In einem kleinen Tutorial zum das\nGrails ",[1002,64802,64805],{"href":64803,"rel":64804},"https://web.archive.org/web/20170106062111/http://www.grails.org:80/plugin/spring-security-core",[1006],"Spring-Security -Plugin","\ns möchte das Plugin-System von Grails demonstrieren:",[439,64808,64809],{},[448,64810,64811],{},"Meine Umgebung:",[994,64813,64814,64817,64820,64823],{},[997,64815,64816],{},"Ubuntu 12.04",[997,64818,64819],{},"Eclipse IDE (Groovy/Grails Tool Suite von SpringSource)",[997,64821,64822],{},"Grails 2.1.0",[997,64824,64825],{},"Java 1.6.0_24",[439,64827,64828,64829,64834],{},"Um das Grails-Plugin nutzen zu können, ist es nötig das hierzu\nbenötigte ",[1002,64830,64833],{"href":64803,"rel":64831,"title":64832},[1006],"SpringSecurityPlugin","Plugin","\nzu installieren. Es gibt 2 Möglichkeiten dies zu bewerkstelligen:",[439,64836,64837],{},"Über die Komandozeile:",[464,64839,64841],{"className":16895,"code":64840,"language":16897,"meta":469,"style":469},"grails install-plugin spring-security-core\n",[471,64842,64843],{"__ignoreMap":469},[474,64844,64845],{"class":476,"line":477},[474,64846,64840],{},[439,64848,64849],{},"oder über einen Eintrag in der grails-app/conf/BuildConfig.groovy:",[464,64851,64853],{"className":16895,"code":64852,"language":16897,"meta":469,"style":469},"plugins {\n compile \":spring-security-core:1.2.7.1\"\n}\n",[471,64854,64855,64860,64865],{"__ignoreMap":469},[474,64856,64857],{"class":476,"line":477},[474,64858,64859],{},"plugins {\n",[474,64861,64862],{"class":476,"line":507},[474,64863,64864],{}," compile \":spring-security-core:1.2.7.1\"\n",[474,64866,64867],{"class":476,"line":547},[474,64868,703],{},[439,64870,64871],{},"Da ich die Übersicht der BuildConfig inzwischen zu schätzen weiß, wählte ich die zweite Variante.",[439,64873,64874],{},"Zur Überprüfung/Installation des Plugins muss nun der Befehl",[464,64876,64878],{"className":16895,"code":64877,"language":16897,"meta":469,"style":469},"grails compile\n",[471,64879,64880],{"__ignoreMap":469},[474,64881,64882],{"class":476,"line":477},[474,64883,64877],{},[439,64885,64886],{},"ausgeführt werden. Dieser Befehl löst die eingetragene Plugin-Dependency auf.",[439,64888,64889],{},"Der nächste Schritt nutzt nun gleich ein Skript des installierten Plugin für die Erstellung der nötigen Domain-Klassen.",[464,64891,64893],{"className":16895,"code":64892,"language":16897,"meta":469,"style":469},"grails s2-quickstart \u003CmeinePackageStruktur> User Role\n",[471,64894,64895],{"__ignoreMap":469},[474,64896,64897],{"class":476,"line":477},[474,64898,64892],{},[439,64900,64901],{},"Diese 3 kleinen Befehle reichen für die Grundfunktionalität von SpringSecurity aus.",[439,64903,64904,64905,64910],{},"Zur Überprüfung der Login-Funktionalität macht es Sinn in\nder ",[1002,64906,64909],{"href":64907,"rel":64908,"title":63665},"https://web.archive.org/web/20140413052251/http://grails.org:80/Bootstrap+Classes",[1006],"grails-app/conf/Bootstrap.groovy","\nein paar Testdaten einzufügen:",[464,64912,64914],{"className":28456,"code":64913,"language":28458,"meta":469,"style":469},"class BootStrap{\n def init={servletContext->\n RoleuserRole=Role.findByAuthority('ROLE_USER')?:newRole(authority:'ROLE_USER').save(failOnError:true)\n Role adminRole=Role.findByAuthority('ROLE_ADMIN')?:newRole(authority:'ROLE_ADMIN').save(failOnError:true)\n UseradminUser=User.findByUsername('adminUser')?:newUser(username:'adminUser',enabled:true,password:'topSecret').save(failOnError:true)\n if(!adminUser .authorities.contains(userRole))\n UserRole.create(adminUser,userRole,true)\n if(!adminUser .authorities.contains(adminRole))\n UserRole.create(adminUser,adminRole,true)\n User dummyUser=User.findByUsername('dummyUser')?:newUser(username:'dummyUser',enabled:true,password:'notSoSecret').save(failOnError:true)\n if(!dummyUser.authorities.contains(userRole))\n UserRole.create(dummyUser,userRole,true)\n ...\n }\n assertUser.count()==2\n assertRole.count()==2\n assertUserRole.count()==3\n …\n}\n",[471,64915,64916,64921,64926,64931,64936,64941,64946,64951,64956,64961,64966,64971,64976,64980,64984,64989,64994,64999,65004],{"__ignoreMap":469},[474,64917,64918],{"class":476,"line":477},[474,64919,64920],{},"class BootStrap{\n",[474,64922,64923],{"class":476,"line":507},[474,64924,64925],{}," def init={servletContext->\n",[474,64927,64928],{"class":476,"line":547},[474,64929,64930],{}," RoleuserRole=Role.findByAuthority('ROLE_USER')?:newRole(authority:'ROLE_USER').save(failOnError:true)\n",[474,64932,64933],{"class":476,"line":584},[474,64934,64935],{}," Role adminRole=Role.findByAuthority('ROLE_ADMIN')?:newRole(authority:'ROLE_ADMIN').save(failOnError:true)\n",[474,64937,64938],{"class":476,"line":607},[474,64939,64940],{}," UseradminUser=User.findByUsername('adminUser')?:newUser(username:'adminUser',enabled:true,password:'topSecret').save(failOnError:true)\n",[474,64942,64943],{"class":476,"line":642},[474,64944,64945],{}," if(!adminUser .authorities.contains(userRole))\n",[474,64947,64948],{"class":476,"line":663},[474,64949,64950],{}," UserRole.create(adminUser,userRole,true)\n",[474,64952,64953],{"class":476,"line":694},[474,64954,64955],{}," if(!adminUser .authorities.contains(adminRole))\n",[474,64957,64958],{"class":476,"line":700},[474,64959,64960],{}," UserRole.create(adminUser,adminRole,true)\n",[474,64962,64963],{"class":476,"line":913},[474,64964,64965],{}," User dummyUser=User.findByUsername('dummyUser')?:newUser(username:'dummyUser',enabled:true,password:'notSoSecret').save(failOnError:true)\n",[474,64967,64968],{"class":476,"line":920},[474,64969,64970],{}," if(!dummyUser.authorities.contains(userRole))\n",[474,64972,64973],{"class":476,"line":926},[474,64974,64975],{}," UserRole.create(dummyUser,userRole,true)\n",[474,64977,64978],{"class":476,"line":932},[474,64979,743],{},[474,64981,64982],{"class":476,"line":938},[474,64983,15319],{},[474,64985,64986],{"class":476,"line":944},[474,64987,64988],{}," assertUser.count()==2\n",[474,64990,64991],{"class":476,"line":950},[474,64992,64993],{}," assertRole.count()==2\n",[474,64995,64996],{"class":476,"line":956},[474,64997,64998],{}," assertUserRole.count()==3\n",[474,65000,65001],{"class":476,"line":962},[474,65002,65003],{}," …\n",[474,65005,65006],{"class":476,"line":4876},[474,65007,703],{},[439,65009,65010],{},"Nun kann man beliebige Teile seiner Applikation von SpringSecurity überwachen lassen.",[439,65012,65013],{},"Ob auf Klassenebene/Methodenebene kann einfach per Annotation gesteuert werden.",[464,65015,65017],{"className":28456,"code":65016,"language":28458,"meta":469,"style":469},"import org.springframework.dao.DataIntegrityViolationException\nimport grails.plugins.springsecurity.Secured\n@Secured(['ROLE_USER'])\nclass MyFancyController {\n...\n@Secured(['ROLE_ADMIN'])\ndef mySuperfancyAdminOnlyMethod(){\n...\n}\n",[471,65018,65019,65024,65029,65034,65039,65043,65048,65053,65057],{"__ignoreMap":469},[474,65020,65021],{"class":476,"line":477},[474,65022,65023],{},"import org.springframework.dao.DataIntegrityViolationException\n",[474,65025,65026],{"class":476,"line":507},[474,65027,65028],{},"import grails.plugins.springsecurity.Secured\n",[474,65030,65031],{"class":476,"line":547},[474,65032,65033],{},"@Secured(['ROLE_USER'])\n",[474,65035,65036],{"class":476,"line":584},[474,65037,65038],{},"class MyFancyController {\n",[474,65040,65041],{"class":476,"line":607},[474,65042,14198],{},[474,65044,65045],{"class":476,"line":642},[474,65046,65047],{},"@Secured(['ROLE_ADMIN'])\n",[474,65049,65050],{"class":476,"line":663},[474,65051,65052],{},"def mySuperfancyAdminOnlyMethod(){\n",[474,65054,65055],{"class":476,"line":694},[474,65056,14198],{},[474,65058,65059],{"class":476,"line":700},[474,65060,703],{},[439,65062,65063],{},"Ein kurzer Funktionstest auf der Weboberfläche überzeugt von der Funktionalität.",[439,65065,65066],{},[1002,65067,65070],{"href":65068,"rel":65069},"https://media.synyx.de/uploads//2012/09/Bildschirmfoto-vom-2012-09-10-1801541.png",[1006],[2205,65071],{"alt":469,"src":65072,"title":65073},"https://media.synyx.de/uploads//2012/09/Bildschirmfoto-vom-2012-09-10-1801541-300x168.png","Grails SpringSecurity login screen",[439,65075,65076],{},"Über den Controller:",[464,65078,65080],{"className":16895,"code":65079,"language":16897,"meta":469,"style":469}," localhost:\u003CmyPort>/\u003CmyApp>/dbconsole\n",[471,65081,65082],{"__ignoreMap":469},[474,65083,65084],{"class":476,"line":477},[474,65085,65079],{},[439,65087,65088],{},"lässt sich die Struktur von der Datenhaltung in SpringSecurity leicht nachvollziehen.",[439,65090,65091,65092,65098],{},"Um noch mehr Funktionalität und auch generierte Admin-Oberflächen zu bekommen, ist es möglich\ndas ",[1002,65093,65097],{"href":65094,"rel":65095,"title":65096},"https://web.archive.org/web/20170104185319/http://grails.org/plugin/spring-security-ui",[1006],"Spring Security UI","spring-security-ui","\nplugin zu installieren. Dies stellt eine AJAX fähige Oberfläche zum Login bereit, sowie eine ganzen Haufen von\nAdmin-Funktionalität. Weiterhin gibt es zusätzlich noch einige weitere Plugins rund um das Thema Spring-Security:",[994,65100,65101,65104,65107,65110,65113,65116,65119,65122,65125,65128,65131],{},[997,65102,65103],{},"Spring Security ACL which adds support for object-level and method-level authorization using ACLs (access control\nlist)",[997,65105,65106],{},"Spring Security AppInfo which provides a basic UI to view the security configuration",[997,65108,65109],{},"Spring Security CAS which adds support for single sign-on using Jasig CAS",[997,65111,65112],{},"Spring Security OpenID which adds support for OpenID authentication",[997,65114,65115],{},"Spring Security Facebook which adds support for Facebook authentication",[997,65117,65118],{},"Spring Security Kerberos which adds support for single sign-on using Kerberos",[997,65120,65121],{},"Spring Security LDAP which adds support for LDAP and ActiveDirectory authentication",[997,65123,65124],{},"Spring Security Mock which adds support for fake/mock authentication during developement",[997,65126,65127],{},"Spring Security RADIUS which adds support for RADIUS authentication",[997,65129,65130],{},"Spring Security Shibboleth Native SP which adds support for container provided Shibboleth authentication.",[997,65132,65133],{},"Spring Security Twitter which adds support for Twitter authentication",[439,65135,65136],{},"Dies sollte einen kleine Überblick über den Aufbau und die Installation eines Grails-Plugins im Allgemeinen und von\nspringSecurity im speziellen geben können.",[1024,65138,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":65140},[],[13208],"2012-09-10T18:35:43","Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang\\nOktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt für\\ndas 5te Semester zu bekommen.","https://synyx.de/blog/studien-projektarbeit-mit-grails/",{},"/blog/studien-projektarbeit-mit-grails",{"title":64770,"description":64779},"blog/studien-projektarbeit-mit-grails",[65150,65151,1042],"grails","grailsplugin","Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang Oktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt…","3mIQuXT5j847fSTIlvFUF7wremH2fb2AdmQl9tncaFc",{"id":65155,"title":65156,"author":65157,"body":65158,"category":65233,"date":65234,"description":65235,"extension":1034,"link":65236,"meta":65237,"navigation":916,"path":65238,"seo":65239,"slug":65162,"stem":65240,"tags":65241,"teaser":65243,"__hash__":65244},"blog/blog/froscon-7-resumee.md","FrOSCon 7 – resumee",[314],{"type":432,"value":65159,"toc":65231},[65160,65163,65166,65169,65181,65184,65187,65190,65193,65196,65199,65202,65205,65207,65210,65213,65216,65219],[435,65161,65156],{"id":65162},"froscon-7-resumee",[439,65164,65165],{},"Die 7. “Free an Opensource Software Conference” fand zwar schon letztes Wochenende in St. Augustin (nahe Bonn) statt,\naber darüber zu bloggen lohnt sich dennoch. Denn die FrOSCon ist mit 5€ Eintritt (bzw. 100€ Business-Support) nicht nur\neine der günstigsten Konferenzen, sondern wie ich finde auch eine der lohnenswertesten.",[439,65167,65168],{},"Die Gründe hierfür sind zahlreich, es wird sehr sparsam mit den Worten “business”, “cloud”, “oracle” und insbesondere\n“enterprise” umgegangen. Letzteres eigentlich nur im Bezug auf den Internet Explorer 6 gehört 😉 . Es gab jede Menge\nthemenbezogene Räume und die Tracks “Administration” “Entwicklung” und allgemeine Nerd-Technik waren recht ausgeglichen\nund allesamt interessant. Ein weiteres Alleinstellungsmerkmal der Konferenz ist die Hüpfburg, im Hof der Fachhochschule,\ndie unter Berücksichtigung der Regel “kleine Nerds vor großen” nicht nur für Kinder ein Spass ist.",[439,65170,65171,65172],{},"In Scheu davor Samstags so früh mit dem Zug los zu fahren, haben wir uns bereits Freitag Abend mit genügend\nReiseproviant (Beck`s) auf den Weg nach Bonn gemacht, vorweg: Es bot uns keinen Vorteil im Bezug auf pünktliches\nErscheinen am Samstag, zum 1.\nKonferenz-Tag.",[1002,65173,65176],{"href":65174,"rel":65175},"https://media.synyx.de/uploads//2012/09/2012-08-26-00.51.26.jpg",[1006],[2205,65177],{"alt":65178,"src":65179,"title":65180},"FrOSConized","https://media.synyx.de/uploads//2012/09/2012-08-26-00.51.26-1024x768.jpg","Haltestelle St. Augustin",[439,65182,65183],{},"Nach der Ankunft in unserem Hotel über der Kneipe “Ewige Lampe”, in der das Licht eigentlich öfter aus war als in jeder\nanderen Lokalität in Bonn, wurde uns das Bönnscher Brauhaus empfohlen und dort konnte man auch in der Tat lecker Essen\nund selbst gebrautes “Bönnsch” geniessen, dem selbstgebrannten Schnaps “Bönnsch Kürsch” stehe ich aber nach wie vor\nzwiegespalten gegenüber.",[439,65185,65186],{},"Mit dem 0. Konferenztag in den Knochen, und Kamillentee im Blut, ging es dann fast vollständig, auf zur FH in St.\nAugustin. Für die Administration typisch, jegliche technische Infrastruktur zu nutzen, begab sich diese erstmal mit den\nmitgebrachten DECT-Telefonen an die eventphone-Basis und liessen sich in die gespannte “DECT-Blase” einbuchen.",[439,65188,65189],{},"Sternförmiges Ausschwärmen in die verschiedenen Vortragssäle.",[439,65191,65192],{},"Unter anderem durfte ich “Monty” lauschen, der erzählte was in der MariaDB-Entwicklung so geht und was man in den\nnächsten Versionen erwarten darf.",[439,65194,65195],{},"Zusammengefunden haben sich alle, Entwickler wie Admins, zum letzten und imho besten Vortrag des Tages “Linux-Versteher\ndank strace” von Harald König, aus dem auch noch auf dem sehr entspannten Social-Event im Hof, oft zitiert wurde.\nHarald hat einfach einen köstlichen Humor.",[439,65197,65198],{},"Köllsch.",[439,65200,65201],{},"Zum 2. Konferenztag nahmen wir uns, trotz des etwas längerem Vorabend, vor uns pünktlich im Frühstückssaal des\nBeethoven-Hotels zusammen zu finden und wundersamer Weise funktionierte das diesmal sogar. Checkout…",[439,65203,65204],{},"Wieder in der, bereits sehr heimelich angemuteten FH, viel Kaffee – Opensource-Kaffee, um genau zu sein.",[439,65206,65189],{},[439,65208,65209],{},"Auch der Sonntag war mit einem sehr guten Vortragsprogramm gesegnet.",[439,65211,65212],{},"Aus “git-goodies” konnten wir trotz routiniertem Umgang mit git doch einiges mitnehmen.",[439,65214,65215],{},"Weitere highlights waren unter anderem “check_mk” von Jens Link, mit dem man Nagios/Icinga wohl wesentlich einfacher\nkonfigurieren und die Unzulänglichkeiten von NRPE umgehen kann,",[439,65217,65218],{},"so wie Erkan Yanar, der mit Kugelschreiber-Skizzen auf der Rückseite des Blocks den man beim Eintritt der Froscon im\nBegrüßungsbeutel findet, in seiner Präsentation aufwartete und die Vorteile von LX-Containern erläuterte.",[439,65220,65221,65222],{},"Mit einer Menge Eindrücken, Ideen, Anekdoten und dem Plan, nächstes Jahr wieder dabei zu sein, im Gepäck ging es nach\neinem gelungenen Wochenende wieder auf den Weg nach\nKarlsruhe. ",[1002,65223,65226],{"href":65224,"rel":65225},"https://media.synyx.de/uploads//2012/09/2012-08-26-19.13.53-e1346672801416.jpg",[1006],[2205,65227],{"alt":65228,"src":65229,"title":65230},"Rainbow_home","https://media.synyx.de/uploads//2012/09/2012-08-26-19.13.53-e1346672801416-225x300.jpg","Rainbow",{"title":469,"searchDepth":507,"depth":507,"links":65232},[],[1412],"2012-09-03T14:09:21","Die 7. “Free an Opensource Software Conference” fand zwar schon letztes Wochenende in St. Augustin (nahe Bonn) statt,\\naber darüber zu bloggen lohnt sich dennoch. Denn die FrOSCon ist mit 5€ Eintritt (bzw. 100€ Business-Support) nicht nur\\neine der günstigsten Konferenzen, sondern wie ich finde auch eine der lohnenswertesten.","https://synyx.de/blog/froscon-7-resumee/",{},"/blog/froscon-7-resumee",{"title":65156,"description":65165},"blog/froscon-7-resumee",[8276,65242,9556,20503,26636],"froscon","Die 7. “Free an Opensource Software Conference” fand zwar schon letztes Wochenende in St. Augustin (nahe Bonn) statt, aber darüber zu bloggen lohnt sich dennoch. Denn die FrOSCon ist mit…","enmuWAjwrmJooyl2938zm6IL7FHdeLg4Lc5kHyQ3K_U",{"id":65246,"title":65247,"author":65248,"body":65249,"category":65565,"date":65566,"description":65567,"extension":1034,"link":65568,"meta":65569,"navigation":916,"path":65570,"seo":65571,"slug":65253,"stem":65572,"tags":65573,"teaser":65577,"__hash__":65578},"blog/blog/big-jasper-reports-with-custom-xml-datasource.md","Big Jasper Reports with custom XML datasource",[383],{"type":432,"value":65250,"toc":65561},[65251,65254,65257,65268,65271,65282,65286,65294,65298,65301,65330,65334,65341,65355,65358,65363,65370,65375,65382,65388,65505,65509,65516,65525,65529,65532,65538,65541,65544,65547,65550,65553,65556,65559],[435,65252,65247],{"id":65253},"big-jasper-reports-with-custom-xml-datasource",[439,65255,65256],{},"While working on one of my projects we were faced with the problem of creating a report with a big amount of data to\nshow on multiple Excel tabs (about 50000 entries grouped by different criteria’s). We had a couple of requirements that\nlead us to choose Jasper Reports as our report generation engine. Other requirements lead us to use XML as data source –\ne.g. to generate the report on the fly without wasting hard disk space for different languages.",[439,65258,65259,65260,65263,65264,65267],{},"Really quick we figured out that the default XML data source implementation with XPath does not work out for us, which\nwas not really a big surprise in regard of how XPath is working – the first test has been terminated after about an hour\nwhile Jasper Reports was still generating the report. The Jasper Report documentation is referring to implement a custom\ndata source based on a SAX parser to solve these kind of performance problems. But this is not completely right because\nwhat you really wanna use is a ",[448,65261,65262],{},"SAXPullParser"," implementation in order to control which is the current parsed tag\nrather than being called by the ",[448,65265,65266],{},"SAXParser"," itself.",[439,65269,65270],{},"So what are the steps to use a SAXPullParser as part of a custom data source:",[8310,65272,65273,65276,65279],{},[997,65274,65275],{},"Create the XML raw data format",[997,65277,65278],{},"Write a custom data source",[997,65280,65281],{},"Incorporate the data source into the report generation",[1065,65283,65285],{"id":65284},"sample-project","Sample project",[439,65287,22944,65288,65293],{},[1002,65289,65292],{"href":65290,"rel":65291},"https://media.synyx.de/uploads//2012/08/jasperreports.zip",[1006],"attached sample project"," shows a complete sample\nimplementation of a custom data source and a SAXPullParser. The XML raw data is already created and part of the test\nresources.",[1633,65295,65297],{"id":65296},"the-xml-format","The XML format",[439,65299,65300],{},"The XML format is very simple and represents e.g. all Persons of a company:",[464,65302,65304],{"className":6253,"code":65303,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003CPersons>\n \u003CPerson firstname=\"First\" lastname=\"Last\" mail=\"first.last@test.test\" title=\"nice guy\" age=\"47\" status=\"married\"/>\n ...\n\u003C/Persons>\n",[471,65305,65306,65311,65316,65321,65325],{"__ignoreMap":469},[474,65307,65308],{"class":476,"line":477},[474,65309,65310],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[474,65312,65313],{"class":476,"line":507},[474,65314,65315],{},"\u003CPersons>\n",[474,65317,65318],{"class":476,"line":547},[474,65319,65320],{}," \u003CPerson firstname=\"First\" lastname=\"Last\" mail=\"first.last@test.test\" title=\"nice guy\" age=\"47\" status=\"married\"/>\n",[474,65322,65323],{"class":476,"line":584},[474,65324,743],{},[474,65326,65327],{"class":476,"line":607},[474,65328,65329],{},"\u003C/Persons>\n",[1633,65331,65333],{"id":65332},"the-custom-data-source","The custom data source",[439,65335,65336,65337,65340],{},"The custom data source extends the ",[448,65338,65339],{},"JRAbstractTextDataSource"," and therefore the sample implementation has to override\nthe methods",[994,65342,65343,65349],{},[997,65344,65345,65348],{},[990,65346,65347],{},"boolean next():"," Determines whether or not there is another row to display",[997,65350,65351,65354],{},[990,65352,65353],{},"Object getFieldValue(JRField field)",": Requests the value for the given field/cell",[439,65356,65357],{},"It simply implements the Iterator pattern in order to render all columns of the report.",[439,65359,65360],{},[448,65361,65362],{},"Initialization",[439,65364,65365,65366,65369],{},"The constructor of the class expects an Inputstream that represents the XML source. Based on that stream the class\ninitializes a ",[448,65367,65368],{},"XMLStreamReader"," which is basically an iterator over the XML tags.",[439,65371,65372],{},[448,65373,65374],{},"boolean next()",[439,65376,65377,65378,65381],{},"The implementation of the ",[990,65379,65380],{},"next()"," method initially iterates over the XML tags till it reaches the first Person tag and\nstops at this point. Unfortunately this means that the custom implementation contains knowledge about how the XML is\nstructured and makes it very hard to reuse.",[439,65383,65384,65385,65387],{},"Every subsequent call to the ",[990,65386,65380],{}," method sets the current pointer to the next Person element and returns true, until\nthe Persons tag has been reached or the end of the document appeared.",[464,65389,65391],{"className":709,"code":65390,"language":711,"meta":469,"style":469}," int eventType = xmlStreamReader.getEventType();\n String tagName = null;\n boolean isStart = false;\n while (xmlStreamReader.hasNext()) {\n eventType = xmlStreamReader.next();\n switch (eventType) {\n case XMLEvent.START_ELEMENT:\n isStart = true;\n case XMLEvent.END_ELEMENT:\n tagName = xmlStreamReader.getLocalName();\n // check if there is still a person element left\n if (isStart && PERSON_TAG_NAME.equals(tagName)) {\n return true;\n } else if (!isStart && PERSONS_TAG_NAME.equals(tagName)) {\n // end tag of persons, nothing else to handle\n return false;\n }\n break;\n case XMLEvent.END_DOCUMENT:\n return false;\n }\n isStart = false;\n }\n",[471,65392,65393,65398,65403,65408,65413,65418,65423,65428,65433,65438,65443,65448,65453,65458,65463,65468,65473,65477,65482,65487,65492,65496,65501],{"__ignoreMap":469},[474,65394,65395],{"class":476,"line":477},[474,65396,65397],{}," int eventType = xmlStreamReader.getEventType();\n",[474,65399,65400],{"class":476,"line":507},[474,65401,65402],{}," String tagName = null;\n",[474,65404,65405],{"class":476,"line":547},[474,65406,65407],{}," boolean isStart = false;\n",[474,65409,65410],{"class":476,"line":584},[474,65411,65412],{}," while (xmlStreamReader.hasNext()) {\n",[474,65414,65415],{"class":476,"line":607},[474,65416,65417],{}," eventType = xmlStreamReader.next();\n",[474,65419,65420],{"class":476,"line":642},[474,65421,65422],{}," switch (eventType) {\n",[474,65424,65425],{"class":476,"line":663},[474,65426,65427],{}," case XMLEvent.START_ELEMENT:\n",[474,65429,65430],{"class":476,"line":694},[474,65431,65432],{}," isStart = true;\n",[474,65434,65435],{"class":476,"line":700},[474,65436,65437],{}," case XMLEvent.END_ELEMENT:\n",[474,65439,65440],{"class":476,"line":913},[474,65441,65442],{}," tagName = xmlStreamReader.getLocalName();\n",[474,65444,65445],{"class":476,"line":920},[474,65446,65447],{}," // check if there is still a person element left\n",[474,65449,65450],{"class":476,"line":926},[474,65451,65452],{}," if (isStart && PERSON_TAG_NAME.equals(tagName)) {\n",[474,65454,65455],{"class":476,"line":932},[474,65456,65457],{}," return true;\n",[474,65459,65460],{"class":476,"line":938},[474,65461,65462],{}," } else if (!isStart && PERSONS_TAG_NAME.equals(tagName)) {\n",[474,65464,65465],{"class":476,"line":944},[474,65466,65467],{}," // end tag of persons, nothing else to handle\n",[474,65469,65470],{"class":476,"line":950},[474,65471,65472],{}," return false;\n",[474,65474,65475],{"class":476,"line":956},[474,65476,5704],{},[474,65478,65479],{"class":476,"line":962},[474,65480,65481],{}," break;\n",[474,65483,65484],{"class":476,"line":4876},[474,65485,65486],{}," case XMLEvent.END_DOCUMENT:\n",[474,65488,65489],{"class":476,"line":4888},[474,65490,65491],{}," return false;\n",[474,65493,65494],{"class":476,"line":4900},[474,65495,30422],{},[474,65497,65498],{"class":476,"line":4913},[474,65499,65500],{}," isStart = false;\n",[474,65502,65503],{"class":476,"line":4921},[474,65504,15319],{},[439,65506,65507],{},[448,65508,65353],{},[439,65510,65511,65512,65515],{},"The implementation of",[990,65513,65514],{},"getFieldValue(JRField field)"," is very simple, because the attribute name is exactly the name that\nis assigned in the Jasper Report document.",[464,65517,65519],{"className":709,"code":65518,"language":711,"meta":469,"style":469},"return xmlStreamReader.getAttributeValue(null, field.getName());\n",[471,65520,65521],{"__ignoreMap":469},[474,65522,65523],{"class":476,"line":477},[474,65524,65518],{},[1065,65526,65528],{"id":65527},"brining-all-together","Brining all together",[439,65530,65531],{},"We have now a XML file that contains our test persons and a custom data source that iterates one by one over each\nperson. It is time to see how this improved the report generation time and bringing all pieces together.",[439,65533,22944,65534,65537],{},[448,65535,65536],{},"ReportGenerator"," class accepts the XML source and the template as stream and via an additional flag it is\npossible to switch between the default and the custom data source. A simple Junit test is using this class to run 4\nreport generations and measures the amount of time it needed. Here the result of the Junit test on my local machine:",[439,65539,65540],{},"`Running com.jasperreports.ReportGeneratorTest",[439,65542,65543],{},"INFO ReportGenerator - Created report with default XML data source in 4.136 seconds.",[439,65545,65546],{},"INFO ReportGenerator - Created report with custom datasource in 0.684 seconds.",[439,65548,65549],{},"INFO ReportGenerator - Created report with default XML data source in 21.463 seconds.",[439,65551,65552],{},"INFO ReportGenerator - Created report with custom datasource in 3.495 seconds.",[439,65554,65555],{},"Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 30.267 sec`",[439,65557,65558],{},"The first two results are using a XML source with 100 Persons, the following two are processing 5000 Persons. The custom\ndata source implementation is about 6 times faster than the default implementation and this sample “only” uses only a\nfraction of what we had to process in our project. In fact our worst case scenario mentioned in the beginning is using\n50000 records on multiple tabs and therefore this improvement pays of very fast.",[1024,65560,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":65562},[65563,65564],{"id":65284,"depth":547,"text":65285},{"id":65527,"depth":547,"text":65528},[1412],"2012-08-29T02:40:26","While working on one of my projects we were faced with the problem of creating a report with a big amount of data to\\nshow on multiple Excel tabs (about 50000 entries grouped by different criteria’s). We had a couple of requirements that\\nlead us to choose Jasper Reports as our report generation engine. Other requirements lead us to use XML as data source –\\ne.g. to generate the report on the fly without wasting hard disk space for different languages.","https://synyx.de/blog/big-jasper-reports-with-custom-xml-datasource/",{},"/blog/big-jasper-reports-with-custom-xml-datasource",{"title":65247,"description":65256},"blog/big-jasper-reports-with-custom-xml-datasource",[65574,65575,65576],"custom-datasource","jasper-reports","saxpullparser","While working on one of my projects we were faced with the problem of creating a report with a big amount of data to show on multiple Excel tabs (about…","SYnjblwoql00btIP3Bhd2-4MQoGLcsdaZYWx_o08uoA",{"id":65580,"title":65581,"author":65582,"body":65583,"category":66406,"date":66407,"description":66408,"extension":1034,"link":66409,"meta":66410,"navigation":916,"path":66411,"seo":66412,"slug":65587,"stem":66413,"tags":66414,"teaser":66415,"__hash__":66416},"blog/blog/implementing-acceptance-tests-with-jbehave.md","Implementing acceptance tests with jbehave",[247],{"type":432,"value":65584,"toc":66404},[65585,65588,65591,65594,65597,65604,65607,65746,65760,65892,65909,65953,65956,66124,66127,66130,66270,66280,66374,66402],[435,65586,65581],{"id":65587},"implementing-acceptance-tests-with-jbehave",[439,65589,65590],{},"Producing high quality software in an agile process means that everybody involved in the delivery team (or in other\nwords: the team as a whole) do their best to ensure that each products increment delivered to the customer meets the\nbusiness values of each story that has been implemented.",[439,65592,65593],{},"One strategy to achieve this, is to define executable specifications each reflecting a stories acceptance criteria.\nDoing so allows you to document the story requirements and drive the development of the product – this is what behaviour\ndriven development (BDD) is about. Not only does an executable specification provide a definition-of-done for a given\nstory; in addition, it will serve as a regression test, ensuring the acceptance criteria are fulfilled while the product\nevolves in following sprints.",[439,65595,65596],{},"An important difference between automated acceptance tests and unit- (or component-) tests is that acceptance tests\nare business-facing, i.e. their responsibility is to make sure that each story implementation delivers a certain\nbusiness value to the customer. Ideally, they are written by the customer (with the help of business analysts and/or\ntesters) and are implemented by the testers, for example by pairing with programmers. There are various tools available\nsupporting the creation of automated acceptance tests, most of them providing a DSL (internal or external).",[439,65598,65599,65603],{},[1002,65600,65602],{"href":24790,"rel":65601,"title":65602},[1006],"jbehave"," is one of these tools, allowing to define a stories acceptance criteria in the\ncommonly used given-when-then form. The following snippets show how a jbehave acceptance test might look like, how it\nis implemented and integrated into a project. To keep things simple the acceptance test verifies the behaviour of a\ncertain service, directly using the service layers API. In a real project – where the behaviour of an application\nrunning in a test environment should be verified – the acceptance test probably would not be written against the serive\nAPI. Instead, an application driver would be injected into the acceptance test, in order to initialize application state\nand then make requests against the applications REST-API or the GUI (using a window driver component).",[439,65605,65606],{},"So, let’s start by adding the required dependencies and plugins into the POM (at this point, it should be obvious that\nmy demo project is Maven-based):",[464,65608,65610],{"className":16895,"code":65609,"language":16897,"meta":469,"style":469},"...\n\u003CmodelVersion>4.0.0\u003C/modelVersion>\n\u003CgroupId>dyn-ip\u003C/groupId>\n\u003CartifactId>dyn-ip\u003C/artifactId>\n\u003Cversion>1.0-SNAPSHOT\u003C/version>\n\u003Cpackaging>war\u003C/packaging>\n\u003Cname>DynIP\u003C/name>\n...\n\u003Cdependency>\n \u003CgroupId>junit\u003C/groupId>\n \u003CartifactId>junit\u003C/artifactId>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n\u003C!-- enables jbehave acceptance tests -->\n\u003Cdependency>\n \u003CgroupId>org.jbehave\u003C/groupId>\n \u003CartifactId>jbehave-core\u003C/artifactId>\n \u003Cversion>3.6.8\u003C/version>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n\u003C!-- adds dependency injection support\n using the Weld CDI container -->\n\u003Cdependency>\n \u003CgroupId>org.jbehave\u003C/groupId>\n \u003CartifactId>jbehave-weld\u003C/artifactId>\n \u003Cversion>3.6.8\u003C/version>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n...\n",[471,65611,65612,65616,65621,65626,65631,65636,65641,65646,65650,65655,65660,65665,65670,65675,65680,65684,65689,65694,65699,65703,65707,65712,65717,65721,65725,65730,65734,65738,65742],{"__ignoreMap":469},[474,65613,65614],{"class":476,"line":477},[474,65615,14198],{},[474,65617,65618],{"class":476,"line":507},[474,65619,65620],{},"\u003CmodelVersion>4.0.0\u003C/modelVersion>\n",[474,65622,65623],{"class":476,"line":547},[474,65624,65625],{},"\u003CgroupId>dyn-ip\u003C/groupId>\n",[474,65627,65628],{"class":476,"line":584},[474,65629,65630],{},"\u003CartifactId>dyn-ip\u003C/artifactId>\n",[474,65632,65633],{"class":476,"line":607},[474,65634,65635],{},"\u003Cversion>1.0-SNAPSHOT\u003C/version>\n",[474,65637,65638],{"class":476,"line":642},[474,65639,65640],{},"\u003Cpackaging>war\u003C/packaging>\n",[474,65642,65643],{"class":476,"line":663},[474,65644,65645],{},"\u003Cname>DynIP\u003C/name>\n",[474,65647,65648],{"class":476,"line":694},[474,65649,14198],{},[474,65651,65652],{"class":476,"line":700},[474,65653,65654],{},"\u003Cdependency>\n",[474,65656,65657],{"class":476,"line":913},[474,65658,65659],{}," \u003CgroupId>junit\u003C/groupId>\n",[474,65661,65662],{"class":476,"line":920},[474,65663,65664],{}," \u003CartifactId>junit\u003C/artifactId>\n",[474,65666,65667],{"class":476,"line":926},[474,65668,65669],{}," \u003Cscope>test\u003C/scope>\n",[474,65671,65672],{"class":476,"line":932},[474,65673,65674],{},"\u003C/dependency>\n",[474,65676,65677],{"class":476,"line":938},[474,65678,65679],{},"\u003C!-- enables jbehave acceptance tests -->\n",[474,65681,65682],{"class":476,"line":944},[474,65683,65654],{},[474,65685,65686],{"class":476,"line":950},[474,65687,65688],{}," \u003CgroupId>org.jbehave\u003C/groupId>\n",[474,65690,65691],{"class":476,"line":956},[474,65692,65693],{}," \u003CartifactId>jbehave-core\u003C/artifactId>\n",[474,65695,65696],{"class":476,"line":962},[474,65697,65698],{}," \u003Cversion>3.6.8\u003C/version>\n",[474,65700,65701],{"class":476,"line":4876},[474,65702,65669],{},[474,65704,65705],{"class":476,"line":4888},[474,65706,65674],{},[474,65708,65709],{"class":476,"line":4900},[474,65710,65711],{},"\u003C!-- adds dependency injection support\n",[474,65713,65714],{"class":476,"line":4913},[474,65715,65716],{}," using the Weld CDI container -->\n",[474,65718,65719],{"class":476,"line":4921},[474,65720,65654],{},[474,65722,65723],{"class":476,"line":4932},[474,65724,65688],{},[474,65726,65727],{"class":476,"line":4938},[474,65728,65729],{}," \u003CartifactId>jbehave-weld\u003C/artifactId>\n",[474,65731,65732],{"class":476,"line":4946},[474,65733,65698],{},[474,65735,65736],{"class":476,"line":4952},[474,65737,65669],{},[474,65739,65740],{"class":476,"line":4957},[474,65741,65674],{},[474,65743,65744],{"class":476,"line":4969},[474,65745,14198],{},[439,65747,22944,65748,65751,65752,65755,65756,65759],{},[471,65749,65750],{},"jbehave-weld"," dependency allows to inject dependencies into our Steps classes (Steps and Stories are shown below).\nFinally, the ",[471,65753,65754],{},"jbehave-maven-plugin"," is included into the POM and configured to match and execute Stories classes at the\nintegration-test phase using the plugins ",[471,65757,65758],{},"run-stories-with-annotated-embedder"," goal:",[464,65761,65763],{"className":16895,"code":65762,"language":16897,"meta":469,"style":469},"...\n\u003Cplugin>\n \u003CgroupId>org.jbehave\u003C/groupId>\n \u003CartifactId>jbehave-maven-plugin\u003C/artifactId>\n \u003Cversion>3.6.8\u003C/version>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cphase>integration-test\u003C/phase>\n \u003Cconfiguration>\n \u003Cincludes>\n \u003Cinclude>**/*Stories.java\u003C/include>\n \u003C/includes>\n \u003Cscope>test\u003C/scope>\n \u003CtestSourceDirectory>src/integrationtest/java\u003C/testSourceDirectory>\n \u003C/configuration>\n \u003Cgoals>\n \u003Cgoal>run-stories-with-annotated-embedder\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n \u003Cdependencies>\n \u003Cdependency>\n \u003CgroupId>org.jbehave\u003C/groupId>\n \u003CartifactId>jbehave-weld\u003C/artifactId>\n \u003Cversion>3.6.8\u003C/version>\n \u003C/dependency>\n \u003C/dependencies>\n\u003C/plugin>\n...\n",[471,65764,65765,65769,65773,65777,65782,65786,65790,65794,65799,65803,65808,65813,65818,65823,65828,65832,65836,65841,65845,65849,65853,65857,65861,65866,65871,65876,65880,65884,65888],{"__ignoreMap":469},[474,65766,65767],{"class":476,"line":477},[474,65768,14198],{},[474,65770,65771],{"class":476,"line":507},[474,65772,44754],{},[474,65774,65775],{"class":476,"line":547},[474,65776,65688],{},[474,65778,65779],{"class":476,"line":584},[474,65780,65781],{}," \u003CartifactId>jbehave-maven-plugin\u003C/artifactId>\n",[474,65783,65784],{"class":476,"line":607},[474,65785,65698],{},[474,65787,65788],{"class":476,"line":642},[474,65789,44794],{},[474,65791,65792],{"class":476,"line":663},[474,65793,44799],{},[474,65795,65796],{"class":476,"line":694},[474,65797,65798],{}," \u003Cphase>integration-test\u003C/phase>\n",[474,65800,65801],{"class":476,"line":700},[474,65802,54045],{},[474,65804,65805],{"class":476,"line":913},[474,65806,65807],{}," \u003Cincludes>\n",[474,65809,65810],{"class":476,"line":920},[474,65811,65812],{}," \u003Cinclude>**/*Stories.java\u003C/include>\n",[474,65814,65815],{"class":476,"line":926},[474,65816,65817],{}," \u003C/includes>\n",[474,65819,65820],{"class":476,"line":932},[474,65821,65822],{}," \u003Cscope>test\u003C/scope>\n",[474,65824,65825],{"class":476,"line":938},[474,65826,65827],{}," \u003CtestSourceDirectory>src/integrationtest/java\u003C/testSourceDirectory>\n",[474,65829,65830],{"class":476,"line":944},[474,65831,54070],{},[474,65833,65834],{"class":476,"line":950},[474,65835,44809],{},[474,65837,65838],{"class":476,"line":956},[474,65839,65840],{}," \u003Cgoal>run-stories-with-annotated-embedder\u003C/goal>\n",[474,65842,65843],{"class":476,"line":962},[474,65844,44819],{},[474,65846,65847],{"class":476,"line":4876},[474,65848,44824],{},[474,65850,65851],{"class":476,"line":4888},[474,65852,44829],{},[474,65854,65855],{"class":476,"line":4900},[474,65856,45076],{},[474,65858,65859],{"class":476,"line":4913},[474,65860,6272],{},[474,65862,65863],{"class":476,"line":4921},[474,65864,65865],{}," \u003CgroupId>org.jbehave\u003C/groupId>\n",[474,65867,65868],{"class":476,"line":4932},[474,65869,65870],{}," \u003CartifactId>jbehave-weld\u003C/artifactId>\n",[474,65872,65873],{"class":476,"line":4938},[474,65874,65875],{}," \u003Cversion>3.6.8\u003C/version>\n",[474,65877,65878],{"class":476,"line":4946},[474,65879,6292],{},[474,65881,65882],{"class":476,"line":4952},[474,65883,45125],{},[474,65885,65886],{"class":476,"line":4957},[474,65887,44834],{},[474,65889,65890],{"class":476,"line":4969},[474,65891,14198],{},[439,65893,65894,65895,7267,65898,65901,65902,65905,65906,6562],{},"Note that in my demo project, all jbehave related classes and resources are located in the ",[471,65896,65897],{},"src/integrationtest/java/",[471,65899,65900],{},"src/integrationtest/resources/"," directories respectively. In order for this to work, you also have to include the\n",[471,65903,65904],{},"build-helper-maven-plugin"," into the POM. In a real project it would probably be better to have a separate project for\nyour acceptance tests. Now, we will define some acceptance criteria stored in the file\n",[471,65907,65908],{},"src/integrationtest/resources/stories/dynip.stories",[464,65910,65912],{"className":16895,"code":65911,"language":16897,"meta":469,"style":469},"Scenario: get an ip address (host unspecified)\nGiven an initialized network service\nWhen no host is specified\nThen the result should be any valid ip address\nScenario: get an ip address for host\nGiven an initialized network service\nWhen the host is localhost\nThen the address should be 127.0.0.1\n",[471,65913,65914,65919,65924,65929,65934,65939,65943,65948],{"__ignoreMap":469},[474,65915,65916],{"class":476,"line":477},[474,65917,65918],{},"Scenario: get an ip address (host unspecified)\n",[474,65920,65921],{"class":476,"line":507},[474,65922,65923],{},"Given an initialized network service\n",[474,65925,65926],{"class":476,"line":547},[474,65927,65928],{},"When no host is specified\n",[474,65930,65931],{"class":476,"line":584},[474,65932,65933],{},"Then the result should be any valid ip address\n",[474,65935,65936],{"class":476,"line":607},[474,65937,65938],{},"Scenario: get an ip address for host\n",[474,65940,65941],{"class":476,"line":642},[474,65942,65923],{},[474,65944,65945],{"class":476,"line":663},[474,65946,65947],{},"When the host is localhost\n",[474,65949,65950],{"class":476,"line":694},[474,65951,65952],{},"Then the address should be 127.0.0.1\n",[439,65954,65955],{},"As you can see, the acceptance test is about verifying the behaviour of some network service. Here, the acceptance\ncriteria for two scenarios are written down in a given-when-then form. Next, we create an implementation of the\nscenario steps in a Steps class (which is just a POJO):",[464,65957,65959],{"className":709,"code":65958,"language":711,"meta":469,"style":469},"@WeldStep\n@Singleton\npublic class DynIpSteps {\nprivate static ValidatorFactory validatorFactory;\n static {\n validatorFactory = buildDefaultValidatorFactory();\n }\n @Inject\n private NetworkService networkService;\n private String host;\n @Given(\"an initialized network service\")\n public void anInitializedNetworkService() {\n }\n @When(\"no host is specified\")\n public void whenNoHostIsSpecified() {\n this.host = null;\n }\n @Then(\"the result should be any valid ip address\")\n public void thenTheResultShouldBeAnyValidIpAddress() {\n Validator validator = validatorFactory.getValidator();\n Set\u003CConstraintViolation\u003CIpv4Address>> violations =\n validator.validate(networkService.getIpv4Address());\n assertThat(violations.size(), is(0));\n }\n @When(\"the host is $host\")\n public void whenTheHostIs$Host(String host) {\n this.host = host;\n }\n @Then(\"the address should be $address\")\n public void thenTheAddressShouldBe$Address(String address) {\n Ipv4Address ip = networkService.getIpv4AddressForHost(host);\n assertThat(ip.getTextualRepresentation(), is(address));\n }\n}\n",[471,65960,65961,65966,65971,65976,65981,65986,65991,65995,66000,66005,66010,66015,66020,66024,66029,66034,66039,66043,66048,66053,66058,66063,66068,66073,66077,66082,66087,66092,66096,66101,66106,66111,66116,66120],{"__ignoreMap":469},[474,65962,65963],{"class":476,"line":477},[474,65964,65965],{},"@WeldStep\n",[474,65967,65968],{"class":476,"line":507},[474,65969,65970],{},"@Singleton\n",[474,65972,65973],{"class":476,"line":547},[474,65974,65975],{},"public class DynIpSteps {\n",[474,65977,65978],{"class":476,"line":584},[474,65979,65980],{},"private static ValidatorFactory validatorFactory;\n",[474,65982,65983],{"class":476,"line":607},[474,65984,65985],{}," static {\n",[474,65987,65988],{"class":476,"line":642},[474,65989,65990],{}," validatorFactory = buildDefaultValidatorFactory();\n",[474,65992,65993],{"class":476,"line":663},[474,65994,15319],{},[474,65996,65997],{"class":476,"line":694},[474,65998,65999],{}," @Inject\n",[474,66001,66002],{"class":476,"line":700},[474,66003,66004],{}," private NetworkService networkService;\n",[474,66006,66007],{"class":476,"line":913},[474,66008,66009],{}," private String host;\n",[474,66011,66012],{"class":476,"line":920},[474,66013,66014],{}," @Given(\"an initialized network service\")\n",[474,66016,66017],{"class":476,"line":926},[474,66018,66019],{}," public void anInitializedNetworkService() {\n",[474,66021,66022],{"class":476,"line":932},[474,66023,15319],{},[474,66025,66026],{"class":476,"line":938},[474,66027,66028],{}," @When(\"no host is specified\")\n",[474,66030,66031],{"class":476,"line":944},[474,66032,66033],{}," public void whenNoHostIsSpecified() {\n",[474,66035,66036],{"class":476,"line":950},[474,66037,66038],{}," this.host = null;\n",[474,66040,66041],{"class":476,"line":956},[474,66042,15319],{},[474,66044,66045],{"class":476,"line":962},[474,66046,66047],{}," @Then(\"the result should be any valid ip address\")\n",[474,66049,66050],{"class":476,"line":4876},[474,66051,66052],{}," public void thenTheResultShouldBeAnyValidIpAddress() {\n",[474,66054,66055],{"class":476,"line":4888},[474,66056,66057],{}," Validator validator = validatorFactory.getValidator();\n",[474,66059,66060],{"class":476,"line":4900},[474,66061,66062],{}," Set\u003CConstraintViolation\u003CIpv4Address>> violations =\n",[474,66064,66065],{"class":476,"line":4913},[474,66066,66067],{}," validator.validate(networkService.getIpv4Address());\n",[474,66069,66070],{"class":476,"line":4921},[474,66071,66072],{}," assertThat(violations.size(), is(0));\n",[474,66074,66075],{"class":476,"line":4932},[474,66076,15319],{},[474,66078,66079],{"class":476,"line":4938},[474,66080,66081],{}," @When(\"the host is $host\")\n",[474,66083,66084],{"class":476,"line":4946},[474,66085,66086],{}," public void whenTheHostIs$Host(String host) {\n",[474,66088,66089],{"class":476,"line":4952},[474,66090,66091],{}," this.host = host;\n",[474,66093,66094],{"class":476,"line":4957},[474,66095,15319],{},[474,66097,66098],{"class":476,"line":4969},[474,66099,66100],{}," @Then(\"the address should be $address\")\n",[474,66102,66103],{"class":476,"line":4990},[474,66104,66105],{}," public void thenTheAddressShouldBe$Address(String address) {\n",[474,66107,66108],{"class":476,"line":5001},[474,66109,66110],{}," Ipv4Address ip = networkService.getIpv4AddressForHost(host);\n",[474,66112,66113],{"class":476,"line":5013},[474,66114,66115],{}," assertThat(ip.getTextualRepresentation(), is(address));\n",[474,66117,66118],{"class":476,"line":5024},[474,66119,15319],{},[474,66121,66122],{"class":476,"line":5035},[474,66123,703],{},[439,66125,66126],{},"While the class level annotations are related to dependency injection, the method level annotations represent the\nvarious steps defined in the stories scenarios.",[439,66128,66129],{},"Along with the Steps class we also implement a Stories class which knows about both, the story files and the Steps\nclasses. In order to reduce boilerplate code, an abstract base class is extended by each Stories class:",[464,66131,66133],{"className":709,"code":66132,"language":711,"meta":469,"style":469},"@UsingSteps(instances = {DynIpSteps.class})\npublic class DynIpStories extends StoriesBase {\n public DynIpStories() {\n super(\"**/*.stories\");\n }\n}\n@RunWith(WeldAnnotatedEmbedderRunner.class)\n@Configure\n@UsingWeld\n@UsingEmbedder(embedder = Embedder.class,\n generateViewAfterStories = true,\n ignoreFailureInStories = true,\n ignoreFailureInView = true)\npublic abstract class StoriesBase extends InjectableEmbedder {\n private final String storiesPath;\n private final URL codeLocation;\n protected StoriesBase(String storiesPath) {\n this.storiesPath = storiesPath;\n this.codeLocation = codeLocationFromClass(this.getClass());\n }\n @Test\n @Override\n public void run() throws Throwable {\n StoryFinder storyFinder = new StoryFinder();\n injectedEmbedder().runStoriesAsPaths(\n storyFinder.findPaths(codeLocation, storiesPath, \"\"));\n }\n}\n",[471,66134,66135,66140,66145,66150,66155,66159,66163,66168,66173,66178,66183,66188,66193,66198,66203,66208,66213,66218,66223,66228,66232,66237,66242,66247,66252,66257,66262,66266],{"__ignoreMap":469},[474,66136,66137],{"class":476,"line":477},[474,66138,66139],{},"@UsingSteps(instances = {DynIpSteps.class})\n",[474,66141,66142],{"class":476,"line":507},[474,66143,66144],{},"public class DynIpStories extends StoriesBase {\n",[474,66146,66147],{"class":476,"line":547},[474,66148,66149],{}," public DynIpStories() {\n",[474,66151,66152],{"class":476,"line":584},[474,66153,66154],{}," super(\"**/*.stories\");\n",[474,66156,66157],{"class":476,"line":607},[474,66158,15319],{},[474,66160,66161],{"class":476,"line":642},[474,66162,703],{},[474,66164,66165],{"class":476,"line":663},[474,66166,66167],{},"@RunWith(WeldAnnotatedEmbedderRunner.class)\n",[474,66169,66170],{"class":476,"line":694},[474,66171,66172],{},"@Configure\n",[474,66174,66175],{"class":476,"line":700},[474,66176,66177],{},"@UsingWeld\n",[474,66179,66180],{"class":476,"line":913},[474,66181,66182],{},"@UsingEmbedder(embedder = Embedder.class,\n",[474,66184,66185],{"class":476,"line":920},[474,66186,66187],{}," generateViewAfterStories = true,\n",[474,66189,66190],{"class":476,"line":926},[474,66191,66192],{}," ignoreFailureInStories = true,\n",[474,66194,66195],{"class":476,"line":932},[474,66196,66197],{}," ignoreFailureInView = true)\n",[474,66199,66200],{"class":476,"line":938},[474,66201,66202],{},"public abstract class StoriesBase extends InjectableEmbedder {\n",[474,66204,66205],{"class":476,"line":944},[474,66206,66207],{}," private final String storiesPath;\n",[474,66209,66210],{"class":476,"line":950},[474,66211,66212],{}," private final URL codeLocation;\n",[474,66214,66215],{"class":476,"line":956},[474,66216,66217],{}," protected StoriesBase(String storiesPath) {\n",[474,66219,66220],{"class":476,"line":962},[474,66221,66222],{}," this.storiesPath = storiesPath;\n",[474,66224,66225],{"class":476,"line":4876},[474,66226,66227],{}," this.codeLocation = codeLocationFromClass(this.getClass());\n",[474,66229,66230],{"class":476,"line":4888},[474,66231,15319],{},[474,66233,66234],{"class":476,"line":4900},[474,66235,66236],{}," @Test\n",[474,66238,66239],{"class":476,"line":4913},[474,66240,66241],{}," @Override\n",[474,66243,66244],{"class":476,"line":4921},[474,66245,66246],{}," public void run() throws Throwable {\n",[474,66248,66249],{"class":476,"line":4932},[474,66250,66251],{}," StoryFinder storyFinder = new StoryFinder();\n",[474,66253,66254],{"class":476,"line":4938},[474,66255,66256],{}," injectedEmbedder().runStoriesAsPaths(\n",[474,66258,66259],{"class":476,"line":4946},[474,66260,66261],{}," storyFinder.findPaths(codeLocation, storiesPath, \"\"));\n",[474,66263,66264],{"class":476,"line":4952},[474,66265,15319],{},[474,66267,66268],{"class":476,"line":4957},[474,66269,703],{},[439,66271,66272,66273,66275,66276,66279],{},"Since we are using jbehave annotations it’s important to set the goal element to ",[471,66274,65758],{},"\nin the ",[471,66277,66278],{},"jbehave-maven-plugins"," configuration (as shown above). Now, there are only a few configuration related classes\nto be implemented: one class that produces the configuration information and two other classes that relate to story\nloading and report building:",[464,66281,66283],{"className":709,"code":66282,"language":711,"meta":469,"style":469},"@ApplicationScoped\npublic class ConfigurationProducer {\n@Produces @WeldConfiguration\npublic Configuration getConfiguration() {\n return new MostUsefulConfiguration()\n .useStoryLoader(new StoryLoader())\n .useStoryReporterBuilder(new ReportBuilder());\n }\n}\npublic class StoryLoader extends LoadFromClasspath {\n public StoryLoader() {\n super(StoryLoader.class.getClassLoader());\n }\n}\npublic class ReportBuilder extends StoryReporterBuilder {\n public ReportBuilder() {\n withFormats(CONSOLE, TXT, HTML).withDefaultFormats();\n }\n}\n",[471,66284,66285,66290,66295,66300,66305,66310,66315,66320,66324,66328,66333,66338,66343,66347,66351,66356,66361,66366,66370],{"__ignoreMap":469},[474,66286,66287],{"class":476,"line":477},[474,66288,66289],{},"@ApplicationScoped\n",[474,66291,66292],{"class":476,"line":507},[474,66293,66294],{},"public class ConfigurationProducer {\n",[474,66296,66297],{"class":476,"line":547},[474,66298,66299],{},"@Produces @WeldConfiguration\n",[474,66301,66302],{"class":476,"line":584},[474,66303,66304],{},"public Configuration getConfiguration() {\n",[474,66306,66307],{"class":476,"line":607},[474,66308,66309],{}," return new MostUsefulConfiguration()\n",[474,66311,66312],{"class":476,"line":642},[474,66313,66314],{}," .useStoryLoader(new StoryLoader())\n",[474,66316,66317],{"class":476,"line":663},[474,66318,66319],{}," .useStoryReporterBuilder(new ReportBuilder());\n",[474,66321,66322],{"class":476,"line":694},[474,66323,15319],{},[474,66325,66326],{"class":476,"line":700},[474,66327,703],{},[474,66329,66330],{"class":476,"line":913},[474,66331,66332],{},"public class StoryLoader extends LoadFromClasspath {\n",[474,66334,66335],{"class":476,"line":920},[474,66336,66337],{}," public StoryLoader() {\n",[474,66339,66340],{"class":476,"line":926},[474,66341,66342],{}," super(StoryLoader.class.getClassLoader());\n",[474,66344,66345],{"class":476,"line":932},[474,66346,15319],{},[474,66348,66349],{"class":476,"line":938},[474,66350,703],{},[474,66352,66353],{"class":476,"line":944},[474,66354,66355],{},"public class ReportBuilder extends StoryReporterBuilder {\n",[474,66357,66358],{"class":476,"line":950},[474,66359,66360],{}," public ReportBuilder() {\n",[474,66362,66363],{"class":476,"line":956},[474,66364,66365],{}," withFormats(CONSOLE, TXT, HTML).withDefaultFormats();\n",[474,66367,66368],{"class":476,"line":962},[474,66369,15319],{},[474,66371,66372],{"class":476,"line":4876},[474,66373,703],{},[439,66375,66376,66377,66380,66381,520,66384,18175,66387,66390,66391,66394,66395,66398,66399],{},"The Steps class must be annotated with annotation ",[471,66378,66379],{},"javax.inject.Singleton"," (this is because in my demo project I’m using\nthe Weld CDI container which does not default to create singleton scoped beans; having no singleton prevents us from\nholding any state information across multiple calls of the ",[471,66382,66383],{},"@Given",[471,66385,66386],{},"@When",[471,66388,66389],{},"@Then"," annotated methods). For the\ndependency injection mechanism to work, there must also be (an empty) ",[471,66392,66393],{},"beans.xml"," file in both, directory\n",[471,66396,66397],{},"src/integrationtest/resources/META-INF/"," and directory ",[471,66400,66401],{},"src/main/resources/META-INF/",[1024,66403,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":66405},[],[1030],"2012-08-26T17:53:49","Producing high quality software in an agile process means that everybody involved in the delivery team (or in other\\nwords: the team as a whole) do their best to ensure that each products increment delivered to the customer meets the\\nbusiness values of each story that has been implemented.","https://synyx.de/blog/implementing-acceptance-tests-with-jbehave/",{},"/blog/implementing-acceptance-tests-with-jbehave",{"title":65581,"description":65590},"blog/implementing-acceptance-tests-with-jbehave",[24886,65602],"Producing high quality software in an agile process means that everybody involved in the delivery team (or in other words: the team as a whole) do their best to ensure…","48wd0yTGmAyY4Dloi3EOh2Qx9HSxfb5R_I-JR7FeADE",{"id":66418,"title":66419,"author":66420,"body":66421,"category":66474,"date":66475,"description":66476,"extension":1034,"link":66477,"meta":66478,"navigation":916,"path":66479,"seo":66480,"slug":66425,"stem":66481,"tags":66482,"teaser":66483,"__hash__":66484},"blog/blog/sommerfest.md","Sommerfest",[326],{"type":432,"value":66422,"toc":66472},[66423,66426,66429,66440,66443,66453,66456,66459,66462],[435,66424,66419],{"id":66425},"sommerfest",[439,66427,66428],{},"Bei strahlendem Sonnenschein treffen wir uns im Klettergarten im Hardtwald und plündern erstmal unseren Kühlwagen bevor\nes auf die Bäume geht. In kleinen Gruppen werden wir jeweils einem Trainer zugeteilt, der uns den Klettergurt erklärt\nund aufpasst, dass keiner vergisst das Sicherungsseil festzuhalten.",[439,66430,66431,66439],{},[1002,66432,66435],{"href":66433,"rel":66434},"https://media.synyx.de/uploads//2012/08/P10809531-e1345923184341.jpg",[1006],[2205,66436],{"alt":469,"src":66437,"title":66438},"https://media.synyx.de/uploads//2012/08/P10809531-224x300.jpg","The Pole","\nGanz gruppendynamisch halten die Kollegen Seile fest, während ein oder zwei mehr oder weniger Furchtlose den Baumkronen\nentgegen klettern. Das einfachste Klettergerät ist eine sehr lange Leiter, die an Seilen von acht Leuten in der\nSenkrechten gehalten wird. Schwierig wird es an der Stelle, wo der Blick in die Tiefe geht, die Hände nix mehr zum\nfesthalten haben und doch noch ein paar Sprossen übrig sind. Besonders interessant ist das ganze natürlich für\ndiejenigen, die ein bisschen Höhenangst haben. Oder für Lucien, der mit verbundenen Augen über eine löchrige Hängebrücke\nspringt.",[439,66441,66442],{},"Irgendwann finde ich mich auf einer tellergroßen Plattform zwischen den Wipfeln wieder, ohne Geländer. 20 Meter unter\nmir stehen acht Menschen, die zwei Seile festhalten. Wie groß ist also die Wahrscheinlichkeit, das ich heil runter\nkomme? 20 zu zwei Achtel?",[439,66444,66445],{},[1002,66446,66449],{"href":66447,"rel":66448},"https://media.synyx.de/uploads//2012/08/P10900061.jpg",[1006],[2205,66450],{"alt":469,"src":66451,"title":66452},"https://media.synyx.de/uploads//2012/08/P10900061-225x300.jpg","Stairway",[439,66454,66455],{},"Die größte sportliche Herausforderung ist eine “Leiter” aus waagrechten Holzbalken. Die Balken haben einen Abstand von\neineinhalb Metern, so dass man nur mit Hilfe einer Seilschlaufe hochkommt. Nur mit Schlaufe? Ach was, meinen Seb und\nMacmac und wieseln sich einfach so nach oben.",[439,66457,66458],{},"Nach ein paar Stunden hat jeder seine Dosis Adrenalin gehabt und wir wenden uns dem üppigen Grillbüffet zu. Schnell\nwerden noch Öl für den Grill und Eis für den stromlosen Kühlwagen besorgt, dann kann es endlich losgehen. Wir schlemmen\nuns durch Schnitzel, Lammspieße, Maiskolben und andere leckere Sachen und vergessen am Ende fast, den Nachtisch\naufzutischen. Nebenher fliegen die Diabolos, man lernt ehemalige und zukünftige synyxer kennen und auch der Nachwuchs\nhat sich schon angefreundet.",[439,66460,66461],{},"Als der Klettergarten schließlich seine Bänke zusammen räumt, macht sich der harte Kern auf zum Schloss um dort\nweiterzufeiern. Wir stellen fest, dass es recht dunkel sein kann, wenn das Licht aus ist, finden aber doch alle den Weg.",[439,66463,66464],{},[1002,66465,66468],{"href":66466,"rel":66467},"https://media.synyx.de/uploads//2012/08/P10507881.jpg",[1006],[2205,66469],{"alt":469,"src":66470,"title":66471},"https://media.synyx.de/uploads//2012/08/P10507881-1024x768.jpg","Grillen",{"title":469,"searchDepth":507,"depth":507,"links":66473},[],[1031],"2012-08-25T22:53:23","Bei strahlendem Sonnenschein treffen wir uns im Klettergarten im Hardtwald und plündern erstmal unseren Kühlwagen bevor\\nes auf die Bäume geht. In kleinen Gruppen werden wir jeweils einem Trainer zugeteilt, der uns den Klettergurt erklärt\\nund aufpasst, dass keiner vergisst das Sicherungsseil festzuhalten.","https://synyx.de/blog/sommerfest/",{},"/blog/sommerfest",{"title":66419,"description":66428},"blog/sommerfest",[],"Bei strahlendem Sonnenschein treffen wir uns im Klettergarten im Hardtwald und plündern erstmal unseren Kühlwagen bevor es auf die Bäume geht. In kleinen Gruppen werden wir jeweils einem Trainer zugeteilt,…","1midzbYs9FGe4mDgj0sqGcy14Th-vGWYS--pT4ZKYMY",{"id":66486,"title":66487,"author":66488,"body":66489,"category":66594,"date":66595,"description":66596,"extension":1034,"link":66597,"meta":66598,"navigation":916,"path":66599,"seo":66600,"slug":66493,"stem":66601,"tags":66602,"teaser":66605,"__hash__":66606},"blog/blog/meine-ausbildung-bei-synyx-teil-2.md","Meine Ausbildung bei synyx – Teil 2",[190],{"type":432,"value":66490,"toc":66589},[66491,66494,66505,66513,66516,66519,66523,66526,66536,66562,66566,66569,66575,66586],[435,66492,66487],{"id":66493},"meine-ausbildung-bei-synyx-teil-2",[439,66495,66496],{},[990,66497,66498,66499,66504],{},"(Fortsetzung ",[1002,66500,66503],{"href":66501,"rel":66502},"http://blog.synyx.de/2011/11/meine-ausbildung-bei-synyx/",[1006],"meines Blogposts"," von letztem Jahr)",[439,66506,66507,66508,66512],{},"Das dritte Jahr meiner Ausbildung bei synyx spielte sich zum Großteil in der\nAbteilung ",[1002,66509,18566],{"href":66510,"rel":66511},"https://synyx.de/code-clinic-softwareoptimierung/",[1006]," ab, in der ich etliche neue Technologien und\nFrameworks kennengelernt und eingesetzt habe.",[1065,66514,16232],{"id":66515},"rückblick",[439,66517,66518],{},"Bei meinen bisherigen Projekten war es so, dass man den Code zu einem guten Teil kannte, weil man an vielen Stellen\ndavon selbst mitentwickelt hat. Die Technologien waren auch oftmals bekannt, und wenn nicht, hatte man sich kurz bei\nKollegen erkundigt, was für das Problem passt, oder aus eigener Erfahrung ein passendes Framework ausgesucht und es\neingesetzt. Die Anwendungen liefen meist auf nur einem Server, auf dem auch die Datenbank war, oder mit externem\nDatenbankserver, was aber kaum Mehraufwand beim Planen und Umsetzen bedeutet. Fachwissen benötigte man nur wenig für die\nUmsetzung der Anwendungen und was man davon benötigte, das hatte man schnell abgeklärt.",[1065,66520,66522],{"id":66521},"willkommen-in-der-enterprise-welt","Willkommen in der Enterprise Welt!",[439,66524,66525],{},"Nicht so in der Code Clinic.",[439,66527,66528,66535],{},[1002,66529,66532],{"href":66530,"rel":66531},"https://media.synyx.de/uploads//2012/07/code_clinic.jpg",[1006],[2205,66533],{"alt":469,"src":66530,"title":66534},"code_clinic","\nHier galt es sich erst einmal in ein sehr großes Projekt mit einiger alten Technik einzuarbeiten, um darin Änderungen\nvorzunehmen. Danach teilweise aber auch größere Teile dieses Projektes Stück für Stück mit neuer Technologie und\nbesserem Code zu ersetzen, damit diese auf die Dauer besser wartbar wird und zuverlässiger und performanter arbeitet.\nAlles auf einmal zu ersetzen war hierbei keine Option, da dies viel zu lange dauern würde und einige Stellen dringender\nwaren, als andere. Dabei war ein sehr ins Gewicht fallender Unterschied zu allen bisherigen Projekten der große Anteil\nder Analyse und Planung am Gesamtaufwand. Man muss sich zuerst einmal in den alten Code einarbeiten, verstehen, was\ndarin gemacht wird, Analysieren, von wo dieser Code in welcher Form verwendet wird, usw. bevor man damit anfängt, sich\nzu überlegen, wie man das besser machen kann, welche Technologien man dafür einsetzt und auch sicherstellt, dass dies\nauch im eingesetzten Projektumfeld funktioniert. Auch wird einiges an fachlichem Wissen für das Entwickeln benötigt. Man\nkann nicht einfach alles aus dem alten Code herauslesen, sonst implementiert man es nur nach, und nicht neu. Daher muss\nman auch viel mit Kunden kommunizieren, in Erfahrung bringen, wie genau etwas funktionierten soll, was für eine Aufgabe\nes erfüllen muss und mit welchen Ausnahmefällen eventuell zu rechnen ist. Hinzu kommt, dass die Anwendung auf mehreren\nStandorten läuft. In unterschiedlichen Versionen und jeweils mit besonderen Anpassungen. Auch hier liegt es an uns, dies\nzu verbessern.",[439,66537,66538,66539,520,66545,66550,66551,66557,66558,66561],{},"Alles in Allem ist die Arbeit hier oftmals stressiger und anstrengender als vorher, aber ich konnte viel lernen (vor\nallem den Umgang mit Kunden) und einige neue Technologien einsetzen. So wird zum Beispiel eine Oracle Datenbank genutzt,\ndie doch schon ein wenig anders funktioniert, als die bisher gewohnten MySQL oder PostgreSQL Datenbanken. Auf der Oracle\nDatanbank durfte ich zum Beispiel mit RMI und JMS experimentieren, damit man die Businesslogik, die bisher teilweise\ndirekt in der Datenbank mit Triggern und Prozeduren realisiert war, langsam im Java Code auslagern konnte. Außerdem\nwollen wir die bisher große Anwendung bei der Neuimplementierung in mehrere kleinere Anwendungen aufgeteilt, die auf\nmehreren Servern verteilt laufen und damit besser skalierbar sind. Diese können zum Beispiel über eine JMS queues\nmiteinander kommunizieren und sich über RMI Java Klassen und Services zur Verfügung stellen. Dann war ich noch bei einer\nEvalualisierung von Reporting- und Dokumenterzeugungs- engines beteiligt, da die bisherige ersetzt werden sollte.\nMögliche Kandidaten waren\nhier ",[1002,66540,66544],{"href":66541,"rel":66542,"title":66543},"http://www.eclipse.org/birt/phoenix/",[1006],"birt","Birt",[1002,66546,66549],{"href":66547,"rel":66548,"title":66549},"http://jasperforge.org/projects/jasperreports/",[1006],"JasperReports","\nund Crystal Reports, wobei wir uns am Ende für Birt entschieden haben. Außerden haben wir für die neue Oberfläche\nauf ",[1002,66552,66556],{"href":66553,"rel":66554,"title":66555},"http://wicket.apache.org/",[1006],"wicket","Wicket","gesetzt, das einem, wenn man sich erst einmal darin eingearbeitet hat,\neine Menge Arbeit mit Javascript/Ajax abnimmt. Da die Anwendung auch an unterschiedlichen Standorten läuft und man das\nDatenbankschema auf dem richtigen Stand haben will, wenn man die Anwendung deployed, setzten wir\nhierfür ",[1002,66559,53946],{"href":53944,"rel":66560,"title":54578},[1006]," ein, das diese Arbeit für uns erleichtert und die Änderungen\nautomatisiert vornehmen kann.",[1065,66563,66565],{"id":66564},"abschlussprojekt","Abschlussprojekt",[439,66567,66568],{},"Ab und an war ich jedoch auch in anderen Projekten, wie zum Beispiel meinem Abschlussprojekt.",[439,66570,66571],{},[2205,66572],{"alt":469,"src":66573,"title":66574},"https://media.synyx.de/uploads//2012/07/detail_-150x150.jpg","detail_",[439,66576,66577,66585],{},[1002,66578,66581],{"href":66579,"rel":66580},"https://media.synyx.de/uploads//2012/07/list_snip_.jpg",[1006],[2205,66582],{"alt":469,"src":66583,"title":66584},"https://media.synyx.de/uploads//2012/07/list_snip_-150x150.jpg","list_snip_","\nZeitlich hat das Projekt sehr gut geklappt und vor der Präsentation bei der IHK konnte ich 2 mal in der Firma\npräsentieren und bekam Feedback, was ich denn noch besser machen könnte. So lief dann auch die Präsentation vor den\nPrüfern super. Die Prüfer waren jedoch recht überrascht, dass man mittlerweile auch mit dem Smartphone präsentieren\nkann (HDMI-Ausgang ftw! 😀 ) und die Katzen auf den Präsentationsfolien waren auch ein Erfolg. Allein die Dokumentation\nwurde nicht sooo gut bewertet, die Gründe dafür sind mir jedoch noch nicht bekannt. Erstmal einen Termin zur Einsicht\nvereinbaren, usw… Bürokratie >.>",[439,66587,66588],{},"Bestanden habe ich aber natürlich und bin seitdem als Junior Developer bei synyx angestellt!",{"title":469,"searchDepth":507,"depth":507,"links":66590},[66591,66592,66593],{"id":66515,"depth":547,"text":16232},{"id":66521,"depth":547,"text":66522},{"id":66564,"depth":547,"text":66565},[13208],"2012-08-21T10:05:46","(Fortsetzung meines Blogposts von letztem Jahr)","https://synyx.de/blog/meine-ausbildung-bei-synyx-teil-2/",{},"/blog/meine-ausbildung-bei-synyx-teil-2",{"title":66487,"description":66596},"blog/meine-ausbildung-bei-synyx-teil-2",[13219,66603,66604],"azubi-2","fachinformatiker-anwendungsentwicklung","(Fortsetzung meines Blogposts von letztem Jahr) Das dritte Jahr meiner Ausbildung bei synyx spielte sich zum Großteil in der Abteilung Code Clinic ab, in der ich etliche neue Technologien und…","PcqCmN1tKg-vAE26BfEmkzfPmOB6a4mqFDEfGMIyMnI",{"id":66608,"title":66609,"author":66610,"body":66611,"category":66684,"date":66685,"description":66618,"extension":1034,"link":66686,"meta":66687,"navigation":916,"path":66688,"seo":66689,"slug":66690,"stem":66691,"tags":66692,"teaser":66693,"__hash__":66694},"blog/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat.md","Clean Code Development – Prinzipien und Praktiken zur Steigerung der Software Qualität",[338],{"type":432,"value":66612,"toc":66682},[66613,66616,66619,66622,66625,66630,66633,66638,66641,66644,66649,66652,66655,66660,66663,66666,66669,66672,66675],[435,66614,66609],{"id":66615},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualität",[439,66617,66618],{},"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe",[439,66620,66621],{},"Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus?",[439,66623,66624],{},"Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien und Praktiken zusammen und sieht\neine stufenweise Einführung derer vor. Ohne auf die einzelnen Grade, welche den Weg zum Clean Code Developer\nbeschreiben, einzugehen möchte ich hier ein paar ausgewählte Prinzipien kurz vorstellen.",[439,66626,66627],{},[448,66628,66629],{},"Don’t Repeat Yourself (DRY)",[439,66631,66632],{},"Code Duplizierung sollte vermieden werden, da Anpassungen an mehreren stellen erfolgen müssen um Konsistenz zu\ngewährleisten. Eigentlich ein alter Hut aber Copy & Paste ist in der Praxis weit verbreitet.",[439,66634,66635],{},[448,66636,66637],{},"Keep it simple, stupid (KISS)",[439,66639,66640],{},"Mach es nicht komplizierter als notwendig.",[439,66642,66643],{},"Ja, auch wenn es dir uncool und langweilig erscheint!",[439,66645,66646],{},[448,66647,66648],{},"Favour Composition over Inheritance (FCoI)",[439,66650,66651],{},"Bei der Vererbung entstehen Abhängikeiten von den Subklassen zu den Elternklassen, was das Austauschen der\nFunktionalität zur Laufzeit erschwert. Wird die Vererbung inflationär verwendet, kann dies ein System schnell unnötig\nkomplex und schlecht testbar werden lassen.",[439,66653,66654],{},"Bei der Komposition verwendet eine Klasse eine andere, was die Abhängigkeiten reduziert und die Testbarkeit wesentlich\nvereinfacht.",[439,66656,66657],{},[448,66658,66659],{},"You Ain’t Gonna Need It (YAGNI)",[439,66661,66662],{},"Dieses Prinzip bedeutet soviel wie “implementiere nichts was du nicht brauchst”. Das klingt logisch aber dennoch hab ich\nselbst schon gegen dieses Prinzip verstoßen.",[439,66664,66665],{},"In der Praxis wird versucht Software auf künftige Anforderungen vorzubereiten aber oft stellt sich später heraus, dass\nsich die ursprünglich erwarteten Anforderungen tatsächlich nie ergeben haben.",[439,66667,66668],{},"Statt dessen ergeben sich natürlich ganz andere Anforderungen die nicht berücksichtigt wurden und deren Umsetzung jetzt\ndurch unnötigen Balast behindert werden.",[439,66670,66671],{},"Die Praktiken des Clean Code Developments sind Methoden und Techniken die ständig eingesetzt werden sollten und umfassen\nzum Beispiel das Issue Tracking, automatisierte Integrationstests, Continuous Integration, statische Codeanalysen aber\nauch das Weitergeben von Erfahrungen und die Teilnahme an Fachveranstaltungen.",[439,66673,66674],{},"Mehr zu Clean Code Development gibt es unter",[439,66676,66677],{},[1002,66678,66679],{"href":66679,"rel":66680,"title":66681},"http://www.clean-code-developer.de",[1006],"clean-code-developer",{"title":469,"searchDepth":507,"depth":507,"links":66683},[],[1030],"2012-08-16T09:57:29","https://synyx.de/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat/",{},"/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",{"title":66609,"description":66618},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat","blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",[15546,25992],"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus? Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien…","qanNmRX3aNPeWTNpe7MMGWO8r9OdqTQQpJRo9UX9hwo",{"id":66696,"title":63120,"author":66697,"body":66698,"category":67328,"date":67329,"description":67330,"extension":1034,"link":67331,"meta":67332,"navigation":916,"path":67333,"seo":67334,"slug":66702,"stem":67335,"tags":67336,"teaser":67337,"__hash__":67338},"blog/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar.md",[335],{"type":432,"value":66699,"toc":67319},[66700,66703,66706,66712,66715,66721,66734,66737,66741,66788,66791,66804,66807,66821,66832,66835,66920,66922,66925,66927,66930,66932,66935,66937,66947,66949,66952,66955,66998,67001,67033,67036,67124,67133,67136,67139,67142,67147,67151,67158,67162,67169,67192,67196,67199,67208,67211,67220,67231,67248,67252,67255,67258,67267,67277,67281,67286,67292,67297,67302,67317],[435,66701,63120],{"id":66702},"visualize-javascript-code-quality-and-code-coverage-with-sonar",[439,66704,66705],{},"It is hard to imagine a web project without JavaScript code today. JavaScript is an easy to learn and very performant\nscript language. In the past we have used JavaScript mostly for eye-candy and form validation. Recently we have been\nasked more often to implement complex user interfaces with trees, sortable tables and things like that. So we decided to\nrely more on JavaScript to improve the feedback of the website to user actions.",[439,66707,66708,66709],{},"The first question that came up was: ",[448,66710,66711],{},"How to develop test driven with JavaScript?",[439,66713,66714],{},"We decided to use Jasmine, a behaviour driven development framework which tests can be run headless in a Maven build for\nexample.",[439,66716,66717,66718],{},"The second question was: ",[448,66719,66720],{},"How to visualise code coverage and code quality?",[439,66722,66723,66724,66729,66730,66733],{},"The tool Sonar has been proven to be useful in our Java projects in the past. So the first I was searching for was the\nJavaScript Plugin for Sonar. It can be\ndownloaded ",[1002,66725,22916],{"href":66726,"rel":66727,"title":66728},"https://web.archive.org/web/20150530172810/http://docs.codehaus.org/display/SONAR/JavaScript+Plugin",[1006],"Sonar JavaScript Plugin","\nand was luckily updated to version 1.0 recently with a bunch of new metrics like “",[990,66731,66732],{},"avoid usage of == and !=","”.",[439,66735,66736],{},"Unfortunately this plugin only supports JsTestDriver for code coverage and other test metrics. However, Jasmine support\nis on the Roadmap and I’m looking forward to see the next release of the JavaScript Plugin. But at the moment I had to\nintegrate our Jasmine and jasmine-jquery tests with JsTestDriver, the JavaScript Plugin of Sonar and an automated maven\nbuild.",[3938,66738,66740],{"id":66739},"used-technologies","Used technologies",[994,66742,66743,66750,66756,66763,66770,66777,66783],{},[997,66744,66745],{},[1002,66746,66749],{"href":66747,"rel":66748,"title":66749},"https://maven.apache.org/",[1006],"Maven",[997,66751,66752],{},[1002,66753,34179],{"href":66754,"rel":66755,"title":34179},"https://github.com/jasmine/jasmine",[1006],[997,66757,66758],{},[1002,66759,66762],{"href":66760,"rel":66761,"title":66762},"https://github.com/ibolmo/jasmine-jstd-adapter",[1006],"jasmine-jstd-adapter",[997,66764,66765],{},[1002,66766,66769],{"href":66767,"rel":66768,"title":66769},"https://code.google.com/p/js-test-driver/",[1006],"JsTestDriver",[997,66771,66772],{},[1002,66773,66776],{"href":66774,"rel":66775,"title":66776},"https://code.google.com/p/jstd-maven-plugin/",[1006],"jstd-maven-plugin",[997,66778,66779],{},[1002,66780,51469],{"href":66781,"rel":66782,"title":51469},"https://sonarsource.com/",[1006],[997,66784,66785],{},[1002,66786,66728],{"href":66726,"rel":66787,"title":66728},[1006],[3938,66789,66769],{"id":66790},"jstestdriver",[439,66792,66793,66794,66798,66799,1402],{},"JsTestDriver is a test runner designed by Google and can be\ndownloaded ",[1002,66795,22916],{"href":66796,"rel":66797,"title":66769},"http://code.google.com/p/js-test-driver/downloads/",[1006],". If you don’t know JSTestDriver\nyet, you maybe want to run over\nit’s ",[1002,66800,22273],{"href":66801,"rel":66802,"title":66803},"http://code.google.com/p/js-test-driver/w/list",[1006],"JsTestDriver Documentation",[439,66805,66806],{},"Some advantages of JsTestDriver:",[994,66808,66809,66812,66815,66818],{},[997,66810,66811],{},"Eclipse and IntelliJ integration",[997,66813,66814],{},"Maven Plugin",[997,66816,66817],{},"parallel test executions across browsers (local or remote)",[997,66819,66820],{},"code coverage",[439,66822,66823,66824,66827,66828,66831],{},"In order to run JsTestDriver you have to create a config file. By default you have to name it ",[990,66825,66826],{},"jsTestDriver.conf"," and\nyou have to save it in ",[990,66829,66830],{},"src/test/resources",". If you want to choose another filename or path remember to define these in\nthe Maven plugin later.",[439,66833,66834],{},"In the config file you have to define following flags (Be sure that there are no whitespaces in front of the keywords):",[464,66836,66838],{"className":16895,"code":66837,"language":16897,"meta":469,"style":469},"server: http://localhost:9876\nload:\n # jasmine dependency\n - lib/jasmine.js\n # to make our jasmine tests work within jstd\n # (must be included after jasmine.js)\n - lib/jasmineAdapter.js\n # models, views and other stuff\n - src/main/js/*.js\ntest:\n # load all test files\n - src/test/js/*Test.js\nplugin:\n - name: \"coverage\"\n jar: \"lib/coverage-1.3.4.b.jar\"\n module: \"com.google.jstestdriver.coverage.CoverageModule\"\n",[471,66839,66840,66845,66850,66855,66860,66865,66870,66875,66880,66885,66890,66895,66900,66905,66910,66915],{"__ignoreMap":469},[474,66841,66842],{"class":476,"line":477},[474,66843,66844],{},"server: http://localhost:9876\n",[474,66846,66847],{"class":476,"line":507},[474,66848,66849],{},"load:\n",[474,66851,66852],{"class":476,"line":547},[474,66853,66854],{}," # jasmine dependency\n",[474,66856,66857],{"class":476,"line":584},[474,66858,66859],{}," - lib/jasmine.js\n",[474,66861,66862],{"class":476,"line":607},[474,66863,66864],{}," # to make our jasmine tests work within jstd\n",[474,66866,66867],{"class":476,"line":642},[474,66868,66869],{}," # (must be included after jasmine.js)\n",[474,66871,66872],{"class":476,"line":663},[474,66873,66874],{}," - lib/jasmineAdapter.js\n",[474,66876,66877],{"class":476,"line":694},[474,66878,66879],{}," # models, views and other stuff\n",[474,66881,66882],{"class":476,"line":700},[474,66883,66884],{}," - src/main/js/*.js\n",[474,66886,66887],{"class":476,"line":913},[474,66888,66889],{},"test:\n",[474,66891,66892],{"class":476,"line":920},[474,66893,66894],{}," # load all test files\n",[474,66896,66897],{"class":476,"line":926},[474,66898,66899],{}," - src/test/js/*Test.js\n",[474,66901,66902],{"class":476,"line":932},[474,66903,66904],{},"plugin:\n",[474,66906,66907],{"class":476,"line":938},[474,66908,66909],{}," - name: \"coverage\"\n",[474,66911,66912],{"class":476,"line":944},[474,66913,66914],{}," jar: \"lib/coverage-1.3.4.b.jar\"\n",[474,66916,66917],{"class":476,"line":950},[474,66918,66919],{}," module: \"com.google.jstestdriver.coverage.CoverageModule\"\n",[1633,66921,58838],{"id":58838},[439,66923,66924],{},"JsTestDriver starts it’s own server and generates a HTML page that can be captured by various browsers.",[1633,66926,34543],{"id":34543},[439,66928,66929],{},"Define all needed JavaScript files like your models and views and so on. You can use a wildcard * to include all files\nwithin the specified directory. Note that it could be relevant to load your files in an correct order since a normal\nHTML page will be created and some dependencies have to be considered. The load sequence will be alphabetically if the\nwildcard is used.",[1633,66931,18497],{"id":18497},[439,66933,66934],{},"simply attaches the test files to the created HTML page.",[1633,66936,6505],{"id":6505},[439,66938,66939,66940,66946],{},"to be able to see the code coverage report in Sonar you have to download\nthe ",[1002,66941,66945],{"href":66942,"rel":66943,"title":66944},"http://code.google.com/p/js-test-driver/downloads/list",[1006],"jstd coverage plugin","coverage plugin"," and save it\nsomewhere as you wish. Just remember to add the path to the plugin jar flag as shown in the listing above.",[3938,66948,66776],{"id":66776},[439,66950,66951],{},"Before we can see the Sonar report about the code quality and code coverage we have to configure maven to run jstd.",[439,66953,66954],{},"Unfortunately, the jstd-maven-plugin is not available at the Maven Central Repository. Therefore we have to add a new\nrepository and pluginRepository to our pom.xml:",[464,66956,66958],{"className":16895,"code":66957,"language":16897,"meta":469,"style":469},"\u003Crepository>\n \u003Cid>jstd-maven-plugin google code repo\u003C/id>\n \u003Curl>http://jstd-maven-plugin.googlecode.com/svn/maven2\u003C/url>\n\u003C/repository>\n\u003CpluginRepository>\n \u003Cid>jstd-maven-plugin google code repo\u003C/id>\n \u003Curl>http://jstd-maven-plugin.googlecode.com/svn/maven2\u003C/url>\n\u003C/pluginRepository>\n",[471,66959,66960,66965,66970,66975,66980,66985,66989,66993],{"__ignoreMap":469},[474,66961,66962],{"class":476,"line":477},[474,66963,66964],{},"\u003Crepository>\n",[474,66966,66967],{"class":476,"line":507},[474,66968,66969],{}," \u003Cid>jstd-maven-plugin google code repo\u003C/id>\n",[474,66971,66972],{"class":476,"line":547},[474,66973,66974],{}," \u003Curl>http://jstd-maven-plugin.googlecode.com/svn/maven2\u003C/url>\n",[474,66976,66977],{"class":476,"line":584},[474,66978,66979],{},"\u003C/repository>\n",[474,66981,66982],{"class":476,"line":607},[474,66983,66984],{},"\u003CpluginRepository>\n",[474,66986,66987],{"class":476,"line":642},[474,66988,66969],{},[474,66990,66991],{"class":476,"line":663},[474,66992,66974],{},[474,66994,66995],{"class":476,"line":694},[474,66996,66997],{},"\u003C/pluginRepository>\n",[439,66999,67000],{},"After that maven should be able to fetch the jstd-maven-plugin artifact:",[464,67002,67004],{"className":16895,"code":67003,"language":16897,"meta":469,"style":469},"\u003Cdependency>\n \u003CgroupId>com.googlecode.jstd-maven-plugin\u003C/groupId>\n \u003CartifactId>jstd-maven-plugin\u003C/artifactId>\n \u003Cversion>1.3.2.5\u003C/version>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n",[471,67005,67006,67010,67015,67020,67025,67029],{"__ignoreMap":469},[474,67007,67008],{"class":476,"line":477},[474,67009,65654],{},[474,67011,67012],{"class":476,"line":507},[474,67013,67014],{}," \u003CgroupId>com.googlecode.jstd-maven-plugin\u003C/groupId>\n",[474,67016,67017],{"class":476,"line":547},[474,67018,67019],{}," \u003CartifactId>jstd-maven-plugin\u003C/artifactId>\n",[474,67021,67022],{"class":476,"line":584},[474,67023,67024],{}," \u003Cversion>1.3.2.5\u003C/version>\n",[474,67026,67027],{"class":476,"line":607},[474,67028,63264],{},[474,67030,67031],{"class":476,"line":642},[474,67032,65674],{},[439,67034,67035],{},"To run our tests with a maven build we need the jstd-maven-plugin as a Maven plugin:",[464,67037,67039],{"className":16895,"code":67038,"language":16897,"meta":469,"style":469},"\u003Cplugin>\n \u003CgroupId>com.googlecode.jstd-maven-plugin\u003C/groupId>\n \u003CartifactId>jstd-maven-plugin\u003C/artifactId>\n \u003Cversion>1.3.2.5\u003C/version>\n \u003Cconfiguration>\n \u003Cbrowser>firefox\u003C/browser>\n \u003Cport>9876\u003C/port>\n \u003CtestOutput>target/jstestdriver\u003C/testOutput>\n \u003C/configuration>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>run-tests\u003C/id>\n \u003Cgoals>\n \u003Cgoal>test\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n\u003C/plugin>\n",[471,67040,67041,67045,67049,67053,67057,67062,67067,67072,67077,67082,67087,67091,67096,67101,67106,67111,67115,67120],{"__ignoreMap":469},[474,67042,67043],{"class":476,"line":477},[474,67044,44754],{},[474,67046,67047],{"class":476,"line":507},[474,67048,67014],{},[474,67050,67051],{"class":476,"line":547},[474,67052,67019],{},[474,67054,67055],{"class":476,"line":584},[474,67056,67024],{},[474,67058,67059],{"class":476,"line":607},[474,67060,67061],{}," \u003Cconfiguration>\n",[474,67063,67064],{"class":476,"line":642},[474,67065,67066],{}," \u003Cbrowser>firefox\u003C/browser>\n",[474,67068,67069],{"class":476,"line":663},[474,67070,67071],{}," \u003Cport>9876\u003C/port>\n",[474,67073,67074],{"class":476,"line":694},[474,67075,67076],{}," \u003CtestOutput>target/jstestdriver\u003C/testOutput>\n",[474,67078,67079],{"class":476,"line":700},[474,67080,67081],{}," \u003C/configuration>\n",[474,67083,67084],{"class":476,"line":913},[474,67085,67086],{}," \u003Cexecutions>\n",[474,67088,67089],{"class":476,"line":920},[474,67090,54113],{},[474,67092,67093],{"class":476,"line":926},[474,67094,67095],{}," \u003Cid>run-tests\u003C/id>\n",[474,67097,67098],{"class":476,"line":932},[474,67099,67100],{}," \u003Cgoals>\n",[474,67102,67103],{"class":476,"line":938},[474,67104,67105],{}," \u003Cgoal>test\u003C/goal>\n",[474,67107,67108],{"class":476,"line":944},[474,67109,67110],{}," \u003C/goals>\n",[474,67112,67113],{"class":476,"line":950},[474,67114,54138],{},[474,67116,67117],{"class":476,"line":956},[474,67118,67119],{}," \u003C/executions>\n",[474,67121,67122],{"class":476,"line":962},[474,67123,44834],{},[439,67125,67126,67127,67132],{},"Three configuration flags are mandatory. More command line flags can be found in\nthe ",[1002,67128,22273],{"href":67129,"rel":67130,"title":67131},"http://code.google.com/p/js-test-driver/wiki/CommandLineFlags",[1006],"jstd-maven-plugin documentation"," of\njstd.",[1633,67134,67135],{"id":67135},"browser",[439,67137,67138],{},"a comma separated list of browsers (more exactly the path to the specific browser) that should be used for the tests",[1633,67140,67141],{"id":67141},"port",[439,67143,67144,67145],{},"the port that is set in ",[990,67146,66826],{},[1633,67148,67150],{"id":67149},"testoutput","testOutput",[439,67152,67153,67154,67157],{},"This specifies the directory where the code coverage reports (needed for Sonar) will be saved. The default directory for\nsonar is ",[990,67155,67156],{},"target/jstestdriver",", so remember to configure sonar accordingly, if you choose another directory.",[3938,67159,67161],{"id":67160},"set-the-sourcedirectory-in-the-pomxml","Set the sourceDirectory in the pom.xml",[439,67163,67164,67165,67168],{},"In order for Sonar to be able to analyze the JavaScript code and to visualize the reports, we must add the path to the\nsource code which is ",[990,67166,67167],{},"src/main/js"," in our case.",[464,67170,67172],{"className":16895,"code":67171,"language":16897,"meta":469,"style":469},"\u003Cbuild>\n \u003CsourceDirectory>src/main/js\u003C/sourceDirectory>\n \u003C!-- ... -->\n\u003C/build>\n",[471,67173,67174,67178,67183,67188],{"__ignoreMap":469},[474,67175,67176],{"class":476,"line":477},[474,67177,54015],{},[474,67179,67180],{"class":476,"line":507},[474,67181,67182],{}," \u003CsourceDirectory>src/main/js\u003C/sourceDirectory>\n",[474,67184,67185],{"class":476,"line":547},[474,67186,67187],{}," \u003C!-- ... -->\n",[474,67189,67190],{"class":476,"line":584},[474,67191,54188],{},[3938,67193,67195],{"id":67194},"run-the-tests-and-the-analysis","Run the tests and the analysis",[439,67197,67198],{},"Everything should be configured correctly now. So just start the maven build:",[464,67200,67202],{"className":16895,"code":67201,"language":16897,"meta":469,"style":469},"mvn jstd:test\n",[471,67203,67204],{"__ignoreMap":469},[474,67205,67206],{"class":476,"line":477},[474,67207,67201],{},[439,67209,67210],{},"JsTestDriver opens the defined browsers, runs all tests and generates the code coverage report. After that we have to\nstart the sonar build:",[464,67212,67214],{"className":16895,"code":67213,"language":16897,"meta":469,"style":469},"mvn sonar:sonar -Dsonar.language=js -Dsonar.branch=js\n",[471,67215,67216],{"__ignoreMap":469},[474,67217,67218],{"class":476,"line":477},[474,67219,67213],{},[439,67221,67222,67223,67226,67227,67230],{},"To tell sonar to analyze a JavaScript project the ",[990,67224,67225],{},"sonar.language"," property is essential. If the same project should be\nanalyzed as a Java project you may want to add a branch with the property ",[990,67228,67229],{},"sonar.branch",". Otherwise the previous values\nwill be overridden with this JavaScript analysis.",[439,67232,67233,8351,67241],{},[1002,67234,67237],{"href":67235,"rel":67236},"https://media.synyx.de/uploads//2012/08/js_sonar_01.png",[1006],[2205,67238],{"alt":469,"src":67239,"title":67240},"https://media.synyx.de/uploads//2012/08/js_sonar_01-150x150.png","JavaScript Plugin - Sonar",[1002,67242,67245],{"href":67243,"rel":67244},"https://media.synyx.de/uploads//2012/08/js_sonar_02.png",[1006],[2205,67246],{"alt":469,"src":67247,"title":67240},"https://media.synyx.de/uploads//2012/08/js_sonar_02-150x150.png",[3938,67249,67251],{"id":67250},"problems","Problems",[439,67253,67254],{},"An annoying problem is running the tests with real browsers like Firefox and Chrome. The maven build automatically\nstarts the browser and also closes it after the tests are finished. But Firefox is not correctly closed by jstd somehow…\nso the next test run fails because Firefox opens a dialog which must be closed manually. The maven build is deadlocked\nand you have to abort and rerun it…",[439,67256,67257],{},"So maybe a running Firefox process would be a workaround, I thought. Well, it kinda worked but the opened tab was not\nclosed anymore (tested on Linux and Windows). Each new test run opened a new tab and after a handful testruns the tests\nfailed because of some strange error. Closing tabs manually solved this, however. Same problem occurred with Chrome (\nVersion 22.0.1201.0 dev).",[439,67259,67260,67261,67266],{},"On one hand it is really nice to run the tests in all desired browsers, on the other hand closing tabs/browsers manually\nmakes it impossible to automate this process. So I’m really looking forward to Jasmine support of the sonar JavaScript\nPlugin to run headless tests as a maven build, just like jasmine-maven-plugin. A quick google search links to this\nproject: ",[1002,67262,67265],{"href":67263,"rel":67264},"https://github.com/jwark/jstd%5C-standalone%5C-headless",[1006],"https://github.com/jwark/jstd\\-standalone\\-headless"," Maybe this could be a solution… Any information is welcome,\nso if you have a working setup, please let me know.",[439,67268,67269,67270,67273,67274,67276],{},"Another problem surely is the mandatory specification of the ",[990,67271,67272],{},"sourceDirectory"," to be able to see the metrics in Sonar.\nUsually you will have a Java project with some JavaScript code. Therefore you certainly can’t pinpoint to ",[990,67275,67167],{},"\nas source directory of the project, for example. Further information is appreciated, again 🙂",[3938,67278,67280],{"id":67279},"todo","Todo",[439,67282,67283],{},[448,67284,67285],{},"automate the analysis within a Jenkins build process",[439,67287,67288,67289],{},"— ",[990,67290,67291],{},"maybe jstd tests can be run headless?",[439,67293,67288,67294],{},[990,67295,67296],{},"maybe maven profiles could be used to prevent the sourceDirectory declaration?",[439,67298,67299],{},[448,67300,67301],{},"require.js integration in jstd-unit-tests",[439,67303,67288,67304],{},[990,67305,67306,67311,67312,67316],{},[1002,67307,67310],{"href":47153,"rel":67308,"title":67309},[1006],"require.js","RequireJS"," is a JavaScript file and module loader\nand ",[1002,67313,8394],{"href":67314,"rel":67315},"https://github.com/podefr/jasmine-reqjs-jstd/wiki/how-to-setup-requirejs---jasmine---jsTestDriver",[1006]," should be\na good starting point.",[1024,67318,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":67320},[67321,67322,67323,67324,67325,67326,67327],{"id":66739,"depth":507,"text":66740},{"id":66790,"depth":507,"text":66769},{"id":66776,"depth":507,"text":66776},{"id":67160,"depth":507,"text":67161},{"id":67194,"depth":507,"text":67195},{"id":67250,"depth":507,"text":67251},{"id":67279,"depth":507,"text":67280},[1030,1412,1413],"2012-08-08T13:43:42","It is hard to imagine a web project without JavaScript code today. JavaScript is an easy to learn and very performant\\nscript language. In the past we have used JavaScript mostly for eye-candy and form validation. Recently we have been\\nasked more often to implement complex user interfaces with trees, sortable tables and things like that. So we decided to\\nrely more on JavaScript to improve the feedback of the website to user actions.","https://synyx.de/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar/",{},"/blog/visualize-javascript-code-quality-and-code-coverage-with-sonar",{"title":63120,"description":66705},"blog/visualize-javascript-code-quality-and-code-coverage-with-sonar",[],"It is hard to imagine a web project without JavaScript code today. JavaScript is an easy to learn and very performant script language. In the past we have used JavaScript…","vXfP3gRYwDQlgLZoV0VTIPNU4wKPbYfHWuZ0ntzkqr8",{"id":67340,"title":67341,"author":67342,"body":67343,"category":67973,"date":67974,"description":67975,"extension":1034,"link":67976,"meta":67977,"navigation":916,"path":67978,"seo":67979,"slug":67347,"stem":67980,"tags":67981,"teaser":67982,"__hash__":67983},"blog/blog/migrating-data-with-liquibase.md","Migrating data with Liquibase",[268],{"type":432,"value":67344,"toc":67971},[67345,67348,67351,67354,67357,67360,67407,67410,67487,67490,67504,67507,67510,67570,67573,67610,67613,67659,67662,67665,67679,67682,67748,67751,67754,67812,67815,67818,67821,67898,67901,67904,67946,67949,67963,67966,67969],[435,67346,67341],{"id":67347},"migrating-data-with-liquibase",[439,67349,67350],{},"Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both\nnew from-scratch projects and already existing ones. Liquibase is great because it allows us to use an SCM tool like\nGit to manage different revisions of an applications database schema – or more specifically, the changes required to\nmigrate the database schema from one revision to another.",[439,67352,67353],{},"While migrating database schemas seems like a pretty straight-forward task at the beginning, things get more\ncomplicated as soon as you want to roll back schema changes without dropping your database (and then rebuilding it).\nLiquibase also supports migrating your data across schema changes, in both directions. But lets start with the basics.",[439,67355,67356],{},"For this example, I only used the Liquibase command line interface, along with the basic MySQL command line client. Of\ncourse, Liquibase also integrates nicely with Maven (as a Maven goal) or Spring (as a bean that executes during context\ninitialization).",[439,67358,67359],{},"I start with a very basic table called „Person“, consisting only of an ID (primary key) and a name:",[464,67361,67363],{"className":16895,"code":67362,"language":16897,"meta":469,"style":469},"\nmysql> describe Person;\n+-------+--------------+------+-----+---------+----------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+--------------+------+-----+---------+----------------+\n| id | bigint(20) | NO | PRI | NULL | auto_increment |\n| name | varchar(255) | NO | UNI | NULL | |\n+-------+--------------+------+-----+---------+----------------+\n2 rows in set (0.00 sec)\n\n",[471,67364,67365,67369,67374,67379,67384,67388,67393,67398,67402],{"__ignoreMap":469},[474,67366,67367],{"class":476,"line":477},[474,67368,917],{"emptyLinePlaceholder":916},[474,67370,67371],{"class":476,"line":507},[474,67372,67373],{},"mysql> describe Person;\n",[474,67375,67376],{"class":476,"line":547},[474,67377,67378],{},"+-------+--------------+------+-----+---------+----------------+\n",[474,67380,67381],{"class":476,"line":584},[474,67382,67383],{},"| Field | Type | Null | Key | Default | Extra |\n",[474,67385,67386],{"class":476,"line":607},[474,67387,67378],{},[474,67389,67390],{"class":476,"line":642},[474,67391,67392],{},"| id | bigint(20) | NO | PRI | NULL | auto_increment |\n",[474,67394,67395],{"class":476,"line":663},[474,67396,67397],{},"| name | varchar(255) | NO | UNI | NULL | |\n",[474,67399,67400],{"class":476,"line":694},[474,67401,67378],{},[474,67403,67404],{"class":476,"line":700},[474,67405,67406],{},"2 rows in set (0.00 sec)\n",[439,67408,67409],{},"Liquibase uses so-called changesets, which are XML-snippets used to describe DDL statements. They are organized in\nchange log files. The following change set is used to create a table (via the „createTable“-tag) and two columns (via\nthe „column“-tag)",[464,67411,67413],{"className":6253,"code":67412,"language":6255,"meta":469,"style":469},"\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n \u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n \u003Ccreatetable tablename=\"Person\">\n \u003Ccolumn autoincrement=\"true\" name=\"id\" type=\"BIGINT\">\n \u003Cconstraints nullable=\"false\" primarykey=\"true\">\n \u003C/constraints>\n \u003C/column>\n \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/createtable>\n \u003C/changeset>\n\u003C/databasechangelog>\n\n",[471,67414,67415,67419,67424,67429,67434,67439,67444,67449,67454,67459,67464,67468,67472,67477,67482],{"__ignoreMap":469},[474,67416,67417],{"class":476,"line":477},[474,67418,917],{"emptyLinePlaceholder":916},[474,67420,67421],{"class":476,"line":507},[474,67422,67423],{},"\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n",[474,67425,67426],{"class":476,"line":547},[474,67427,67428],{}," \u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n",[474,67430,67431],{"class":476,"line":584},[474,67432,67433],{}," \u003Ccreatetable tablename=\"Person\">\n",[474,67435,67436],{"class":476,"line":607},[474,67437,67438],{}," \u003Ccolumn autoincrement=\"true\" name=\"id\" type=\"BIGINT\">\n",[474,67440,67441],{"class":476,"line":642},[474,67442,67443],{}," \u003Cconstraints nullable=\"false\" primarykey=\"true\">\n",[474,67445,67446],{"class":476,"line":663},[474,67447,67448],{}," \u003C/constraints>\n",[474,67450,67451],{"class":476,"line":694},[474,67452,67453],{}," \u003C/column>\n",[474,67455,67456],{"class":476,"line":700},[474,67457,67458],{}," \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n",[474,67460,67461],{"class":476,"line":913},[474,67462,67463],{}," \u003Cconstraints nullable=\"false\">\n",[474,67465,67466],{"class":476,"line":920},[474,67467,67448],{},[474,67469,67470],{"class":476,"line":926},[474,67471,67453],{},[474,67473,67474],{"class":476,"line":932},[474,67475,67476],{}," \u003C/createtable>\n",[474,67478,67479],{"class":476,"line":938},[474,67480,67481],{}," \u003C/changeset>\n",[474,67483,67484],{"class":476,"line":944},[474,67485,67486],{},"\u003C/databasechangelog>\n",[439,67488,67489],{},"When I run Liquibase via command line, it sets up the „Person“ table. The relevant command is „update“:",[464,67491,67493],{"className":16895,"code":67492,"language":16897,"meta":469,"style":469},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=db.changelog-0.1.0.xml \u003Cb>update\u003C/b>\n\n",[471,67494,67495,67499],{"__ignoreMap":469},[474,67496,67497],{"class":476,"line":477},[474,67498,917],{"emptyLinePlaceholder":916},[474,67500,67501],{"class":476,"line":507},[474,67502,67503],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=db.changelog-0.1.0.xml \u003Cb>update\u003C/b>\n",[439,67505,67506],{},"Liquibase already knows how to roll back certain changesets, like the „createTable“ changeset above. If we call the\ncommand line client with „rollbackCount 1“ instead of „update“, it rolls back the last changeset it executed, and the\n„Person“ table is gone.",[439,67508,67509],{},"Other changesets cannot be rolled back automatically. Consider the following „insert“-changeset that inserts an entry\ninto our „Person“ table:",[464,67511,67513],{"className":6253,"code":67512,"language":6255,"meta":469,"style":469},"\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n \u003Cchangeset author=\"mueller@synyx.de\" id=\"init-1\">\n \u003Cinsert tablename=\"Person\">\n \u003Ccolumn name=\"name\" value=\"John Doe\">\n \u003C/column>\n \u003C/insert>\n \u003Crollback>\n DELETE FROM Person WHERE name LIKE 'John Doe';\n \u003C/rollback>\n \u003C/changeset>\n\u003C/databasechangelog>\n\n",[471,67514,67515,67519,67523,67528,67533,67538,67542,67547,67552,67557,67562,67566],{"__ignoreMap":469},[474,67516,67517],{"class":476,"line":477},[474,67518,917],{"emptyLinePlaceholder":916},[474,67520,67521],{"class":476,"line":507},[474,67522,67423],{},[474,67524,67525],{"class":476,"line":547},[474,67526,67527],{}," \u003Cchangeset author=\"mueller@synyx.de\" id=\"init-1\">\n",[474,67529,67530],{"class":476,"line":584},[474,67531,67532],{}," \u003Cinsert tablename=\"Person\">\n",[474,67534,67535],{"class":476,"line":607},[474,67536,67537],{}," \u003Ccolumn name=\"name\" value=\"John Doe\">\n",[474,67539,67540],{"class":476,"line":642},[474,67541,67453],{},[474,67543,67544],{"class":476,"line":663},[474,67545,67546],{}," \u003C/insert>\n",[474,67548,67549],{"class":476,"line":694},[474,67550,67551],{}," \u003Crollback>\n",[474,67553,67554],{"class":476,"line":700},[474,67555,67556],{}," DELETE FROM Person WHERE name LIKE 'John Doe';\n",[474,67558,67559],{"class":476,"line":913},[474,67560,67561],{}," \u003C/rollback>\n",[474,67563,67564],{"class":476,"line":920},[474,67565,67481],{},[474,67567,67568],{"class":476,"line":926},[474,67569,67486],{},[439,67571,67572],{},"I manually added a „rollback“-tag containg an SQL statement that reverses the changset. Note that the „rollback“-tag\ncan contain either SQL statements as text or certain Liquibase refactoring tags. Since we now have two change log xml\nfiles, I created a „master“-file that imports the other files in the order in which they should be executed:",[464,67574,67576],{"className":6253,"code":67575,"language":6255,"meta":469,"style":469},"\n\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\n http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd\">\n \u003Cinclude file=\"db.changelog-0.1.0.xml\">\u003C/include>\n \u003Cinclude file=\"db.changelog-0.1.0.init.xml\">\u003C/include>\n\u003C/databasechangelog>\n\n",[471,67577,67578,67582,67586,67591,67596,67601,67606],{"__ignoreMap":469},[474,67579,67580],{"class":476,"line":477},[474,67581,917],{"emptyLinePlaceholder":916},[474,67583,67584],{"class":476,"line":507},[474,67585,65310],{},[474,67587,67588],{"class":476,"line":547},[474,67589,67590],{},"\u003Cdatabasechangelog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemalocation=\"http://www.liquibase.org/xml/ns/dbchangelog/1.9\n",[474,67592,67593],{"class":476,"line":584},[474,67594,67595],{}," http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd\">\n",[474,67597,67598],{"class":476,"line":607},[474,67599,67600],{}," \u003Cinclude file=\"db.changelog-0.1.0.xml\">\u003C/include>\n",[474,67602,67603],{"class":476,"line":642},[474,67604,67605],{}," \u003Cinclude file=\"db.changelog-0.1.0.init.xml\">\u003C/include>\n",[474,67607,67608],{"class":476,"line":663},[474,67609,67486],{},[439,67611,67612],{},"If we run the „update“ command with the master change log file, it checks wether the first changeset was already\nexecuted (depending on you rolled it back or not) and then executes the second changeset that adds a „Person“ entry. To\nmake this work, Liquibase create a helper table called „DATABASECHANGELOGS“ containg already-executed change sets along\nwith a hash value (to make sure no-one modifies changesets once they have been executed):",[464,67614,67616],{"className":16895,"code":67615,"language":16897,"meta":469,"style":469},"\nmysql> select id, md5sum, description from DATABASECHANGELOG;\n+--------+------------------------------------+--------------+\n| id | md5sum | description |\n+--------+------------------------------------+--------------+\n| 1 | 3:5a36f447e90b35c3802cb6fe16cb12a7 | Create Table |\n| init-1 | 3:43c29e0011ebfcfd9cfbbb8450179a41 | Insert Row |\n+--------+------------------------------------+--------------+\n2 rows in set (0.00 sec)\n\n",[471,67617,67618,67622,67627,67632,67637,67641,67646,67651,67655],{"__ignoreMap":469},[474,67619,67620],{"class":476,"line":477},[474,67621,917],{"emptyLinePlaceholder":916},[474,67623,67624],{"class":476,"line":507},[474,67625,67626],{},"mysql> select id, md5sum, description from DATABASECHANGELOG;\n",[474,67628,67629],{"class":476,"line":547},[474,67630,67631],{},"+--------+------------------------------------+--------------+\n",[474,67633,67634],{"class":476,"line":584},[474,67635,67636],{},"| id | md5sum | description |\n",[474,67638,67639],{"class":476,"line":607},[474,67640,67631],{},[474,67642,67643],{"class":476,"line":642},[474,67644,67645],{},"| 1 | 3:5a36f447e90b35c3802cb6fe16cb12a7 | Create Table |\n",[474,67647,67648],{"class":476,"line":663},[474,67649,67650],{},"| init-1 | 3:43c29e0011ebfcfd9cfbbb8450179a41 | Insert Row |\n",[474,67652,67653],{"class":476,"line":694},[474,67654,67631],{},[474,67656,67657],{"class":476,"line":700},[474,67658,67406],{},[439,67660,67661],{},"Now that we got the basics running, lets try something more challenging: an actual change to our schema that requires\nboth schema and data migration. Our „Person“ table currently has only a name column, and we decided that we want to\nsplit it up into a „firstname“ and a „lastname“ column.",[439,67663,67664],{},"Before beginning work, I have Liquibase „tag“ the database so that we can roll back to this tag later on:",[464,67666,67668],{"className":16895,"code":67667,"language":16897,"meta":469,"style":469},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>tag liquiblog_0_1_0\u003C/b>\n\n",[471,67669,67670,67674],{"__ignoreMap":469},[474,67671,67672],{"class":476,"line":477},[474,67673,917],{"emptyLinePlaceholder":916},[474,67675,67676],{"class":476,"line":507},[474,67677,67678],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>tag liquiblog_0_1_0\u003C/b>\n",[439,67680,67681],{},"I created a new change set that adds the two new columns:",[464,67683,67685],{"className":6253,"code":67684,"language":6255,"meta":469,"style":469},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n \u003Caddcolumn tablename=\"Person\">\n \u003Ccolumn name=\"firstname\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003Ccolumn name=\"lastname\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/addcolumn>\n\u003C/changeset>\n\n",[471,67686,67687,67691,67696,67701,67706,67711,67716,67721,67726,67730,67734,67738,67743],{"__ignoreMap":469},[474,67688,67689],{"class":476,"line":477},[474,67690,917],{"emptyLinePlaceholder":916},[474,67692,67693],{"class":476,"line":507},[474,67694,67695],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"1\" runonchange=\"true\">\n",[474,67697,67698],{"class":476,"line":547},[474,67699,67700],{}," \u003Caddcolumn tablename=\"Person\">\n",[474,67702,67703],{"class":476,"line":584},[474,67704,67705],{}," \u003Ccolumn name=\"firstname\" type=\"VARCHAR(255)\">\n",[474,67707,67708],{"class":476,"line":607},[474,67709,67710],{}," \u003Cconstraints nullable=\"false\">\n",[474,67712,67713],{"class":476,"line":642},[474,67714,67715],{}," \u003C/constraints>\n",[474,67717,67718],{"class":476,"line":663},[474,67719,67720],{}," \u003C/column>\n",[474,67722,67723],{"class":476,"line":694},[474,67724,67725],{}," \u003Ccolumn name=\"lastname\" type=\"VARCHAR(255)\">\n",[474,67727,67728],{"class":476,"line":700},[474,67729,67710],{},[474,67731,67732],{"class":476,"line":913},[474,67733,67715],{},[474,67735,67736],{"class":476,"line":920},[474,67737,67720],{},[474,67739,67740],{"class":476,"line":926},[474,67741,67742],{}," \u003C/addcolumn>\n",[474,67744,67745],{"class":476,"line":932},[474,67746,67747],{},"\u003C/changeset>\n",[439,67749,67750],{},"Once again, Liquibase knows how to roll back this change set, so we can skip the rollback tag.",[439,67752,67753],{},"Now that the table has two additional columns, we must take care of migrating our existing data to the new schema before\ndeleting the old, now obsolete „name“ column. Since data manipulation is not supported out-of-the-box by Liquibase,\nwe have to use its „sql“ tag to include native SQL statements within a changeset.",[464,67755,67757],{"className":6253,"code":67756,"language":6255,"meta":469,"style":469},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"2\">\n \u003Csql>\n UPDATE Person SET firstname = SUBSTRING_INDEX(name, ' ', 1);\n UPDATE Person SET lastname = SUBSTRING_INDEX(name, ' ', -1);\n \u003C/sql>\n \u003Crollback>\n UPDATE Person SET firstname = '';\n UPDATE Person SET lastname = '';\n \u003C/rollback>\n\u003C/changeset>\n\n",[471,67758,67759,67763,67768,67773,67778,67783,67788,67793,67798,67803,67808],{"__ignoreMap":469},[474,67760,67761],{"class":476,"line":477},[474,67762,917],{"emptyLinePlaceholder":916},[474,67764,67765],{"class":476,"line":507},[474,67766,67767],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"2\">\n",[474,67769,67770],{"class":476,"line":547},[474,67771,67772],{}," \u003Csql>\n",[474,67774,67775],{"class":476,"line":584},[474,67776,67777],{}," UPDATE Person SET firstname = SUBSTRING_INDEX(name, ' ', 1);\n",[474,67779,67780],{"class":476,"line":607},[474,67781,67782],{}," UPDATE Person SET lastname = SUBSTRING_INDEX(name, ' ', -1);\n",[474,67784,67785],{"class":476,"line":642},[474,67786,67787],{}," \u003C/sql>\n",[474,67789,67790],{"class":476,"line":663},[474,67791,67792],{}," \u003Crollback>\n",[474,67794,67795],{"class":476,"line":694},[474,67796,67797],{}," UPDATE Person SET firstname = '';\n",[474,67799,67800],{"class":476,"line":700},[474,67801,67802],{}," UPDATE Person SET lastname = '';\n",[474,67804,67805],{"class":476,"line":913},[474,67806,67807],{}," \u003C/rollback>\n",[474,67809,67810],{"class":476,"line":920},[474,67811,67747],{},[439,67813,67814],{},"Note that the content of the „rollback“-tag is kind of redundant, but the tag itself is required because Liquibase\nprevents us from rolling back changesets that cannot be rolled back implicitly and have no explicit rollback tag.",[439,67816,67817],{},"Once again, after executing Liquibase with the „update“-option, the new changeset is run, and our newly-created\n„firstname“ and „lastname“ columns now contain data.",[439,67819,67820],{},"Finally, I want to remove the old „name“ column.",[464,67822,67824],{"className":6253,"code":67823,"language":6255,"meta":469,"style":469},"\n\u003Cchangeset author=\"mueller@synyx.de\" id=\"3\" runonchange=\"true\">\n \u003Cdropcolumn columnname=\"name\" tablename=\"Person\">\n \u003C/dropcolumn>\n \u003Crollback>\n \u003Caddcolumn tablename=\"Person\">\n \u003Ccolumn name=\"name\" type=\"VARCHAR(255)\">\n \u003Cconstraints nullable=\"false\">\n \u003C/constraints>\n \u003C/column>\n \u003C/addcolumn>\n \u003Csql>\n UPDATE Person SET name = CONCAT(firstname, CONCAT(' ', lastname));\n \u003C/sql>\n \u003C/rollback>\n\u003C/changeset>\n\n",[471,67825,67826,67830,67835,67840,67845,67849,67854,67858,67862,67866,67870,67875,67880,67885,67890,67894],{"__ignoreMap":469},[474,67827,67828],{"class":476,"line":477},[474,67829,917],{"emptyLinePlaceholder":916},[474,67831,67832],{"class":476,"line":507},[474,67833,67834],{},"\u003Cchangeset author=\"mueller@synyx.de\" id=\"3\" runonchange=\"true\">\n",[474,67836,67837],{"class":476,"line":547},[474,67838,67839],{}," \u003Cdropcolumn columnname=\"name\" tablename=\"Person\">\n",[474,67841,67842],{"class":476,"line":584},[474,67843,67844],{}," \u003C/dropcolumn>\n",[474,67846,67847],{"class":476,"line":607},[474,67848,67792],{},[474,67850,67851],{"class":476,"line":642},[474,67852,67853],{}," \u003Caddcolumn tablename=\"Person\">\n",[474,67855,67856],{"class":476,"line":663},[474,67857,67458],{},[474,67859,67860],{"class":476,"line":694},[474,67861,67463],{},[474,67863,67864],{"class":476,"line":700},[474,67865,67448],{},[474,67867,67868],{"class":476,"line":913},[474,67869,67453],{},[474,67871,67872],{"class":476,"line":920},[474,67873,67874],{}," \u003C/addcolumn>\n",[474,67876,67877],{"class":476,"line":926},[474,67878,67879],{}," \u003Csql>\n",[474,67881,67882],{"class":476,"line":932},[474,67883,67884],{}," UPDATE Person SET name = CONCAT(firstname, CONCAT(' ', lastname));\n",[474,67886,67887],{"class":476,"line":938},[474,67888,67889],{}," \u003C/sql>\n",[474,67891,67892],{"class":476,"line":944},[474,67893,67807],{},[474,67895,67896],{"class":476,"line":950},[474,67897,67747],{},[439,67899,67900],{},"Again, the changeset itself is quite simple because Liquibase supports dropping columns, but the „rollback“-tag is more\ncomplicated: I first re-add the old „name“-column using the standart „addColumn“-tag, and then I used a custom SQL\nstatement to set the columns value.",[439,67902,67903],{},"We end up with a new database schema, complete with data:",[464,67905,67907],{"className":16895,"code":67906,"language":16897,"meta":469,"style":469},"\nmysql> select * from Person;\n+----+-----------+------------+\n| id | firstname | lastname |\n+----+-----------+------------+\n| 1 | John | Doe |\n+----+-----------+------------+\n1 rows in set (0.00 sec)\n\n",[471,67908,67909,67913,67918,67923,67928,67932,67937,67941],{"__ignoreMap":469},[474,67910,67911],{"class":476,"line":477},[474,67912,917],{"emptyLinePlaceholder":916},[474,67914,67915],{"class":476,"line":507},[474,67916,67917],{},"mysql> select * from Person;\n",[474,67919,67920],{"class":476,"line":547},[474,67921,67922],{},"+----+-----------+------------+\n",[474,67924,67925],{"class":476,"line":584},[474,67926,67927],{},"| id | firstname | lastname |\n",[474,67929,67930],{"class":476,"line":607},[474,67931,67922],{},[474,67933,67934],{"class":476,"line":642},[474,67935,67936],{},"| 1 | John | Doe |\n",[474,67938,67939],{"class":476,"line":663},[474,67940,67922],{},[474,67942,67943],{"class":476,"line":694},[474,67944,67945],{},"1 rows in set (0.00 sec)\n",[439,67947,67948],{},"Because we created a tag earlier and included rollback instructions in all our changesets, we can always roll back these\nmodifications without loosing any data! By running..",[464,67950,67952],{"className":16895,"code":67951,"language":16897,"meta":469,"style":469},"\n./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>rollback liquiblog_0_1_0\u003C/b>\n\n",[471,67953,67954,67958],{"__ignoreMap":469},[474,67955,67956],{"class":476,"line":477},[474,67957,917],{"emptyLinePlaceholder":916},[474,67959,67960],{"class":476,"line":507},[474,67961,67962],{},"./liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password=\"\" --changeLogFile=changelog-master.xml \u003Cb>rollback liquiblog_0_1_0\u003C/b>\n",[439,67964,67965],{},"..we get our original database back!",[439,67967,67968],{},"Of course, the example with splitting / concatenating strings is a little far-fetched, but the same principles can be\napplied to more sophisticated refactorings. I came across the idea for this blog post when we had to split an existing\ndomain class (mapped to a single table) into an abstract base class and two subclasses, preferrably without losing data.",[1024,67970,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":67972},[],[1030],"2012-08-03T10:27:51","Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both\\nnew from-scratch projects and already existing ones. Liquibase is great because it allows us to use an SCM tool like\\nGit to manage different revisions of an applications database schema – or more specifically, the changes required to\\nmigrate the database schema from one revision to another.","https://synyx.de/blog/migrating-data-with-liquibase/",{},"/blog/migrating-data-with-liquibase",{"title":67341,"description":67350},"blog/migrating-data-with-liquibase",[54575,54578,14723,389],"Recently, we started integrating Liquibase as a database schema migration tool into most of my team’s projects, for both new from-scratch projects and already existing ones. Liquibase is great because…","Vinp4k8Ts7cd7z-85P0-KPtpbF-5oJqchl7xudePQQI",{"id":67985,"title":67986,"author":67987,"body":67988,"category":68885,"date":68886,"description":68887,"extension":1034,"link":68888,"meta":68889,"navigation":916,"path":68890,"seo":68891,"slug":67992,"stem":68892,"tags":68893,"teaser":68895,"__hash__":68896},"blog/blog/consolidating-development-environments-a-bash-magic-tutorial.md","Consolidating development environments – a Bash Magic tutorial",[157],{"type":432,"value":67989,"toc":68883},[67990,67993,67996,67999,68002,68005,68025,68028,68038,68041,68044,68085,68088,68091,68272,68302,68307,68315,68320,68331,68334,68575,68578,68581,68814,68817,68821,68833,68837,68845,68848,68874,68877,68880],[435,67991,67986],{"id":67992},"consolidating-development-environments-a-bash-magic-tutorial",[439,67994,67995],{},"Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a\nconstant struggle to keep your programming environment in sync with what you are actually doing. For that big legacy\nproduct you are maintaining you might need an old Java 1.5 in a specific version – for that fancy new web-app you\nmight be using the newest Java.",[439,67997,67998],{},"Different people have different strategies on how to take care of that problem. IDEs can be used to set software\nversions for specific projects, but using your build-tool from the commandline uses a completely different environment.\nAdapting your current command line environment is a little trickier and can solved in various ways – with varying\ncapabilities and problems.",[439,68000,68001],{},"This article is aimed at anyone wanting to learn a little more about Bash background magic – the resulting code will be\nworkable but will have to be fleshed out by the user to his own needs.",[439,68003,68004],{},"So, what do we actually want our “system” to do:",[994,68006,68007,68010,68013,68016,68019,68022],{},[997,68008,68009],{},"Control build environment (Java, Maven, …)",[997,68011,68012],{},"Special parameters for the build/execution environment (MAVEN_OPTS, …)",[997,68014,68015],{},"Switching environments while working",[997,68017,68018],{},"Switching environments based on project",[997,68020,68021],{},"Different environments in different terminals",[997,68023,68024],{},"More than one user should be able to reuse “environment profiles”",[439,68026,68027],{},"The first idea on how to switch an environment seems to be to create symlinks to the correct environment. That can be\npartly automated but isn’t really pretty and the environment is set for all terminals.",[439,68029,68030,68031,68037],{},"The way I will present here isn’t new, in fact ",[1002,68032,68036],{"href":68033,"rel":68034,"title":68035},"http://rvm.io",[1006],"rvm","http://rvm.io/"," probably does something very\nsimilar for Ruby (and is a lot more fleshed out most likely). But the technique is quite nice and makes switching\nenvironments quite unproblematic.",[439,68039,68040],{},"Having multiple users use the same kind of environment of course takes a little standardizing. The software for the\nbuild environment should be located at the same place for example. Our DevOps use Puppet to automate that, but that kind\nof automation isn’t really a requirement to use anything in this post.",[439,68042,68043],{},"Lets have a look how the heart of the technique looks like:",[464,68045,68047],{"className":466,"code":68046,"language":468,"meta":469,"style":469},"$ foo() { echo foo; }\n$ PROMPT_COMMAND=foo\nfoo\n$\n",[471,68048,68049,68068,68075,68080],{"__ignoreMap":469},[474,68050,68051,68053,68056,68058,68061,68063,68065],{"class":476,"line":477},[474,68052,59935],{"class":480},[474,68054,68055],{"class":484}," foo",[474,68057,46312],{"class":503},[474,68059,68060],{"class":484},"{",[474,68062,12478],{"class":484},[474,68064,68055],{"class":484},[474,68066,68067],{"class":503},"; }\n",[474,68069,68070,68072],{"class":476,"line":507},[474,68071,59935],{"class":480},[474,68073,68074],{"class":484}," PROMPT_COMMAND=foo\n",[474,68076,68077],{"class":476,"line":547},[474,68078,68079],{"class":480},"foo\n",[474,68081,68082],{"class":476,"line":584},[474,68083,68084],{"class":480},"$\n",[439,68086,68087],{},"Bash lets you run a command before each prompt. The command is able to influence the current environment – this is\nimportant, as you want to set the profile for the current shell and don’t want to spawn subshells all the time. It can\nalso set how the next prompt will look like, using the PS1 environment variable.",[439,68089,68090],{},"Lets take that idea, put it in a file and build a little bit of code around it to build a quite basic profile switcher (\nwhich we will call profilehandler or ph for short).",[464,68092,68094],{"className":466,"code":68093,"language":468,"meta":469,"style":469},"profilehandler() {\n if [ x\"$PH_PROFILE\" = x\"new\" ]; then\n export JAVA_HOME=/opt/software/jdk/jdk1.7.0_04\n elif [ x\"$PH_PROFILE\" = x\"old\" ]; then\n export JAVA_HOME=/opt/software/jdk/jdk1.5.0_22\n fi\n if [ -n \"$PH_PROFILE\" ]; then\n export PATH=\"$JAVA_HOME/bin:$PATH\"\n fi\n export PS1=\"${PH_PROFILE:+[$PH_PROFILE] }\\$ \"\n}\nPROMPT_COMMAND=profilehandler\n",[471,68095,68096,68103,68129,68142,68166,68177,68181,68200,68222,68226,68258,68262],{"__ignoreMap":469},[474,68097,68098,68101],{"class":476,"line":477},[474,68099,68100],{"class":480},"profilehandler",[474,68102,15227],{"class":503},[474,68104,68105,68107,68110,68112,68115,68117,68119,68122,68125,68127],{"class":476,"line":507},[474,68106,2980],{"class":810},[474,68108,68109],{"class":503}," [ x",[474,68111,2289],{"class":484},[474,68113,68114],{"class":503},"$PH_PROFILE",[474,68116,2289],{"class":484},[474,68118,1661],{"class":810},[474,68120,68121],{"class":503}," x",[474,68123,68124],{"class":484},"\"new\"",[474,68126,2339],{"class":503},[474,68128,2308],{"class":810},[474,68130,68131,68134,68137,68139],{"class":476,"line":547},[474,68132,68133],{"class":810}," export",[474,68135,68136],{"class":503}," JAVA_HOME",[474,68138,811],{"class":810},[474,68140,68141],{"class":503},"/opt/software/jdk/jdk1.7.0_04\n",[474,68143,68144,68147,68149,68151,68153,68155,68157,68159,68162,68164],{"class":476,"line":584},[474,68145,68146],{"class":810}," elif",[474,68148,68109],{"class":503},[474,68150,2289],{"class":484},[474,68152,68114],{"class":503},[474,68154,2289],{"class":484},[474,68156,1661],{"class":810},[474,68158,68121],{"class":503},[474,68160,68161],{"class":484},"\"old\"",[474,68163,2339],{"class":503},[474,68165,2308],{"class":810},[474,68167,68168,68170,68172,68174],{"class":476,"line":607},[474,68169,68133],{"class":810},[474,68171,68136],{"class":503},[474,68173,811],{"class":810},[474,68175,68176],{"class":503},"/opt/software/jdk/jdk1.5.0_22\n",[474,68178,68179],{"class":476,"line":642},[474,68180,3037],{"class":810},[474,68182,68183,68185,68187,68190,68192,68194,68196,68198],{"class":476,"line":663},[474,68184,2980],{"class":810},[474,68186,2286],{"class":503},[474,68188,68189],{"class":810},"-n",[474,68191,2720],{"class":484},[474,68193,68114],{"class":503},[474,68195,2289],{"class":484},[474,68197,2339],{"class":503},[474,68199,2308],{"class":810},[474,68201,68202,68204,68207,68209,68211,68214,68217,68220],{"class":476,"line":694},[474,68203,68133],{"class":810},[474,68205,68206],{"class":503}," PATH",[474,68208,811],{"class":810},[474,68210,2289],{"class":484},[474,68212,68213],{"class":503},"$JAVA_HOME",[474,68215,68216],{"class":484},"/bin:",[474,68218,68219],{"class":503},"$PATH",[474,68221,2744],{"class":484},[474,68223,68224],{"class":476,"line":700},[474,68225,3037],{"class":810},[474,68227,68228,68231,68234,68236,68239,68242,68244,68247,68249,68252,68255],{"class":476,"line":913},[474,68229,68230],{"class":810}," export",[474,68232,68233],{"class":503}," PS1",[474,68235,811],{"class":810},[474,68237,68238],{"class":484},"\"${",[474,68240,68241],{"class":503},"PH_PROFILE",[474,68243,6562],{"class":810},[474,68245,68246],{"class":484},"+[",[474,68248,68114],{"class":503},[474,68250,68251],{"class":484},"] }",[474,68253,68254],{"class":510},"\\$",[474,68256,68257],{"class":484}," \"\n",[474,68259,68260],{"class":476,"line":920},[474,68261,703],{"class":503},[474,68263,68264,68267,68269],{"class":476,"line":926},[474,68265,68266],{"class":503},"PROMPT_COMMAND",[474,68268,811],{"class":810},[474,68270,68271],{"class":484},"profilehandler\n",[464,68273,68275],{"className":466,"code":68274,"language":468,"meta":469,"style":469},"$ source profilehandler.sh\n$ PH_PROFILE=new\n[new] $ echo $JAVA_HOME/bin # -> 1.7\n",[471,68276,68277,68287,68294],{"__ignoreMap":469},[474,68278,68279,68281,68284],{"class":476,"line":477},[474,68280,59935],{"class":480},[474,68282,68283],{"class":484}," source",[474,68285,68286],{"class":484}," profilehandler.sh\n",[474,68288,68289,68291],{"class":476,"line":507},[474,68290,59935],{"class":480},[474,68292,68293],{"class":484}," PH_PROFILE=new\n",[474,68295,68296,68299],{"class":476,"line":547},[474,68297,68298],{"class":503},"[new] $ echo $JAVA_HOME/bin ",[474,68300,68301],{"class":2277},"# -> 1.7\n",[439,68303,68304],{},[448,68305,68306],{},"Good:",[994,68308,68309,68312],{},[997,68310,68311],{},"Can set profile",[997,68313,68314],{},"Shows profile in path",[439,68316,68317],{},[448,68318,68319],{},"Bad:",[994,68321,68322,68325,68328],{},[997,68323,68324],{},"Ugly interface",[997,68326,68327],{},"Leaks paths into PATH",[997,68329,68330],{},"Profiles are specified directly in code",[439,68332,68333],{},"Lets refactor that a little and make it slightly easier and safer to use.",[464,68335,68337],{"className":466,"code":68336,"language":468,"meta":469,"style":469},"# set up where our software/profiles will reside\nPH_HOME=$HOME/tmp/ph\nprofilehandler() {\n local profilepath\n # check if we have a valid profile\n if [ -f \"$PH_HOME/profiles/$PH_PROFILE\" ]; then\n profilepath=\"$PH_HOME/profiles/$PH_PROFILE\"\n fi\n # clean up\n local cleanpath=\"$(echo \"$PATH\" | sed 's#\\('$PH_HOME'[^:]*:\\)\\|\\(::\\)##g')\"\n export PATH=\"$cleanpath\"\n # a profile can be loaded, do it\n if [ -n \"$profilepath\" ]; then\n . \"$profilepath\"\n export PATH=\"${JAVA_HOME:+$JAVA_HOME/bin:}$PATH\"\n fi\n # set prompt\n export PS1=\"${PH_PROFILE:+[$PH_PROFILE] }\\$ \"\n}\n",[471,68338,68339,68344,68356,68362,68370,68375,68399,68416,68420,68425,68456,68471,68476,68495,68506,68538,68542,68547,68571],{"__ignoreMap":469},[474,68340,68341],{"class":476,"line":477},[474,68342,68343],{"class":2277},"# set up where our software/profiles will reside\n",[474,68345,68346,68349,68351,68353],{"class":476,"line":507},[474,68347,68348],{"class":503},"PH_HOME",[474,68350,811],{"class":810},[474,68352,54869],{"class":503},[474,68354,68355],{"class":484},"/tmp/ph\n",[474,68357,68358,68360],{"class":476,"line":547},[474,68359,68100],{"class":480},[474,68361,15227],{"class":503},[474,68363,68364,68367],{"class":476,"line":584},[474,68365,68366],{"class":810}," local",[474,68368,68369],{"class":503}," profilepath\n",[474,68371,68372],{"class":476,"line":607},[474,68373,68374],{"class":2277}," # check if we have a valid profile\n",[474,68376,68377,68379,68381,68383,68385,68388,68391,68393,68395,68397],{"class":476,"line":642},[474,68378,2980],{"class":810},[474,68380,2286],{"class":503},[474,68382,56538],{"class":810},[474,68384,2720],{"class":484},[474,68386,68387],{"class":503},"$PH_HOME",[474,68389,68390],{"class":484},"/profiles/",[474,68392,68114],{"class":503},[474,68394,2289],{"class":484},[474,68396,2339],{"class":503},[474,68398,2308],{"class":810},[474,68400,68401,68404,68406,68408,68410,68412,68414],{"class":476,"line":663},[474,68402,68403],{"class":503}," profilepath",[474,68405,811],{"class":810},[474,68407,2289],{"class":484},[474,68409,68387],{"class":503},[474,68411,68390],{"class":484},[474,68413,68114],{"class":503},[474,68415,2744],{"class":484},[474,68417,68418],{"class":476,"line":694},[474,68419,3037],{"class":810},[474,68421,68422],{"class":476,"line":700},[474,68423,68424],{"class":2277}," # clean up\n",[474,68426,68427,68429,68432,68434,68436,68438,68440,68442,68444,68446,68448,68451,68453],{"class":476,"line":913},[474,68428,68366],{"class":810},[474,68430,68431],{"class":503}," cleanpath",[474,68433,811],{"class":810},[474,68435,55099],{"class":484},[474,68437,2470],{"class":510},[474,68439,2720],{"class":484},[474,68441,68219],{"class":503},[474,68443,55826],{"class":484},[474,68445,6911],{"class":810},[474,68447,14177],{"class":480},[474,68449,68450],{"class":484}," 's#\\('",[474,68452,68387],{"class":503},[474,68454,68455],{"class":484},"'[^:]*:\\)\\|\\(::\\)##g')\"\n",[474,68457,68458,68460,68462,68464,68466,68469],{"class":476,"line":920},[474,68459,68230],{"class":810},[474,68461,68206],{"class":503},[474,68463,811],{"class":810},[474,68465,2289],{"class":484},[474,68467,68468],{"class":503},"$cleanpath",[474,68470,2744],{"class":484},[474,68472,68473],{"class":476,"line":926},[474,68474,68475],{"class":2277}," # a profile can be loaded, do it\n",[474,68477,68478,68480,68482,68484,68486,68489,68491,68493],{"class":476,"line":932},[474,68479,2980],{"class":810},[474,68481,2286],{"class":503},[474,68483,68189],{"class":810},[474,68485,2720],{"class":484},[474,68487,68488],{"class":503},"$profilepath",[474,68490,2289],{"class":484},[474,68492,2339],{"class":503},[474,68494,2308],{"class":810},[474,68496,68497,68500,68502,68504],{"class":476,"line":938},[474,68498,68499],{"class":510}," .",[474,68501,2720],{"class":484},[474,68503,68488],{"class":503},[474,68505,2744],{"class":484},[474,68507,68508,68510,68512,68514,68516,68519,68521,68523,68525,68527,68530,68532,68534,68536],{"class":476,"line":944},[474,68509,68133],{"class":810},[474,68511,68206],{"class":503},[474,68513,811],{"class":810},[474,68515,68238],{"class":484},[474,68517,68518],{"class":503},"JAVA_HOME",[474,68520,6562],{"class":810},[474,68522,23407],{"class":484},[474,68524,68213],{"class":503},[474,68526,6875],{"class":810},[474,68528,68529],{"class":503},"bin",[474,68531,6562],{"class":810},[474,68533,19768],{"class":484},[474,68535,68219],{"class":503},[474,68537,2744],{"class":484},[474,68539,68540],{"class":476,"line":950},[474,68541,3037],{"class":810},[474,68543,68544],{"class":476,"line":956},[474,68545,68546],{"class":2277}," # set prompt\n",[474,68548,68549,68551,68553,68555,68557,68559,68561,68563,68565,68567,68569],{"class":476,"line":962},[474,68550,68230],{"class":810},[474,68552,68233],{"class":503},[474,68554,811],{"class":810},[474,68556,68238],{"class":484},[474,68558,68241],{"class":503},[474,68560,6562],{"class":810},[474,68562,68246],{"class":484},[474,68564,68114],{"class":503},[474,68566,68251],{"class":484},[474,68568,68254],{"class":510},[474,68570,68257],{"class":484},[474,68572,68573],{"class":476,"line":4876},[474,68574,703],{"class":503},[439,68576,68577],{},"Let the pain of that code dump subside a little bit, then proceed reading. We don’t do much more than the previous\nsnippet, just load the profile from an external file (in $PH_HOME/profiles) and make sure that the PATH gets cleaned up.\nThis method of clearing is quite fickle – you need to have all software in PH_HOME and certain edge cases do not get\nhandled – but for our simple requirements, that’s just fine.",[439,68579,68580],{},"Lets also create a nice UI so we can set our profile more elegantly.",[464,68582,68584],{"className":466,"code":68583,"language":468,"meta":469,"style":469},"# make a nice useable interface\nph() {\n case \"$1\" in\n set)\n if ! [ -f \"$PH_HOME/profiles/$2\" ]; then\n echo >&2 \"Bad profile: $2\"\n return\n fi\n export PH_PROFILE=\"$2\"\n ;;\n enable)\n ph set \"${2:-default}\"\n export oldPS1=\"$PS1\"\n export PROMPT_COMMAND=profilehandler\n ;;\n disable)\n unset PH_PROFILE\n unset PROMPT_COMMAND\n export PS1=\"$oldPS1\"\n ;;\n *)\n echo \"Usage: ph enable [profile]|disable|set profile\"\n esac\n}\n",[471,68585,68586,68591,68598,68612,68619,68645,68660,68665,68670,68686,68691,68698,68720,68736,68747,68751,68758,68766,68773,68788,68792,68797,68805,68810],{"__ignoreMap":469},[474,68587,68588],{"class":476,"line":477},[474,68589,68590],{"class":2277},"# make a nice useable interface\n",[474,68592,68593,68596],{"class":476,"line":507},[474,68594,68595],{"class":480},"ph",[474,68597,15227],{"class":503},[474,68599,68600,68603,68605,68608,68610],{"class":476,"line":547},[474,68601,68602],{"class":810}," case",[474,68604,2720],{"class":484},[474,68606,68607],{"class":510},"$1",[474,68609,2289],{"class":484},[474,68611,55153],{"class":810},[474,68613,68614,68617],{"class":476,"line":584},[474,68615,68616],{"class":55158}," set",[474,68618,1101],{"class":810},[474,68620,68621,68623,68626,68628,68630,68632,68634,68636,68639,68641,68643],{"class":476,"line":607},[474,68622,14286],{"class":810},[474,68624,68625],{"class":810}," !",[474,68627,2286],{"class":503},[474,68629,56538],{"class":810},[474,68631,2720],{"class":484},[474,68633,68387],{"class":503},[474,68635,68390],{"class":484},[474,68637,68638],{"class":510},"$2",[474,68640,2289],{"class":484},[474,68642,2339],{"class":503},[474,68644,2308],{"class":810},[474,68646,68647,68650,68653,68656,68658],{"class":476,"line":642},[474,68648,68649],{"class":510}," echo",[474,68651,68652],{"class":810}," >&2",[474,68654,68655],{"class":484}," \"Bad profile: ",[474,68657,68638],{"class":510},[474,68659,2744],{"class":484},[474,68661,68662],{"class":476,"line":663},[474,68663,68664],{"class":810}," return\n",[474,68666,68667],{"class":476,"line":694},[474,68668,68669],{"class":810}," fi\n",[474,68671,68672,68675,68678,68680,68682,68684],{"class":476,"line":700},[474,68673,68674],{"class":810}," export",[474,68676,68677],{"class":503}," PH_PROFILE",[474,68679,811],{"class":810},[474,68681,2289],{"class":484},[474,68683,68638],{"class":510},[474,68685,2744],{"class":484},[474,68687,68688],{"class":476,"line":913},[474,68689,68690],{"class":503}," ;;\n",[474,68692,68693,68696],{"class":476,"line":920},[474,68694,68695],{"class":55158}," enable",[474,68697,1101],{"class":810},[474,68699,68700,68703,68705,68707,68710,68713,68716,68718],{"class":476,"line":926},[474,68701,68702],{"class":480}," ph",[474,68704,10202],{"class":484},[474,68706,2720],{"class":484},[474,68708,68709],{"class":510},"${2",[474,68711,68712],{"class":810},":-",[474,68714,68715],{"class":503},"default",[474,68717,19768],{"class":510},[474,68719,2744],{"class":484},[474,68721,68722,68724,68727,68729,68731,68734],{"class":476,"line":932},[474,68723,68674],{"class":810},[474,68725,68726],{"class":503}," oldPS1",[474,68728,811],{"class":810},[474,68730,2289],{"class":484},[474,68732,68733],{"class":503},"$PS1",[474,68735,2744],{"class":484},[474,68737,68738,68740,68743,68745],{"class":476,"line":938},[474,68739,68674],{"class":810},[474,68741,68742],{"class":503}," PROMPT_COMMAND",[474,68744,811],{"class":810},[474,68746,68271],{"class":503},[474,68748,68749],{"class":476,"line":944},[474,68750,68690],{"class":503},[474,68752,68753,68756],{"class":476,"line":950},[474,68754,68755],{"class":55158}," disable",[474,68757,1101],{"class":810},[474,68759,68760,68763],{"class":476,"line":956},[474,68761,68762],{"class":510}," unset",[474,68764,68765],{"class":484}," PH_PROFILE\n",[474,68767,68768,68770],{"class":476,"line":962},[474,68769,68762],{"class":510},[474,68771,68772],{"class":484}," PROMPT_COMMAND\n",[474,68774,68775,68777,68779,68781,68783,68786],{"class":476,"line":4876},[474,68776,68674],{"class":810},[474,68778,68233],{"class":503},[474,68780,811],{"class":810},[474,68782,2289],{"class":484},[474,68784,68785],{"class":503},"$oldPS1",[474,68787,2744],{"class":484},[474,68789,68790],{"class":476,"line":4888},[474,68791,68690],{"class":503},[474,68793,68794],{"class":476,"line":4900},[474,68795,68796],{"class":810}," *)\n",[474,68798,68799,68802],{"class":476,"line":4913},[474,68800,68801],{"class":510}," echo",[474,68803,68804],{"class":484}," \"Usage: ph enable [profile]|disable|set profile\"\n",[474,68806,68807],{"class":476,"line":4921},[474,68808,68809],{"class":810}," esac\n",[474,68811,68812],{"class":476,"line":4932},[474,68813,703],{"class":503},[439,68815,68816],{},"We now have some sanity checking, a way to enable and disable our environment switcher and some amount of cleanup code\nso we get our old prompt back.",[439,68818,68819],{},[448,68820,68306],{},[994,68822,68823,68825,68827,68830],{},[997,68824,68311],{},[997,68826,68314],{},[997,68828,68829],{},"Nicer interface",[997,68831,68832],{},"Profiles easily addable",[439,68834,68835],{},[448,68836,68319],{},[994,68838,68839,68842],{},[997,68840,68841],{},"Insufficient cleanup (PATH retains Java path on disable)",[997,68843,68844],{},"Lacks features",[439,68846,68847],{},"But hey, that’s only our second take. And there is much more room for improvement.",[994,68849,68850,68853,68856,68859,68862,68865,68868,68871],{},[997,68851,68852],{},"Automatic profile switching on project directories (maybe implemented by a dotfile in the project directory – the\nprofilehandler can search down the stack for that and set the profile accordingly)",[997,68854,68855],{},"Local and global profile paths, so shared profiles can be distributed easily and users can override global settings",[997,68857,68858],{},"Configuring how the prompt looks",[997,68860,68861],{},"Showing the branch of your favorite SCM inside the prompt",[997,68863,68864],{},"Allow profiles to do their own cleanup/initialize",[997,68866,68867],{},"Support more than just Java",[997,68869,68870],{},"List available profiles",[997,68872,68873],{},"Much more exhaustive environment cleanup on disable",[439,68875,68876],{},"Having the capability to call a function before each prompt and in the scope of the current shell is very handy. But\nthat also means you have a limitation on how much you can do. Adding features is a good thing, but you have to take care\nto not extend the running time to a user-noticeable amount. You should make sure to only call programs which are sure\nto return in a short amount of time. If you are working partly on a network mounted disk, keep in mind that this could\npotentially block your shell for an extended amount of time. If you’re using automount and something (like looking up\nthe current GIT branch) in your profilehandler is looking at each directory up to the root, this also can hurt\nperformance. Also, while extending the profilehandler, if you get a stuck shell which does not show a prompt anymore,\nkeep an eye open for programs reading from stdin.",[439,68878,68879],{},"No matter which method you are using, it is probably a good idea to document your environment settings so everyone in\nthe project has a similar setup. And automating that process also sounds like a very good idea. So have fun playing\naround!",[1024,68881,68882],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":469,"searchDepth":507,"depth":507,"links":68884},[],[9045,1030],"2012-07-23T12:47:58","Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a\\nconstant struggle to keep your programming environment in sync with what you are actually doing. For that big legacy\\nproduct you are maintaining you might need an old Java 1.5 in a specific version – for that fancy new web-app you\\nmight be using the newest Java.","https://synyx.de/blog/consolidating-development-environments-a-bash-magic-tutorial/",{},"/blog/consolidating-development-environments-a-bash-magic-tutorial",{"title":67986,"description":67995},"blog/consolidating-development-environments-a-bash-magic-tutorial",[468,15546,14132,68894,54687],"profiles","Developers have a tendency to not only work on a single project at once. Depending on those projects, there is a constant struggle to keep your programming environment in sync…","1DO3SrSal0CaXAOb3LB9CmmXX8wDLM5skOhuqLYUQd4",{"id":68898,"title":68899,"author":68900,"body":68901,"category":68987,"date":68988,"description":68910,"extension":1034,"link":68989,"meta":68990,"navigation":916,"path":68991,"seo":68992,"slug":68905,"stem":68993,"tags":68994,"teaser":68995,"__hash__":68996},"blog/blog/ein-jahr-bei-synyx-aus-sicht-eines-studenten.md","Ein Jahr bei synyx aus Sicht eines Studenten.",[418],{"type":432,"value":68902,"toc":68985},[68903,68906,68911,68914,68917,68925,68930,68933,68936,68939,68942,68945,68948,68953,68956,68966,68976,68979,68982],[435,68904,68899],{"id":68905},"ein-jahr-bei-synyx-aus-sicht-eines-studenten",[439,68907,68908],{},[448,68909,68910],{},"Und wo ist das Büro?",[439,68912,68913],{},"Nach meiner Bewerbung bei synyx, wurde ich nach einer detaillierteren Auflistung meiner schon durchgeführten Projekte\ngefragt, die ich in der Bewerbung erwähnt hatte. Natürlich habe ich alles aufgezählt, an das ich einen Finger angesetzt\nhatte. So vermittelte ich zwar vielseitiges Interesse an Entwicklung in der Java-Umgebung, hatte jedoch wenige\nKenntnisse über Frameworks und Tools. Das Gespräch durfte ich mit Thomas und Flo halten. Immerhin ein Chef mit im\nMeeting, Grund genug, aufgeregt zu sein. Mein erster Gedanke jedoch war: “Zum Glück hab ich keinen Anzug angezogen,\nsogar das Hemd hätte ich im Schrank lassen können.” Nach der Vorstellung der Firma und einem Gespräch über meine\nKenntnisse und Interessen wurde mir das Büro gezeigt.",[439,68915,68916],{},"Ich war mir nicht sicher, ob ich mich wirklich in einem Büro befinde oder in einer von Nerds eingerichteten Wohnung.",[439,68918,68919,68920,68924],{},"Angefangen bei einem Poster von ",[1002,68921,28],{"href":68922,"rel":68923,"title":28},"http://www.fabianbuch.com/",[1006]," (mit 3d Brille), über ominöse\nKalender in einem von männlichen Entwicklern beherrschten Büroraum, einem riesigen Aquarium, natürlich mit Webcam, einer\nCafé Lounge mit Retro-Games bis hin zu einer großen Küche mit ebenso großer Getränkeauswahl und leckerem Kaffee. Das\nalles vermittelte in einem Altbau mit hohen Stuckdecken und Parkettboden ein heimeliges Gefühl.",[439,68926,68927],{},[448,68928,68929],{},"Lernen, lernen, lernen",[439,68931,68932],{},"Durch Basteleien an unserer ehemaligen Homepage wurde ich an das Content Management System OpenCMS herangeführt. Hier\nimplementierte ich einen RSS Reader, der unsere Beiträge aus dem externen Blog in Kurzform auf der Homepage darstellt.\nDer Struktur von OpenCMS angenähert, wurde ich in ein Produktiv-Projekt integriert und kam das erste Mal mit Scrum in\nBerührung. Seitdem will ich Dailies nicht mehr missen. Strukturiert, immer auf dem neuesten Stand und als Student\ngleichermaßen wahrgenommen wie Projektleiter oder Festangestellter, programmiert es sich am einfachsten (oder am\nliebsten?).",[439,68934,68935],{},"Dazu kommt ein wöchentliches Freitagsmeeting, wodurch jeder einen Überblick über Entwicklungen in allen anderen\nProjekten bekommt und Berührungspunkte bemerkt. Damit werden wiederkehrende Probleme schnell identifiziert und gelöst.\nAußerdem werden Führungsentscheidungen kommuniziert was z.B. Sales oder HR angeht, bzw. Themen wie der aktuell\nanstehende Umzug diskutiert.",[439,68937,68938],{},"Tools, die ich durch synyx intensiver, teils durch täglichen Gebrauch, (kennen-)gelernt habe: ChilliProject (früher\nRedmine) zur Projektverwaltung, Maven (bald vllt auch mal gradle) als Buildtool, svn und git zur Versionierung, Nexus,\nSonar, Jenkins, Konzepte wie Continuous-[Deployment|Integration|Delivery].",[439,68940,68941],{},"Allgemein wird hier, nicht zuletzt durch ein internes Code Projekt und interne Schulungen (schule@synyx), sehr viel Wert\nauf Codequalität gelegt.",[439,68943,68944],{},"Mittlerweile habe ich in diversen Projekten ausgeholfen und da man als Student genauso behandelt wird wie feste\nMitarbeiter, kam es auch mal zu der Situation, dass ich in meinem Projekt für den Kunden der einzige Ansprechpartner bei\neinem Deployment war. Aber man wächst ja mit Verantwortung!",[439,68946,68947],{},"Der gesamte Arbeitstag wird durch einen IRC Chat begleitet, der zur internen und externen Kommunikation verwendet wird.\nVon Essensplanung über lustige Webfundstücke zu schnell diskutierten Themen wird hier alles angerissen. Man braucht\nseine Zeit, bis man die teilweise sehr nerdigen Insider intus hat.",[439,68949,68950],{},[448,68951,68952],{},"Und täglich grüßt das Murmeltier, aber nur ohne (Eigen-)Initiative",[439,68954,68955],{},"Etwa alle zwei Wochen gehen die einigermaßen sportlichen Mitarbeiter, Ehemalige und Freunde zum synoccer. Das wird in\nletzter Zeit von mir auf den letzten Drücker organisiert, wenn ich es nicht mal vergesse. Dann heißt es in der Halle\noder auch mal auf einem Freiplatz den Chef tunneln!",[439,68957,68958,68959,68965],{},"Als nächstes größeres Projekt steht bei mir die Diplomarbeit an. Dafür darf ich mir aus unserem durch\ndas ",[1002,68960,68964],{"href":68961,"rel":68962,"title":68963},"http://www.ka-news.de/wirtschaft/karlsruhe/Karlsruher-Open-Source-Dienstleister-synyx-erhaelt-Foerderpreis;art127,907822",[1006],"synyx bei Ka-News","Zentrale Innovationsprogramm Mittelstand (ZIM)","\ngeförderten Projekt einen Aspekt herauspicken und in Angriff nehmen.",[439,68967,68968,68969,68975],{},"Unser Arbeitsplatz-, Konferenzbudget, montl. Stammtische, Sommer-, Winterfest für Mitarbeiter, Ehemalige und Verwandte\nund die",[1002,68970,68974],{"href":68971,"rel":68972,"title":68973},"http://kmu4family.mfg.de/de/best-practices/kochmuddi-und-co-bei-synyx-1.10804",[1006],"Kochmuddi und co","Kochmuddi","\nzeigen, dass auf die ganz eigene Firmenkultur nicht nur Wert gelegt wird, sie wird auch gelebt. Was wohl auch die\nMaklerin fand, als fast die ganze Belegschaft das potentielle neue Büro begutachtet hat.",[439,68977,68978],{},"Man sollte bei synyx dazulernen wollen und Initiative zeigen, dann ist man hier richtig, dann macht es Spaß.",[439,68980,68981],{},"Ich könnte noch viel mehr erzählen, aber der Beitrag ist jetzt schon viel länger geworden, als ich eigentlich wollte.",[439,68983,68984],{},"Fazit: Bei synyx werden die Haare länger, aber nicht grau (außer bei Oli ",{"title":469,"searchDepth":507,"depth":507,"links":68986},[],[1031],"2012-07-14T07:43:51","https://synyx.de/blog/ein-jahr-bei-synyx-aus-sicht-eines-studenten/",{},"/blog/ein-jahr-bei-synyx-aus-sicht-eines-studenten",{"title":68899,"description":68910},"blog/ein-jahr-bei-synyx-aus-sicht-eines-studenten",[],"Und wo ist das Büro? Nach meiner Bewerbung bei synyx, wurde ich nach einer detaillierteren Auflistung meiner schon durchgeführten Projekte gefragt, die ich in der Bewerbung erwähnt hatte. Natürlich habe ich…","tuYF2CWhwN1l8R1oTmug6AhrWxdML2H6FPM0a7Ne_1c",{"id":68998,"title":68999,"author":69000,"body":69001,"category":69040,"date":69041,"description":69008,"extension":1034,"link":69042,"meta":69043,"navigation":916,"path":69044,"seo":69045,"slug":69005,"stem":69046,"tags":69047,"teaser":69048,"__hash__":69049},"blog/blog/renaming-files-with-a-lot-of-spaces.md","Renaming files with a lot of spaces",[51],{"type":432,"value":69002,"toc":69038},[69003,69006,69009,69019,69024,69027,69030,69035],[435,69004,68999],{"id":69005},"renaming-files-with-a-lot-of-spaces",[439,69007,69008],{},"I just want to rename a bunch of files on a Linux-Streaming-Server.",[439,69010,69011,69012,69015,69016],{},"So my first approache was to substitute ",[990,69013,69014],{},"foo"," with ",[990,69017,69018],{},"bar",[439,69020,69021],{},[990,69022,69023],{},"for file in *; do mv $file `echo $file | sed -r “s/foo/bar/”`; done",[439,69025,69026],{},"But since the bash separator is a space this will not work with files which contains spaces.",[439,69028,69029],{},"So I have to remove or substitute the spaces before. I used translate (tr) to change the spaces to underlines:",[439,69031,69032],{},[990,69033,69034],{},"for file in *; do mv “$file” `echo $file | tr ‘ ‘ ‘_’`; done",[439,69036,69037],{},"An other approache is to change the bash seperator.",{"title":469,"searchDepth":507,"depth":507,"links":69039},[],[9045],"2012-07-13T17:02:37","https://synyx.de/blog/renaming-files-with-a-lot-of-spaces/",{},"/blog/renaming-files-with-a-lot-of-spaces",{"title":68999,"description":69008},"blog/renaming-files-with-a-lot-of-spaces",[],"I just want to rename a bunch of files on a Linux-Streaming-Server. So my first approache was to substitute foo with bar for file in *; do mv $file `echo…","mJvywco2SiTqy_0VjayMoQ6DtSnIvZ2QOBFTZ3HH7V0",{"id":69051,"title":69052,"author":69053,"body":69054,"category":69092,"date":69093,"description":69094,"extension":1034,"link":69095,"meta":69096,"navigation":916,"path":69097,"seo":69098,"slug":69058,"stem":69099,"tags":69100,"teaser":69102,"__hash__":69103},"blog/blog/synyx-open-source-projekte-jetzt-auf-github.md","synyx Open Source Projekte jetzt auf GitHub",[172],{"type":432,"value":69055,"toc":69090},[69056,69059,69062,69078,69081],[435,69057,69052],{"id":69058},"synyx-open-source-projekte-jetzt-auf-github",[439,69060,69061],{},"Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf\nsynyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer User, nahm viel Zeit in\nAnspruch, und ehrlich gesagt, programmieren wir lieber als User zu verwalten.",[439,69063,69064,69065,18762,69071,69077],{},"Seit Ende letzten Jahres haben wir einige Projekte auch auf GitHub veröffentlicht. Bis jetzt liefen beide Plattformen\nparallel. Wir haben uns entschieden, synyx.org abzuschalten, und unsere Open Source Projekte komplett auf GitHub\numzuziehen. Die Vorteile liegen eindeutig auf der Hand: GitHub bietet einfache Mechanismen,\nwie ",[1002,69066,69070],{"href":69067,"rel":69068,"title":69069},"https://de.wikipedia.org/wiki/Abspaltung_%28Softwareentwicklung%29",[1006],"Wikipedia: Forking / Abspaltung","Forks",[1002,69072,69076],{"href":69073,"rel":69074,"title":69075},"http://www.heise.de/glossar/entry/Git-Pull-Request-397971.html",[1006],"Heise: Pull Request","Pull Requests",", an um\nInteressierten die Möglichkeit zu bieten an unseren Projekten mitzuarbeiten.",[439,69079,69080],{},"Dank GitHub API, svn2git, Textile/Markdown in Gollum und Git war die Migration vom Redmine-basierten synyx.org zu\nGitHub relativ problemlos.",[439,69082,69083,69084,69089],{},"Wer an unseren Open Source Projekten Interesse hat, kann sich\nauf ",[1002,69085,69086],{"href":69086,"rel":69087,"title":69088},"https://github.com/synyx/",[1006],"GitHub synyx"," einfach mal umschauen.",{"title":469,"searchDepth":507,"depth":507,"links":69091},[],[1031],"2012-06-26T11:00:57","Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf\\nsynyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer User, nahm viel Zeit in\\nAnspruch, und ehrlich gesagt, programmieren wir lieber als User zu verwalten.","https://synyx.de/blog/synyx-open-source-projekte-jetzt-auf-github/",{},"/blog/synyx-open-source-projekte-jetzt-auf-github",{"title":69052,"description":69061},"blog/synyx-open-source-projekte-jetzt-auf-github",[22987,26636,69101],"redmine","Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf synyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer…","-2nTQFX94lk0-P6SIZtLNuZgY8Yx4uf4o138Cj--5IA",{"id":69105,"title":69106,"author":69107,"body":69108,"category":69142,"date":69143,"description":69115,"extension":1034,"link":69144,"meta":69145,"navigation":916,"path":69146,"seo":69147,"slug":69148,"stem":69149,"tags":69150,"teaser":69151,"__hash__":69152},"blog/blog/kochen-fur-die-kochmuddi.md","Kochen für die Kochmuddi",[262],{"type":432,"value":69109,"toc":69140},[69110,69113,69116,69119,69122,69125,69128,69131,69134,69137],[435,69111,69106],{"id":69112},"kochen-für-die-kochmuddi",[439,69114,69115],{},"Gestern gab es bei synyx ein besonderes Event!",[439,69117,69118],{},"Unsere Kochmuddi hatte gestern ihren 50. Geburtstag. Das haben wir uns natürlich zum Anlass genommen, ihr einmal eine\nbesondere Freude zu machen und uns für das viele leckere Essen bei ihr zu bedanken.",[439,69120,69121],{},"Die Planung startete bereits letzte Woche. Was können wir machen? Was soll man ihr schenken? Aus den Reihen der Kollegen\nkam dann die Idee, warum kochen wir nicht einfach für unsere Kochmuddi? Die Idee fand großen Anklang und so überlegten\nwir uns, was wir denn Gutes zaubern könnten. Schnell stand fest, es sollte was regionales sein. Die Wahl fiel dann auf\nKartoffelsuppe mit Apfelküchle.",[439,69123,69124],{},"Nun war noch die Frage: Wie können wir unsere Kochmuddi davon abhalten zu kochen und sie trotzdem unter einem Vorwand\nherlocken? Und noch wichtiger, sie sollte davon ja nichts mitbekommen!",[439,69126,69127],{},"So ganz ohne kleine Schwindelei ging das dann doch nicht. Daher haben wir sie am Dienstag angerufen und ihr erklärt,\ndass sich keiner zum Essen angemeldet habe. Das hat sie doch ersteinmal geschockt und ihr leider eine etwas schlaflose\nNacht bereitet, da sie sich Sorgen gemacht hat, ob das letzte Essen so schlecht war. Wir erklärten ihr dann noch, sie\nmüsse aber trotzdem kommen, da wir noch einen wichtigen Kunden erwarten würden und die Meetingräume doch ganz dringend\nnoch gereinigt werden müssten. Das würde erst um 12.30 Uhr gehen, da vorher noch ein anderes Meeting stattfindet. Zum\nGlück hat sie es uns abgekauft!",[439,69129,69130],{},"Bereits am Dienstag wurde schon der Großeinkauf erledigt. Unser Chef Thomas stand sogar Dienstag Nachmittag in der Küche\nund machte ein tolles Dessert. Beim zweiten Versuch klappte es auch mit der Sahne, die vorher zu Butter geworden war (\nAnmerkung: Wir könnten uns an ein Dessert a la Chef gewöhnen).",[439,69132,69133],{},"Viele Helfer standen Mittwoch früh mit Küchenwerkzeug bewaffnet schon um 9 Uhr bereit, damit alles pünktlich fertig\nwerden konnte. So legten wir Morgens gleich los mit Kartoffeln schälen und schnippeln. Einen Berg von 9 kg hatten wir\nvor uns liegen. Immerhin mussten ca. 24 hungrige Mäuler gestopft werden. Wir kamen uns vor, wie in einer Großküche.\nAnschließend mussten noch die 26 Äpfel geschält, entkernt und geschnitten werden, sowie der Teig für die Apfelküchle\nangerührt werden. 30 Eier und 1,5 kg Mehl, was für ein Act! In Rekordzeit schafften wir es, die ganzen Apfelküchle noch\nrechtzeitig zum Essen auszubraten. Dank der kleinen Verspätung unserer Kochmuddi war also wirklich alles fertig, als sie\nkam.",[439,69135,69136],{},"Sie schaute schon etwas verdutzt, dass sich so viele in der Küche tummelten als sie ankam. Kurzerhand verfrachteten wir\nunsere Kochmuddi auf einen Stuhl, gratulierten ihr und unsere Chefs überreichten ihr das Present, dass wir noch\norganisiert hatten. Dabei erklärten wir ihr auch, dass sie heute mit uns mitessen muss, da wir extra für sie gekocht\nhaben. Unsere Kochmuddi war völlig sprachlos und mit Tränen in den Augen. Selbstverständlich freute sich jeder, dass die\nÜberraschung so gut geglückt war und sie sich so darüber gefreut hat. Sogar unser Essen hat ihr sehr gut geschmeckt.",[439,69138,69139],{},"Aber ganz ehrlich… wir sind froh, dass wir den Job nicht machen müssen und für die Menge kochen! Hoffentlich kommt sie\nganz schnell aus ihrem verdienten Urlaub wieder und kann uns dann alle wieder köstlich bekochen! Kochmuddi, du bist die\nBeste und hoffentlich bleibst du uns noch lange bei synyx erhalten!",{"title":469,"searchDepth":507,"depth":507,"links":69141},[],[1031],"2012-06-14T14:54:40","https://synyx.de/blog/kochen-fur-die-kochmuddi/",{},"/blog/kochen-fur-die-kochmuddi",{"title":69106,"description":69115},"kochen-fur-die-kochmuddi","blog/kochen-fur-die-kochmuddi",[],"Gestern gab es bei synyx ein besonderes Event! Unsere Kochmuddi hatte gestern ihren 50. Geburtstag. Das haben wir uns natürlich zum Anlass genommen, ihr einmal eine besondere Freude zu machen…","uOWQAEe8MI4bip4n5brPYQCbmOpfdWXtKiDdqdreHOA",{"id":69154,"title":69155,"author":69156,"body":69157,"category":69409,"date":69410,"description":69411,"extension":1034,"link":69412,"meta":69413,"navigation":916,"path":69414,"seo":69415,"slug":69161,"stem":69416,"tags":69417,"teaser":69422,"__hash__":69423},"blog/blog/scheduling-and-asynchronous-execution-with-spring.md","Scheduling and asynchronous execution with Spring",[30],{"type":432,"value":69158,"toc":69403},[69159,69162,69165,69169,69172,69197,69201,69204,69207,69231,69234,69256,69259,69281,69284,69306,69309,69318,69321,69330,69334,69337,69340,69364,69367,69389,69393,69401],[435,69160,69155],{"id":69161},"scheduling-and-asynchronous-execution-with-spring",[439,69163,69164],{},"You want to execute cron jobs or call your methods asynchronously? Thanks to Spring’s annotation support for scheduling\nand asynchronous execution you can achieve this in a few minutes.",[1065,69166,69168],{"id":69167},"some-xml-magic","Some xml magic",[439,69170,69171],{},"At first define your task executor and scheduler. The following lines will create an instance of ThreadPoolTaskExecutor\nand an instance of ThreadPoolTaskScheduler with the given pool sizes. The task element annotation-driven allows you to\nuse Spring’s annotations for scheduling and asynchronous execution within the beans defined in your application context.",[464,69173,69175],{"className":6253,"code":69174,"language":6255,"meta":469,"style":469},"\u003Cbean id=\"myClass\" class=\"my.project.path.myClass\" />\n\u003Ctask:annotation-driven executor=\"myExecutor\" scheduler=\"myScheduler\" />\n\u003Ctask:executor id=\"myExecutor\" pool-size=\"5\" />\n\u003Ctask:scheduler id=\"myScheduler\" pool-size=\"10\" />\n",[471,69176,69177,69182,69187,69192],{"__ignoreMap":469},[474,69178,69179],{"class":476,"line":477},[474,69180,69181],{},"\u003Cbean id=\"myClass\" class=\"my.project.path.myClass\" />\n",[474,69183,69184],{"class":476,"line":507},[474,69185,69186],{},"\u003Ctask:annotation-driven executor=\"myExecutor\" scheduler=\"myScheduler\" />\n",[474,69188,69189],{"class":476,"line":547},[474,69190,69191],{},"\u003Ctask:executor id=\"myExecutor\" pool-size=\"5\" />\n",[474,69193,69194],{"class":476,"line":584},[474,69195,69196],{},"\u003Ctask:scheduler id=\"myScheduler\" pool-size=\"10\" />\n",[1065,69198,69200],{"id":69199},"the-scheduled-annotation","The @Scheduled annotation",[439,69202,69203],{},"With the @Scheduled annotation you can execute your method as a cron job. Using this annotation requires that the method\nto be scheduled must be of type void and must not expect any arguments. The following examples show you how to use the\n@Scheduled annotation.",[439,69205,69206],{},"If you want periodic scheduling you can use the property fixedRate. In this example the method would be executed every\n42 seconds.",[464,69208,69210],{"className":709,"code":69209,"language":711,"meta":469,"style":469},"@Scheduled(fixedRate = 42000)\npublic void execute() {\n // do something\n}\n",[471,69211,69212,69217,69222,69227],{"__ignoreMap":469},[474,69213,69214],{"class":476,"line":477},[474,69215,69216],{},"@Scheduled(fixedRate = 42000)\n",[474,69218,69219],{"class":476,"line":507},[474,69220,69221],{},"public void execute() {\n",[474,69223,69224],{"class":476,"line":547},[474,69225,69226],{}," // do something\n",[474,69228,69229],{"class":476,"line":584},[474,69230,703],{},[439,69232,69233],{},"If you prefer cron expressions you can use them either. The following example is analogue to the example above only\nusing cron expressions. The annotated method would be executed each full minute and every 7 seconds.",[464,69235,69237],{"className":709,"code":69236,"language":711,"meta":469,"style":469},"@Scheduled(cron = \"*/7 * * * * *\")\npublic void execute() {\n // do something\n}\n",[471,69238,69239,69244,69248,69252],{"__ignoreMap":469},[474,69240,69241],{"class":476,"line":477},[474,69242,69243],{},"@Scheduled(cron = \"*/7 * * * * *\")\n",[474,69245,69246],{"class":476,"line":507},[474,69247,69221],{},[474,69249,69250],{"class":476,"line":547},[474,69251,69226],{},[474,69253,69254],{"class":476,"line":584},[474,69255,703],{},[439,69257,69258],{},"Without question you have much more possibilities with cron expressions than with periodic scheduling. In this example\nyour method would be executed every weekday (Monday to Friday) on 9.45 am.",[464,69260,69262],{"className":709,"code":69261,"language":711,"meta":469,"style":469},"@Scheduled(cron = \"0 45 9 * * MON-FRI\")\npublic void execute() {\n // do something\n}\n",[471,69263,69264,69269,69273,69277],{"__ignoreMap":469},[474,69265,69266],{"class":476,"line":477},[474,69267,69268],{},"@Scheduled(cron = \"0 45 9 * * MON-FRI\")\n",[474,69270,69271],{"class":476,"line":507},[474,69272,69221],{},[474,69274,69275],{"class":476,"line":547},[474,69276,69226],{},[474,69278,69279],{"class":476,"line":584},[474,69280,703],{},[439,69282,69283],{},"A pretty cool feature is that you even can use placeholders for your cron expression which are resolved against the\nconfigured property-placeholder.",[464,69285,69287],{"className":709,"code":69286,"language":711,"meta":469,"style":469},"@Scheduled(cron = \"${myclass.cron.execute.sth}\")\npublic void execute() {\n // do something\n}\n",[471,69288,69289,69294,69298,69302],{"__ignoreMap":469},[474,69290,69291],{"class":476,"line":477},[474,69292,69293],{},"@Scheduled(cron = \"${myclass.cron.execute.sth}\")\n",[474,69295,69296],{"class":476,"line":507},[474,69297,69221],{},[474,69299,69300],{"class":476,"line":547},[474,69301,69226],{},[474,69303,69304],{"class":476,"line":584},[474,69305,703],{},[439,69307,69308],{},"Define where the properties are loaded from within your application context:",[464,69310,69312],{"className":6253,"code":69311,"language":6255,"meta":469,"style":469},"\u003Ccontext:property-placeholder location=\"classpath:application.properties\"/>\n",[471,69313,69314],{"__ignoreMap":469},[474,69315,69316],{"class":476,"line":477},[474,69317,69311],{},[439,69319,69320],{},"Then the properties are loaded from the file which contains your cron-expressions like this:",[464,69322,69324],{"className":16895,"code":69323,"language":16897,"meta":469,"style":469},"myclass.cron.execute.sth=0 45 9 * * MON-FRI\n",[471,69325,69326],{"__ignoreMap":469},[474,69327,69328],{"class":476,"line":477},[474,69329,69323],{},[1065,69331,69333],{"id":69332},"the-async-annotation","The @Async annotation",[439,69335,69336],{},"The @Async annotation allows you to invoke your method asynchronously. The execution of the method will occur in a task\nthat has been submitted to the TaskExecutor defined in your application context. Contrary to the methods annotated with\nthe @Scheduled annotations the methods you annotate with @Async may be of other type than void and can expect arguments.",[439,69338,69339],{},"This is a simple example of a @Async annotated method without a return value.",[464,69341,69343],{"className":709,"code":69342,"language":711,"meta":469,"style":469},"@Async\nvoid execute(String string) {\n // do something asynchronously\n}\n",[471,69344,69345,69350,69355,69360],{"__ignoreMap":469},[474,69346,69347],{"class":476,"line":477},[474,69348,69349],{},"@Async\n",[474,69351,69352],{"class":476,"line":507},[474,69353,69354],{},"void execute(String string) {\n",[474,69356,69357],{"class":476,"line":547},[474,69358,69359],{}," // do something asynchronously\n",[474,69361,69362],{"class":476,"line":584},[474,69363,703],{},[439,69365,69366],{},"Like mentioned above your @Async annotated method may have a return value. However this return value must be of type\nFuture. This means that first the other tasks are performed and then is called get() on that Future.",[464,69368,69370],{"className":709,"code":69369,"language":711,"meta":469,"style":469},"@Async\nFuture\u003CString> execute(String string) {\n // do something asynchronously\n}\n",[471,69371,69372,69376,69381,69385],{"__ignoreMap":469},[474,69373,69374],{"class":476,"line":477},[474,69375,69349],{},[474,69377,69378],{"class":476,"line":507},[474,69379,69380],{},"Future\u003CString> execute(String string) {\n",[474,69382,69383],{"class":476,"line":547},[474,69384,69359],{},[474,69386,69387],{"class":476,"line":584},[474,69388,703],{},[1065,69390,69392],{"id":69391},"further-information","Further information",[439,69394,69395,69396],{},"Have a look at\nthe ",[1002,69397,69400],{"href":69398,"rel":69399},"http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#scheduling",[1006],"Spring Framework Reference Documentation",[1024,69402,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":69404},[69405,69406,69407,69408],{"id":69167,"depth":547,"text":69168},{"id":69199,"depth":547,"text":69200},{"id":69332,"depth":547,"text":69333},{"id":69391,"depth":547,"text":69392},[13208,1030],"2012-06-13T15:49:15","You want to execute cron jobs or call your methods asynchronously? Thanks to Spring’s annotation support for scheduling\\nand asynchronous execution you can achieve this in a few minutes.","https://synyx.de/blog/scheduling-and-asynchronous-execution-with-spring/",{},"/blog/scheduling-and-asynchronous-execution-with-spring",{"title":69155,"description":69164},"blog/scheduling-and-asynchronous-execution-with-spring",[15247,69418,69419,69420,1426,69421],"scheduled","asynchronous","scheduling","spring-annotations","You want to execute cron jobs or call your methods asynchronously? Thanks to Spring’s annotation support for scheduling and asynchronous execution you can achieve this in a few minutes. Some…","fzW4lb1yw0kNbXB83aAmQzrzjrslpT-gtiBvUzbdR7Q",{"id":69425,"title":69426,"author":69427,"body":69428,"category":69593,"date":69594,"description":69595,"extension":1034,"link":69596,"meta":69597,"navigation":916,"path":69598,"seo":69599,"slug":69432,"stem":69600,"tags":69601,"teaser":69603,"__hash__":69604},"blog/blog/performance-tuning-maven-opencms-builds-using-postgresql.md","Performance tuning Maven-OpenCms builds using PostgreSQL",[247],{"type":432,"value":69429,"toc":69591},[69430,69433,69436,69461,69464,69471,69478,69493,69511,69518,69542,69548,69572,69575,69589],[435,69431,69426],{"id":69432},"performance-tuning-maven-opencms-builds-using-postgresql",[439,69434,69435],{},"Having a newly installed Ubuntu 12.04 on my machine, I noticed that building the OpenCms project I am currently working\non is a very time consuming process:",[464,69437,69439],{"className":16895,"code":69438,"language":16897,"meta":469,"style":469},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 12m34.748s\nuser 2m10.132s\nsys 0m6.836s\n",[471,69440,69441,69446,69451,69456],{"__ignoreMap":469},[474,69442,69443],{"class":476,"line":477},[474,69444,69445],{},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\n",[474,69447,69448],{"class":476,"line":507},[474,69449,69450],{},"real 12m34.748s\n",[474,69452,69453],{"class":476,"line":547},[474,69454,69455],{},"user 2m10.132s\n",[474,69457,69458],{"class":476,"line":584},[474,69459,69460],{},"sys 0m6.836s\n",[439,69462,69463],{},"Uh, more than 12 minutes… definitely too much; even for an OpenCms project.",[439,69465,69466,69467],{},"During the Maven builds install phase a lot of database write operations are performed: after an OpenCms module has been\nbuilt (it’s a multi-module project so there are more than one OpenCms modules), each OpenCms module gets deleted,\nre-imported and finally published into the OpenCms VFS. For more details on how we do build automation for OpenCms\nprojects with Maven, see ",[1002,69468,22916],{"href":69469,"rel":69470},"http://blog.synyx.de/2011/04/maven-and-opencms/",[1006],[439,69472,69473,69474,69477],{},"I searched the web and quickly found the reason for this performance issue: on my machine I have an ext4 file system;\nthe PostgreSQL server version installed is 9.1. And thats where PostgreSQLs ",[990,69475,69476],{},"Write Ahead Log"," (WAL) configuration\nsettings become interesting. In short, the PostgreSQL server uses synchronous commits by default, which means that\nPostgreSQL waits for ext4 to confirm that page images have been written to the permanent WAL storage on disk.",[439,69479,69480,69481,69484,69485,69488,69489,69492],{},"One solution is to set the configuration parameter ",[448,69482,69483],{},"fsync"," to value ",[448,69486,69487],{},"off"," (file postgresql.conf). This prevents\nPostgreSQL from performing any attempt to synchronize database write operations by never invoking the operating systems\nfsync() system call. This introduces the risk of ",[990,69490,69491],{},"data corruption"," in the event of a power failure or system crash.",[439,69494,69495,69496,69499,69500,69503,69504,69507,69508,11288],{},"Another option is switching to asynchronous transaction commits which means that the PostgreSQL server does no longer\nwait for confirmation that the transactions WAL records have been written on disk. Instead, it continues just after the\ntransaction commit is considered ",[990,69497,69498],{},"logically"," completed. This can be achieved by setting the configuration parameter *\n*synchronous*commit** to value *",[990,69501,69502],{},"off** (file postgresql.conf). This comes at the risk of _data loss"," (but not ",[990,69505,69506],{},"data\ncorruption",", as with ",[448,69509,69510],{},"fsync=off",[439,69512,69513,69514,69517],{},"After setting ",[448,69515,69516],{},"synchronous_commit=off"," the build process is much faster:",[464,69519,69521],{"className":16895,"code":69520,"language":16897,"meta":469,"style":469},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 1m47.170s\nuser 2m9.856s\nsys 0m6.688s\n",[471,69522,69523,69527,69532,69537],{"__ignoreMap":469},[474,69524,69525],{"class":476,"line":477},[474,69526,69445],{},[474,69528,69529],{"class":476,"line":507},[474,69530,69531],{},"real 1m47.170s\n",[474,69533,69534],{"class":476,"line":547},[474,69535,69536],{},"user 2m9.856s\n",[474,69538,69539],{"class":476,"line":584},[474,69540,69541],{},"sys 0m6.688s\n",[439,69543,69544,69545,69547],{},"Applying ",[448,69546,69510],{}," even saves some more seconds:",[464,69549,69551],{"className":16895,"code":69550,"language":16897,"meta":469,"style":469},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 1m42.451s\nuser 2m8.744s\nsys 0m6.668s\n",[471,69552,69553,69557,69562,69567],{"__ignoreMap":469},[474,69554,69555],{"class":476,"line":477},[474,69556,69445],{},[474,69558,69559],{"class":476,"line":507},[474,69560,69561],{},"real 1m42.451s\n",[474,69563,69564],{"class":476,"line":547},[474,69565,69566],{},"user 2m8.744s\n",[474,69568,69569],{"class":476,"line":584},[474,69570,69571],{},"sys 0m6.668s\n",[439,69573,69574],{},"For more details on PostgreSQLs WAL mechanism and configuration options have a look at the PostgreSQL documentation:",[994,69576,69577,69583],{},[997,69578,69579],{},[1002,69580,69581],{"href":69581,"rel":69582,"title":69581},"http://www.postgresql.org/docs/9.1/static/wal.html",[1006],[997,69584,69585],{},[1002,69586,69587],{"href":69587,"rel":69588,"title":69587},"http://www.postgresql.org/docs/9.1/static/runtime-config-wal.html",[1006],[1024,69590,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":69592},[],[1030],"2012-05-28T21:11:08","Having a newly installed Ubuntu 12.04 on my machine, I noticed that building the OpenCms project I am currently working\\non is a very time consuming process:","https://synyx.de/blog/performance-tuning-maven-opencms-builds-using-postgresql/",{},"/blog/performance-tuning-maven-opencms-builds-using-postgresql",{"title":69426,"description":69435},"blog/performance-tuning-maven-opencms-builds-using-postgresql",[69602,22988,64517,1425],"ext4","Having a newly installed Ubuntu 12.04 on my machine, I noticed that building the OpenCms project I am currently working on is a very time consuming process: oli@rikit:~/develop/projects/foo$ time mvn…","BcH1ccLWHHHTFLQHxO3htFYV_U0NUl64uvG08kiP6uw",{"id":69606,"title":69607,"author":69608,"body":69609,"category":69821,"date":69822,"description":69823,"extension":1034,"link":69824,"meta":69825,"navigation":916,"path":69826,"seo":69827,"slug":69828,"stem":69829,"tags":69830,"teaser":69833,"__hash__":69834},"blog/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct.md","Android 2.1 SQLite: problem with QueryBuilder and Distinct",[190],{"type":432,"value":69610,"toc":69819},[69611,69614,69617,69620,69795,69802,69805,69814,69817],[435,69612,69607],{"id":69613},"android-21-sqlite-problem-with-querybuilder-and-distinct",[439,69615,69616],{},"In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\nbut on 2.1 it crashed every time when trying to get a column from a cursor.",[439,69618,69619],{},"Here’s the simplified code:",[464,69621,69623],{"className":709,"code":69622,"language":711,"meta":469,"style":469},"//member, a SQLiteOpenHelper\nBackendOpenHelper helper;\n//...\npublic List \u003CExample> getExamples(String arg){\nSQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n builder.setTables(\"example e JOIN\n secondtable s ON e.id = s.example_id\");\n Map\u003CString, String> projectionMap =\n new HashMap\u003CString, String>();\n projectionMap.put(\"id\", \"e.id\");\n //... put in some more values ...\n builder.setProjectionMap(projectionMap);\n builder.setDistinct(true);\n builder.appendWhere(\" e.someRow = ? \");\n //... some more wheres ...\n SQLiteDatabase db = helper.getReadableDatabase();\n String[] selectionArgs = new String[] {\n arg\n };\n Cursor cursor = builder.query(db, null,\n null, selectionArgs, null, null, null);\n if (cursor.moveToFirst()) {\n while (cursor.isAfterLast() == false) {\n int index = cursor.getColumnIndex(\"id\");\n //on android 2.1, index is returned as -1\n //on newer versions as 1\n int id = cursor.getInt(index);\n //crashes if index is -1\n //...\n cursor.moveToNext();\n }\n }\n cursor.close();\n //...\n}\n",[471,69624,69625,69630,69635,69639,69644,69649,69654,69659,69664,69669,69674,69679,69684,69689,69694,69699,69704,69709,69714,69718,69723,69728,69733,69738,69743,69748,69753,69758,69763,69768,69773,69777,69781,69786,69791],{"__ignoreMap":469},[474,69626,69627],{"class":476,"line":477},[474,69628,69629],{},"//member, a SQLiteOpenHelper\n",[474,69631,69632],{"class":476,"line":507},[474,69633,69634],{},"BackendOpenHelper helper;\n",[474,69636,69637],{"class":476,"line":547},[474,69638,1234],{},[474,69640,69641],{"class":476,"line":584},[474,69642,69643],{},"public List \u003CExample> getExamples(String arg){\n",[474,69645,69646],{"class":476,"line":607},[474,69647,69648],{},"SQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n",[474,69650,69651],{"class":476,"line":642},[474,69652,69653],{}," builder.setTables(\"example e JOIN\n",[474,69655,69656],{"class":476,"line":663},[474,69657,69658],{}," secondtable s ON e.id = s.example_id\");\n",[474,69660,69661],{"class":476,"line":694},[474,69662,69663],{}," Map\u003CString, String> projectionMap =\n",[474,69665,69666],{"class":476,"line":700},[474,69667,69668],{}," new HashMap\u003CString, String>();\n",[474,69670,69671],{"class":476,"line":913},[474,69672,69673],{}," projectionMap.put(\"id\", \"e.id\");\n",[474,69675,69676],{"class":476,"line":920},[474,69677,69678],{}," //... put in some more values ...\n",[474,69680,69681],{"class":476,"line":926},[474,69682,69683],{}," builder.setProjectionMap(projectionMap);\n",[474,69685,69686],{"class":476,"line":932},[474,69687,69688],{}," builder.setDistinct(true);\n",[474,69690,69691],{"class":476,"line":938},[474,69692,69693],{}," builder.appendWhere(\" e.someRow = ? \");\n",[474,69695,69696],{"class":476,"line":944},[474,69697,69698],{}," //... some more wheres ...\n",[474,69700,69701],{"class":476,"line":950},[474,69702,69703],{}," SQLiteDatabase db = helper.getReadableDatabase();\n",[474,69705,69706],{"class":476,"line":956},[474,69707,69708],{}," String[] selectionArgs = new String[] {\n",[474,69710,69711],{"class":476,"line":962},[474,69712,69713],{}," arg\n",[474,69715,69716],{"class":476,"line":4876},[474,69717,46539],{},[474,69719,69720],{"class":476,"line":4888},[474,69721,69722],{}," Cursor cursor = builder.query(db, null,\n",[474,69724,69725],{"class":476,"line":4900},[474,69726,69727],{}," null, selectionArgs, null, null, null);\n",[474,69729,69730],{"class":476,"line":4913},[474,69731,69732],{}," if (cursor.moveToFirst()) {\n",[474,69734,69735],{"class":476,"line":4921},[474,69736,69737],{}," while (cursor.isAfterLast() == false) {\n",[474,69739,69740],{"class":476,"line":4932},[474,69741,69742],{}," int index = cursor.getColumnIndex(\"id\");\n",[474,69744,69745],{"class":476,"line":4938},[474,69746,69747],{}," //on android 2.1, index is returned as -1\n",[474,69749,69750],{"class":476,"line":4946},[474,69751,69752],{}," //on newer versions as 1\n",[474,69754,69755],{"class":476,"line":4952},[474,69756,69757],{}," int id = cursor.getInt(index);\n",[474,69759,69760],{"class":476,"line":4957},[474,69761,69762],{}," //crashes if index is -1\n",[474,69764,69765],{"class":476,"line":4969},[474,69766,69767],{}," //...\n",[474,69769,69770],{"class":476,"line":4990},[474,69771,69772],{}," cursor.moveToNext();\n",[474,69774,69775],{"class":476,"line":5001},[474,69776,42564],{},[474,69778,69779],{"class":476,"line":5013},[474,69780,5704],{},[474,69782,69783],{"class":476,"line":5024},[474,69784,69785],{}," cursor.close();\n",[474,69787,69788],{"class":476,"line":5035},[474,69789,69790],{}," //...\n",[474,69792,69793],{"class":476,"line":5047},[474,69794,703],{},[439,69796,69797,69798,69801],{},"After some research I found out that this apparently happens, when using ",[990,69799,69800],{},"distinct"," with the QueryBuilder on android\n2.1.",[439,69803,69804],{},"So a quick fix for this problem is to simply don’t use the getColumnIndex() method from the cursor, but instead just\naccess it by its id (Though you have to remember to change this part of the code if you make changes to your table\nrows).",[464,69806,69808],{"className":709,"code":69807,"language":711,"meta":469,"style":469}," int id = cursor.getInt(1);\n",[471,69809,69810],{"__ignoreMap":469},[474,69811,69812],{"class":476,"line":477},[474,69813,69807],{},[439,69815,69816],{},"I hope this will help someone who encounters the same problem, so he doesn’t have to search for a solution as long as I\nhad to.",[1024,69818,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":69820},[],[11122,1413],"2012-05-22T09:39:08","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\\nbut on 2.1 it crashed every time when trying to get a column from a cursor.","https://synyx.de/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct/",{},"/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",{"title":69607,"description":69616},"android-2-1-sqlite-problem-with-querybuilder-and-distinct","blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",[69831,11132,54575,18496,69832],"2-1","sqlite","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly, but on 2.1 it crashed every time when trying to…","x1Vxa8-B7BB8vb0rMrFKnHNmHw0JMSkiTZ3HMChf8mU",{"id":69836,"title":69837,"author":69838,"body":69839,"category":70523,"date":70524,"description":70525,"extension":1034,"link":70526,"meta":70527,"navigation":916,"path":70528,"seo":70529,"slug":69843,"stem":70530,"tags":70531,"teaser":70536,"__hash__":70537},"blog/blog/how-to-monitor-and-manage-your-java-application-with-jmx.md","How to monitor and manage your Java application with JMX",[30],{"type":432,"value":69840,"toc":70516},[69841,69844,69847,69862,69941,69944,69948,69951,70041,70044,70051,70055,70069,70074,70079,70106,70109,70114,70119,70147,70150,70172,70177,70182,70210,70216,70259,70268,70272,70275,70280,70283,70293,70298,70301,70312,70315,70318,70350,70353,70406,70410,70413,70416,70480,70483,70487,70493,70500,70507,70514],[435,69842,69837],{"id":69843},"how-to-monitor-and-manage-your-java-application-with-jmx",[439,69845,69846],{},"JMX (Java Management Extensions) provides the infrastructure to support monitoring and management of your Java\napplications. Resources you manage with JMX are called Managed Beans (MBeans). I want to show you how to quickly\nregister your own Service as MBean using Spring and Source-Level Metadata (JDK 5.0+ annotations).",[439,69848,69849,69850,69853,69854,69857,69858,69861],{},"The following sample is built on a tool that allows to manage the staffs’ applications for vacation digitally instead of\nusing paper. If a staff member applies for leave, the application gets the status ",[990,69851,69852],{},"waiting",". Then an authorized person (\nthe boss) has to decide about this application. It may be set to ",[990,69855,69856],{},"allowed"," or to ",[990,69859,69860],{},"rejected",". It might be that you want\nto have an overview of the applications and their status and you may even want to remind the authorized persons via\nemail to review the pending applications. Even if the vacation management tool has a web-based frontend for doing the\nmost of the actions, I think it still makes a good example for describing how to use JMX in your Java application. The\nfollowing class is a skeleton of the class which shall be exposed to JMX as MBean.",[464,69863,69865],{"className":709,"code":69864,"language":711,"meta":469,"style":469},"\npublic class JmxDemo {\n private long numberOfWaitingApplications;\n public long getNumberOfWaitingApplications() {\n return numberOfWaitingApplications;\n }\n public long countApplicationsInStatus(String status) {\n // do something and return number of applications with the given status\n }\n public List\u003CString> showWaitingApplications() {\n // do something and return a list of all waiting applications\n }\n public String remindBossAboutWaitingApplications() {\n // remind the boss via email to decide about the waiting applications\n }\n}\n\n",[471,69866,69867,69871,69876,69881,69886,69891,69895,69900,69905,69909,69914,69919,69923,69928,69933,69937],{"__ignoreMap":469},[474,69868,69869],{"class":476,"line":477},[474,69870,917],{"emptyLinePlaceholder":916},[474,69872,69873],{"class":476,"line":507},[474,69874,69875],{},"public class JmxDemo {\n",[474,69877,69878],{"class":476,"line":547},[474,69879,69880],{}," private long numberOfWaitingApplications;\n",[474,69882,69883],{"class":476,"line":584},[474,69884,69885],{}," public long getNumberOfWaitingApplications() {\n",[474,69887,69888],{"class":476,"line":607},[474,69889,69890],{}," return numberOfWaitingApplications;\n",[474,69892,69893],{"class":476,"line":642},[474,69894,1276],{},[474,69896,69897],{"class":476,"line":663},[474,69898,69899],{}," public long countApplicationsInStatus(String status) {\n",[474,69901,69902],{"class":476,"line":694},[474,69903,69904],{}," // do something and return number of applications with the given status\n",[474,69906,69907],{"class":476,"line":700},[474,69908,1276],{},[474,69910,69911],{"class":476,"line":913},[474,69912,69913],{}," public List\u003CString> showWaitingApplications() {\n",[474,69915,69916],{"class":476,"line":920},[474,69917,69918],{}," // do something and return a list of all waiting applications\n",[474,69920,69921],{"class":476,"line":926},[474,69922,1276],{},[474,69924,69925],{"class":476,"line":932},[474,69926,69927],{}," public String remindBossAboutWaitingApplications() {\n",[474,69929,69930],{"class":476,"line":938},[474,69931,69932],{}," // remind the boss via email to decide about the waiting applications\n",[474,69934,69935],{"class":476,"line":944},[474,69936,1276],{},[474,69938,69939],{"class":476,"line":950},[474,69940,703],{},[439,69942,69943],{},"If you want to use this class as a MBean, a few steps are necessary.",[1065,69945,69947],{"id":69946},"_1-not-yet-another-xml-file","1. Not yet another xml file…",[439,69949,69950],{},"It’s best you create an extra xml file (let’s call it jmxContext.xml) for JMX configuration and import it in your\napplicationContext.xml. In your jmxContext.xml you define your MBean and the MBeanExporter.",[464,69952,69954],{"className":6253,"code":69953,"language":6255,"meta":469,"style":469},"\n\u003Cbean id=\"jmxDemo\" class=\"org.synyx.urlaubsverwaltung.jmx.JmxDemo\">\n \u003C!-- maybe you need contructor-injection -->\n \u003C!-- \u003Cconstructor-arg ref=\"myService\" /> -->\n\u003C/bean>\n\u003C!-- you may just copy the following lines -->\n\u003Cbean id=\"exporter\" class=\"org.springframework.jmx.export.MBeanExporter\" lazy-init=\"false\">\n \u003Cproperty name=\"autodetect\" value=\"true\" />\n \u003Cproperty name=\"namingStrategy\" ref=\"namingStrategy\" />\n \u003Cproperty name=\"assembler\" ref=\"assembler\" />\n\u003C/bean>\n\u003Cbean id=\"jmxAttributeSource\" class=\"org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource\" />\n\u003Cbean id=\"assembler\" class=\"org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler\">\n \u003Cproperty name=\"attributeSource\" ref=\"jmxAttributeSource\" />\n\u003C/bean>\n\u003Cbean id=\"namingStrategy\" class=\"org.springframework.jmx.export.naming.MetadataNamingStrategy\">\n \u003Cproperty name=\"attributeSource\" ref=\"jmxAttributeSource\" />\n\u003C/bean>\n\n",[471,69955,69956,69960,69965,69970,69975,69980,69985,69990,69995,70000,70005,70009,70014,70019,70024,70028,70033,70037],{"__ignoreMap":469},[474,69957,69958],{"class":476,"line":477},[474,69959,917],{"emptyLinePlaceholder":916},[474,69961,69962],{"class":476,"line":507},[474,69963,69964],{},"\u003Cbean id=\"jmxDemo\" class=\"org.synyx.urlaubsverwaltung.jmx.JmxDemo\">\n",[474,69966,69967],{"class":476,"line":547},[474,69968,69969],{}," \u003C!-- maybe you need contructor-injection -->\n",[474,69971,69972],{"class":476,"line":584},[474,69973,69974],{}," \u003C!-- \u003Cconstructor-arg ref=\"myService\" /> -->\n",[474,69976,69977],{"class":476,"line":607},[474,69978,69979],{},"\u003C/bean>\n",[474,69981,69982],{"class":476,"line":642},[474,69983,69984],{},"\u003C!-- you may just copy the following lines -->\n",[474,69986,69987],{"class":476,"line":663},[474,69988,69989],{},"\u003Cbean id=\"exporter\" class=\"org.springframework.jmx.export.MBeanExporter\" lazy-init=\"false\">\n",[474,69991,69992],{"class":476,"line":694},[474,69993,69994],{}," \u003Cproperty name=\"autodetect\" value=\"true\" />\n",[474,69996,69997],{"class":476,"line":700},[474,69998,69999],{}," \u003Cproperty name=\"namingStrategy\" ref=\"namingStrategy\" />\n",[474,70001,70002],{"class":476,"line":913},[474,70003,70004],{}," \u003Cproperty name=\"assembler\" ref=\"assembler\" />\n",[474,70006,70007],{"class":476,"line":920},[474,70008,69979],{},[474,70010,70011],{"class":476,"line":926},[474,70012,70013],{},"\u003Cbean id=\"jmxAttributeSource\" class=\"org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource\" />\n",[474,70015,70016],{"class":476,"line":932},[474,70017,70018],{},"\u003Cbean id=\"assembler\" class=\"org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler\">\n",[474,70020,70021],{"class":476,"line":938},[474,70022,70023],{}," \u003Cproperty name=\"attributeSource\" ref=\"jmxAttributeSource\" />\n",[474,70025,70026],{"class":476,"line":944},[474,70027,69979],{},[474,70029,70030],{"class":476,"line":950},[474,70031,70032],{},"\u003Cbean id=\"namingStrategy\" class=\"org.springframework.jmx.export.naming.MetadataNamingStrategy\">\n",[474,70034,70035],{"class":476,"line":956},[474,70036,70023],{},[474,70038,70039],{"class":476,"line":962},[474,70040,69979],{},[439,70042,70043],{},"If your application is running inside a container such as Tomcat, you even don’t have to configure the MBeanServer\nbecause the container has its own one.",[439,70045,70046,70047,70050],{},"Setting MBeanExporter’s property ",[990,70048,70049],{},"autodetect"," to true, means that the MBeanExporter will register all the Beans within\nyour application’s context that are annotated in the way described in the next section as MBeans.",[1065,70052,70054],{"id":70053},"_2-lets-transform-your-spring-bean-to-a-managed-bean","2. Let’s transform your Spring Bean to a Managed Bean!",[439,70056,70057,70058,70061,70062,70065,70066,1402],{},"Spring uses information provided by annotations to generate MBeans. The attributes of the annotations are speaking for\nthemselves so further description isn’t necessary. To mark a Bean for export, it has to be annotated with\n",[990,70059,70060],{},"@ManagedResource",", Attributes are annotated with ",[990,70063,70064],{},"@ManagedAttribute"," and Methods with ",[990,70067,70068],{},"@ManagedOperation",[439,70070,70071],{},[448,70072,70073],{},"2.1 Bean",[439,70075,70076,70077,1402],{},"Mark your Bean with ",[990,70078,70060],{},[464,70080,70082],{"className":709,"code":70081,"language":711,"meta":469,"style":469},"\n@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"My managed Bean.\")\npublic class JmxDemo {\n // lot of stuff\n}\n\n",[471,70083,70084,70088,70093,70097,70102],{"__ignoreMap":469},[474,70085,70086],{"class":476,"line":477},[474,70087,917],{"emptyLinePlaceholder":916},[474,70089,70090],{"class":476,"line":507},[474,70091,70092],{},"@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"My managed Bean.\")\n",[474,70094,70095],{"class":476,"line":547},[474,70096,69875],{},[474,70098,70099],{"class":476,"line":584},[474,70100,70101],{}," // lot of stuff\n",[474,70103,70104],{"class":476,"line":607},[474,70105,703],{},[439,70107,70108],{},"Make sure that your MBean doesn’t contain ‘MBean’ in its name since it would be treated as a StandardMBean causing your\nannotations not to work.",[439,70110,70111],{},[448,70112,70113],{},"2.2 Attributes",[439,70115,70116,70117,1402],{},"Annotate the Getter and Setter with ",[990,70118,70064],{},[464,70120,70122],{"className":709,"code":70121,"language":711,"meta":469,"style":469},"\n@ManagedAttribute(description = \"Get the number of all waiting applications\" )\npublic long getNumberOfWaitingApplications() {\n return numberOfWaitingApplications;\n}\n\n",[471,70123,70124,70128,70133,70138,70143],{"__ignoreMap":469},[474,70125,70126],{"class":476,"line":477},[474,70127,917],{"emptyLinePlaceholder":916},[474,70129,70130],{"class":476,"line":507},[474,70131,70132],{},"@ManagedAttribute(description = \"Get the number of all waiting applications\" )\n",[474,70134,70135],{"class":476,"line":547},[474,70136,70137],{},"public long getNumberOfWaitingApplications() {\n",[474,70139,70140],{"class":476,"line":584},[474,70141,70142],{}," return numberOfWaitingApplications;\n",[474,70144,70145],{"class":476,"line":607},[474,70146,703],{},[439,70148,70149],{},"Exposing attributes may be:",[994,70151,70152,70155,70158,70161,70164,70167,70169],{},[997,70153,70154],{},"Basic types",[997,70156,70157],{},"Primitives and their wrappers",[997,70159,70160],{},"String",[997,70162,70163],{},"BigDecimal",[997,70165,70166],{},"BigInteger",[997,70168,64072],{},[997,70170,70171],{},"Arrays and collections of basic types",[439,70173,70174],{},[448,70175,70176],{},"2.2 Methods",[439,70178,70179,70180,1402],{},"Annotate each method you wish to expose with ",[990,70181,70068],{},[464,70183,70185],{"className":709,"code":70184,"language":711,"meta":469,"style":469},"\n@ManagedOperation(description = \"Shows a list of all waiting applications with some information.\")\npublic List\u003CString> showWaitingApplications() {\n // do something and return a list of all waiting applications\n}\n\n",[471,70186,70187,70191,70196,70201,70206],{"__ignoreMap":469},[474,70188,70189],{"class":476,"line":477},[474,70190,917],{"emptyLinePlaceholder":916},[474,70192,70193],{"class":476,"line":507},[474,70194,70195],{},"@ManagedOperation(description = \"Shows a list of all waiting applications with some information.\")\n",[474,70197,70198],{"class":476,"line":547},[474,70199,70200],{},"public List\u003CString> showWaitingApplications() {\n",[474,70202,70203],{"class":476,"line":584},[474,70204,70205],{}," // do something and return a list of all waiting applications\n",[474,70207,70208],{"class":476,"line":607},[474,70209,703],{},[439,70211,70212,70213,1402],{},"If your methods have parameters you can describe them further with ",[990,70214,70215],{},"@ManagedOperationParameters",[464,70217,70219],{"className":709,"code":70218,"language":711,"meta":469,"style":469},"\n@ManagedOperation(description = \"Get the number of all applications that have the given status.\")\n@ManagedOperationParameters({\n @ManagedOperationParameter(name = \"status\", description = \"The status may be waiting, allowed, rejected or cancelled.\")\n})\npublic long countApplicationsInStatus(String state) {\n // do something and return number of applications with the given status\n}\n\n",[471,70220,70221,70225,70230,70235,70240,70245,70250,70255],{"__ignoreMap":469},[474,70222,70223],{"class":476,"line":477},[474,70224,917],{"emptyLinePlaceholder":916},[474,70226,70227],{"class":476,"line":507},[474,70228,70229],{},"@ManagedOperation(description = \"Get the number of all applications that have the given status.\")\n",[474,70231,70232],{"class":476,"line":547},[474,70233,70234],{},"@ManagedOperationParameters({\n",[474,70236,70237],{"class":476,"line":584},[474,70238,70239],{}," @ManagedOperationParameter(name = \"status\", description = \"The status may be waiting, allowed, rejected or cancelled.\")\n",[474,70241,70242],{"class":476,"line":607},[474,70243,70244],{},"})\n",[474,70246,70247],{"class":476,"line":642},[474,70248,70249],{},"public long countApplicationsInStatus(String state) {\n",[474,70251,70252],{"class":476,"line":663},[474,70253,70254],{}," // do something and return number of applications with the given status\n",[474,70256,70257],{"class":476,"line":694},[474,70258,703],{},[439,70260,70261,70262,70264,70265,70267],{},"Make sure to annotate your Getter/Setter with ",[990,70263,70064],{}," and not with ",[990,70266,70068],{},". Otherwise your\nmethods won’t work.",[1065,70269,70271],{"id":70270},"_3-try-it","3. Try it!",[439,70273,70274],{},"You can now use the functions of your MBean either with JConsole or with other tools. (e.g. JMinix)",[439,70276,70277],{},[448,70278,70279],{},"3.1 JConsole",[439,70281,70282],{},"JConsole is part of Oracle’s JDK, so you can just start it by executing the JConsole command in your JDK’s\nbinary-folder. You can connect to local or to remote Java Virtual Machines. If you are running your application on the\nsame host as JConsole it should show up at the ‘Local Process’ section.",[439,70284,70285],{},[1002,70286,70289],{"href":70287,"rel":70288},"https://media.synyx.de/uploads//2012/04/jconsole.png",[1006],[2205,70290],{"alt":469,"src":70291,"title":70292},"https://media.synyx.de/uploads//2012/04/jconsole-300x251.png","jconsole",[439,70294,70295],{},[448,70296,70297],{},"3.2 JMinix",[439,70299,70300],{},"If you want to have a JMX entry point in your web application instead of using JConsole, JMinix might be the right\nchoice for you.",[439,70302,70303],{},[1002,70304,70307],{"href":70305,"rel":70306},"https://media.synyx.de/uploads//2012/04/jminix.png",[1006],[2205,70308],{"alt":70309,"src":70310,"title":70311},"JConsole","https://media.synyx.de/uploads//2012/04/jminix-300x187.png","JMinix",[439,70313,70314],{},"You can include it easily in your Maven based web application:",[439,70316,70317],{},"Add JMinix as dependency in your pom.xml",[464,70319,70321],{"className":6253,"code":70320,"language":6255,"meta":469,"style":469},"\n\u003Cdependency>\n \u003CgroupId>org.jminix\u003C/groupId>\n \u003CartifactId>jminix\u003C/artifactId>\n \u003Cversion>1.0.0\u003C/version>\n\u003C/dependency>\n\n",[471,70322,70323,70327,70331,70336,70341,70346],{"__ignoreMap":469},[474,70324,70325],{"class":476,"line":477},[474,70326,917],{"emptyLinePlaceholder":916},[474,70328,70329],{"class":476,"line":507},[474,70330,65654],{},[474,70332,70333],{"class":476,"line":547},[474,70334,70335],{}," \u003CgroupId>org.jminix\u003C/groupId>\n",[474,70337,70338],{"class":476,"line":584},[474,70339,70340],{}," \u003CartifactId>jminix\u003C/artifactId>\n",[474,70342,70343],{"class":476,"line":607},[474,70344,70345],{}," \u003Cversion>1.0.0\u003C/version>\n",[474,70347,70348],{"class":476,"line":642},[474,70349,65674],{},[439,70351,70352],{},"JMinix uses a simple HttpServlet that you have to register and map to an url-pattern in your web.xml",[464,70354,70356],{"className":6253,"code":70355,"language":6255,"meta":469,"style":469},"\n\u003C!-- JMX -->\n\u003Cservlet>\n \u003Cservlet-name>JmxMiniConsoleServlet\u003C/servlet-name>\n \u003Cservlet-class>org.jminix.console.servlet.MiniConsoleServlet\u003C/servlet-class>\n\u003C/servlet>\n\u003Cservlet-mapping>\n \u003Cservlet-name>JmxMiniConsoleServlet\u003C/servlet-name>\n \u003Curl-pattern>/jmx/*\u003C/url-pattern>\n\u003C/servlet-mapping>\n\n",[471,70357,70358,70362,70367,70372,70377,70382,70387,70392,70396,70401],{"__ignoreMap":469},[474,70359,70360],{"class":476,"line":477},[474,70361,917],{"emptyLinePlaceholder":916},[474,70363,70364],{"class":476,"line":507},[474,70365,70366],{},"\u003C!-- JMX -->\n",[474,70368,70369],{"class":476,"line":547},[474,70370,70371],{},"\u003Cservlet>\n",[474,70373,70374],{"class":476,"line":584},[474,70375,70376],{}," \u003Cservlet-name>JmxMiniConsoleServlet\u003C/servlet-name>\n",[474,70378,70379],{"class":476,"line":607},[474,70380,70381],{}," \u003Cservlet-class>org.jminix.console.servlet.MiniConsoleServlet\u003C/servlet-class>\n",[474,70383,70384],{"class":476,"line":642},[474,70385,70386],{},"\u003C/servlet>\n",[474,70388,70389],{"class":476,"line":663},[474,70390,70391],{},"\u003Cservlet-mapping>\n",[474,70393,70394],{"class":476,"line":694},[474,70395,70376],{},[474,70397,70398],{"class":476,"line":700},[474,70399,70400],{}," \u003Curl-pattern>/jmx/*\u003C/url-pattern>\n",[474,70402,70403],{"class":476,"line":913},[474,70404,70405],{},"\u003C/servlet-mapping>\n",[1065,70407,70409],{"id":70408},"_4-notifications","4. Notifications",[439,70411,70412],{},"Notifications (javax.management.Notification) can be broadcast from your component to notify about something interesting\nhappening. This is only a simple example of using Notifications.",[439,70414,70415],{},"Example: You want to be notified if a user logs in.",[464,70417,70419],{"className":709,"code":70418,"language":711,"meta":469,"style":469},"\n@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"Manage some 'Urlaubsverwaltung' problems.\")\npublic class JmxDemoReady implements NotificationPublisherAware {\n // lot of stuff\n private NotificationPublisher notificationPublisher;\n public void notifyAboutLogin(String msg) {\n notificationPublisher.sendNotification(new Notification(\"Login Action\", this, 0, msg));\n }\n @Override\n public void setNotificationPublisher(NotificationPublisher notificationPublisher) {\n this.notificationPublisher = notificationPublisher;\n }\n}\n\n",[471,70420,70421,70425,70430,70435,70439,70444,70449,70454,70458,70462,70467,70472,70476],{"__ignoreMap":469},[474,70422,70423],{"class":476,"line":477},[474,70424,917],{"emptyLinePlaceholder":916},[474,70426,70427],{"class":476,"line":507},[474,70428,70429],{},"@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"Manage some 'Urlaubsverwaltung' problems.\")\n",[474,70431,70432],{"class":476,"line":547},[474,70433,70434],{},"public class JmxDemoReady implements NotificationPublisherAware {\n",[474,70436,70437],{"class":476,"line":584},[474,70438,70101],{},[474,70440,70441],{"class":476,"line":607},[474,70442,70443],{}," private NotificationPublisher notificationPublisher;\n",[474,70445,70446],{"class":476,"line":642},[474,70447,70448],{}," public void notifyAboutLogin(String msg) {\n",[474,70450,70451],{"class":476,"line":663},[474,70452,70453],{}," notificationPublisher.sendNotification(new Notification(\"Login Action\", this, 0, msg));\n",[474,70455,70456],{"class":476,"line":694},[474,70457,1276],{},[474,70459,70460],{"class":476,"line":700},[474,70461,21043],{},[474,70463,70464],{"class":476,"line":913},[474,70465,70466],{}," public void setNotificationPublisher(NotificationPublisher notificationPublisher) {\n",[474,70468,70469],{"class":476,"line":920},[474,70470,70471],{}," this.notificationPublisher = notificationPublisher;\n",[474,70473,70474],{"class":476,"line":926},[474,70475,1276],{},[474,70477,70478],{"class":476,"line":932},[474,70479,703],{},[439,70481,70482],{},"With the NotificationPublisher you are able to create Notifications in a very simple way. At the right place in your\ncode, you inject your JmxDemo Bean and call the method notifyAboutLogin() when a user logs in. JConsole now displays a\nthird menu item called ‘Notifications’, besides ‘Attributes’ and ‘Operations’. If you click on ‘Subscribe’, you get a\nNotification every time a user logs in your web application.",[1065,70484,70486],{"id":70485},"_5-further-information","5. Further information:",[439,70488,70489],{},[1002,70490,69400],{"href":70491,"rel":70492},"http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/jmx.html",[1006],[439,70494,70495],{},[1002,70496,70499],{"href":70497,"rel":70498},"http://docs.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html",[1006],"About JConsole",[439,70501,70502],{},[1002,70503,70506],{"href":70504,"rel":70505},"http://code.google.com/p/jminix/",[1006],"About JMinix",[439,70508,70509],{},[1002,70510,70513],{"href":70511,"rel":70512},"http://blog.synyx.de/2011/11/elektronische-urlaubsverwaltung-made-by-youngsters",[1006],"About the vacation management web application",[1024,70515,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":70517},[70518,70519,70520,70521,70522],{"id":69946,"depth":547,"text":69947},{"id":70053,"depth":547,"text":70054},{"id":70270,"depth":547,"text":70271},{"id":70408,"depth":547,"text":70409},{"id":70485,"depth":547,"text":70486},[13208,1030],"2012-05-07T17:56:12","JMX (Java Management Extensions) provides the infrastructure to support monitoring and management of your Java\\napplications. Resources you manage with JMX are called Managed Beans (MBeans). I want to show you how to quickly\\nregister your own Service as MBean using Spring and Source-Level Metadata (JDK 5.0+ annotations).","https://synyx.de/blog/how-to-monitor-and-manage-your-java-application-with-jmx/",{},"/blog/how-to-monitor-and-manage-your-java-application-with-jmx",{"title":69837,"description":69846},"blog/how-to-monitor-and-manage-your-java-application-with-jmx",[70532,70292,70533,59865,70534,70535,21204,1426],"annotation","jminix","mbeans","metadata","JMX (Java Management Extensions) provides the infrastructure to support monitoring and management of your Java applications. Resources you manage with JMX are called Managed Beans (MBeans). I want to show…","QgQ9fiVGx1LjWxJa2qqvi3OPyGYhZwpjzQPZez0x9A0",{"id":70539,"title":70540,"author":70541,"body":70542,"category":70573,"date":70574,"description":70575,"extension":1034,"link":70576,"meta":70577,"navigation":916,"path":70578,"seo":70579,"slug":70580,"stem":70581,"tags":70582,"teaser":70583,"__hash__":70584},"blog/blog/schauspieltechniken-fur-beruf-und-alltag-braucht-man-das.md","Schauspieltechniken für Beruf und Alltag – Braucht man das?",[262],{"type":432,"value":70543,"toc":70571},[70544,70547,70550,70553,70556,70559,70562,70565,70568],[435,70545,70540],{"id":70546},"schauspieltechniken-für-beruf-und-alltag-braucht-man-das",[439,70548,70549],{},"Zunächst war ich etwas skeptisch, als ich die Anmeldebestätigung vom CyberForum für den Workshop “Schauspieltechniken\nfür Beruf und Alltag” erhielt, für den Katja uns einfach angemeldet hat. Wozu braucht man das überhaupt? Man will doch\nseinem Umfeld nichts vorspielen? Ehrlichkeit ist doch das A und O in zwischenmenschlichen Beziehungen! Es ging hier\njedoch nicht darum, anderen etwas “vorzuspielen”, das sollte ich im Verlauf des Workshops noch feststellen.",[439,70551,70552],{},"Somit ließ ich mich einfach überraschen und ging eher ohne große Erwartungen am vergangenen Donnerstag mit Katja\ndorthin. Der Workshop fand im Staatstheater in Karlsruhe, in einem der Probenräume, statt. Frau Daniela Vöge, die\nLeiterin des Workshops, ist Diplom-Politologin, Schauspielerin, Kulturmanagerin und Theaterpädagogin (BuT). Sie war\nfünf Jahre Leitende Theaterpädagogin am Staatstheater in Karlsruhe und ist Lehrbeauftragte am KIT.",[439,70554,70555],{},"Zunächst stellte sich jeder Teilnehmer kurz vor und tat seine Erwartungen kund. Nach der kurzen Vorstellung bat uns Frau\nVöge dann alle einfach mal kreuz und quer durch den Raum zu laufen. Erst normal, dann zügig, danch sollten wir uns immer\nein Ziel suchen, auf das wir zuliefen. Derweil wir so durch den Raum liefen, ohne eine wirkliche Ahnung wozu diese Übung\ngut sein sollte, erklärte uns Frau Vöge den nächsten Schritt. Wir sollten also weiterlaufen und auf ihr Zeichen warten.\nSobald sie “Stopp” gesagt hat, sollten wir stehenbleiben, die Augen schließen und auf die Person zeigen, die sie uns an\nHand eines Kleidungstückes beschrieben hat. Diese Übung war gar nicht so einfach, da man sich auf sein “Ziel”\nkonzentriert hat und dabei nicht unbedingt auf die anderen Teilnehmer geachtet hat, wo diese stehen oder was sie\nanhaben. Mit der Zeit klappte die Übung allerdings auch etwas besser.",[439,70557,70558],{},"Für die nächste Übung wurden wir in zwei Gruppen aufgeteil. Auch hier sollten wir wieder durch den Raum laufen. Diesmal\nbekam jedoch immer eine Gruppe die Anweisung stehen zubleiben, der Rest sollte weiterlaufen. Nachdem das wirklich\neinfach war, sollten die Gruppen selbständig stehenbleiben oder weiterlaufen. Hier musste also jeder auf seine Gruppe\nachten. Diese Übung war zwar wesentlich einfacher, man spürte aber auch deutlich, dass man mit der Zeit anfing\nwesentlich mehr wahrzunehmen als nur sein “Ziel”.",[439,70560,70561],{},"Die nächste Übung wurde dann richtig spannend. Wir sollten uns vorstellen, dass sich das sich die nächste Situation an\neiner Bushaltestelle abspielt.Vier der Teilnehmer waren die Darsteller und der Rest Beobachter. Jeder der Darsteller\nsollte nun eine Person mit hohem oder niedrigen Status spielen (auf vier Stufen aufgeteilt). Nun sollten die Beobachter\nan Hand der Körpersprache sehen, welchen Status welcher Darsteller hat. Es war wirklich interessant, denn auch ohne das\nein Wort gesprochen wurde, lagen wir mit unserer Bewertung richtig. In der nächsten Stufe wurde das nocheinmal etwas\nschwieriger, denn hier gab es dann fünf Stufen, somit eine “neutrale” Person. Aber auch hier konnte man das an der\nKörpersprache gut erkennen.",[439,70563,70564],{},"Als nächstes gab es dann noch ein paar kurze Rollenspiele, die wir in Zweier-Gruppen vorführen durften. Auch hier stand\ndie Körpersprache im Vordergrund. Besonders lustig wurde es, als sich jeder ein Fantasiewort zusammenstellen sollte,\nmeins war “fassdeuschakratet”, und damit dann eine Szene spielen sollte. Das Wort diente dazu, um zu zeigen, dass es\nnicht unbedingt darauf ankommt WAS man sagt, sondern WIE und was man vorab schon durch die Körpersprache zum Ausdruck\nbringt.",[439,70566,70567],{},"Fazit: Mich hat der Workshop wirklich überzeugt. Ich bin mit dem Gefühl dort rausgegangen, etwas Sicherheit zu bekommen,\nwenn ich anderen gegenüberstehe und etwas sage. Auch ist mir bewusst geworden, wie viel wir doch durch unsere\nKörpersprache schon preisgeben und es dadurch unserem Gegenüber manchmal leichter oder aber auch schwerer machen uns zu\nmanipulieren. Denn merkt mein Gegenüber, dass ich unsicher bin weil ich das durch meine Körperhaltung zum Ausdruck\nbringe, wird es für mich umso schwieriger mein Vorhaben rüberzubringen und davon zu überzeugen.",[439,70569,70570],{},"Ich freue mich jetzt schon auf den Fortsetzungskurs im Juli und bin gespannt, was ich dort dann für mich selbst\nmitnehmen kann.",{"title":469,"searchDepth":507,"depth":507,"links":70572},[],[1031],"2012-05-04T15:04:26","Zunächst war ich etwas skeptisch, als ich die Anmeldebestätigung vom CyberForum für den Workshop “Schauspieltechniken\\nfür Beruf und Alltag” erhielt, für den Katja uns einfach angemeldet hat. Wozu braucht man das überhaupt? Man will doch\\nseinem Umfeld nichts vorspielen? Ehrlichkeit ist doch das A und O in zwischenmenschlichen Beziehungen! Es ging hier\\njedoch nicht darum, anderen etwas “vorzuspielen”, das sollte ich im Verlauf des Workshops noch feststellen.","https://synyx.de/blog/schauspieltechniken-fur-beruf-und-alltag-braucht-man-das/",{},"/blog/schauspieltechniken-fur-beruf-und-alltag-braucht-man-das",{"title":70540,"description":70549},"schauspieltechniken-fur-beruf-und-alltag-braucht-man-das","blog/schauspieltechniken-fur-beruf-und-alltag-braucht-man-das",[],"Zunächst war ich etwas skeptisch, als ich die Anmeldebestätigung vom CyberForum für den Workshop “Schauspieltechniken für Beruf und Alltag” erhielt, für den Katja uns einfach angemeldet hat. Wozu braucht man…","3ymSzNWbApVf9wzLF-MB_7V2Kswo-omkjDp6qIZqCSA",{"id":70586,"title":70587,"author":70588,"body":70589,"category":70690,"date":70691,"description":70692,"extension":1034,"link":70693,"meta":70694,"navigation":916,"path":70695,"seo":70696,"slug":70593,"stem":70697,"tags":70698,"teaser":70699,"__hash__":70700},"blog/blog/my-take-on-things-java-community-events-vs-java-conferences.md","My take on things – Java Community events vs. Java Conferences",[15],{"type":432,"value":70590,"toc":70688},[70591,70594,70597,70602,70605,70610,70615,70622,70625,70630,70635,70638,70641,70645,70650,70653,70656,70661,70664,70667,70672,70675,70678,70681,70685],[435,70592,70587],{"id":70593},"my-take-on-things-java-community-events-vs-java-conferences",[439,70595,70596],{},"Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\nich auf der JAX’2012 leider nur 2 Tage sein konnte.",[439,70598,70599],{},[448,70600,70601],{},"Organisation:",[439,70603,70604],{},"Hier können beide Konferenzen eindeutig punkten. Die JAX war an den beiden Tagen meines Besuchs hervorragend\norganisiert, es gab kaum Wartezeiten oder aber überfüllte Säle, selbst bei den recht beliebten Keynotes nicht. Beide\nVeranstaltungen profitieren natürlich von den hervorragenden Locations. Dies macht die Organisation deutlich einfacher,\nwas man wirklich an vielen, vielen Kleinigkeiten bemerkt. Die Devoxx liegt hier aber aufgrund der Tatsache das sie in\neinem der größten Kinos in Europa stattfindet noch einen Punkt besser im Rennen. Die Bequemlichkeit der Säle ist einfach\nungeschlagen (Kinosessel der modernsten Art!) und natürlich auch die Sicht. Während man im einen oder anderen Talk auf\nder JAX auf einmal eine Säule oder den Beamer im Weg hatte (ist eben nicht besser machbar) gibt es aufgrund der\nperfekten Location auf der Devoxx solche Probleme nicht.",[439,70606,70607],{},[990,70608,70609],{},"+1 für die Devoxx",[439,70611,70612],{},[448,70613,70614],{},"Verpflegung:",[439,70616,70617,70618,70621],{},"Die Verpflegung ist ja immer so eine Sache auf Konferenzen, die einen brauchen nur Kaffee, die anderen dauernd\nNervenfutter und Süßes für ihre ",[990,70619,70620],{},"Konzentration."," Die Devoxx bietet mit dauernden Kaffeetankstellen als auch dauernd\ngefüllten Kühlschränken recht ordentliche Ausstattung. Auch für Frühstück, Mittagessen und einen Nachmittagssnack ist\ngesorgt. An für sich gibt es hier keine Klagen. Ach doch, bitte, bitte liebe Devoxxianer … sorgt für besseren Kaffee!!!!\nIch bezahle gerne ein wenig mehr 🙂",[439,70623,70624],{},"Das Angebot der Devoxx ist allerdings in keinster Weise mit dem Angebot der JAX zu vergleichen. Auf der JAX gab es\nwährend der 2 Tage alles, was das Herz begehrt. Guten Kaffee, hervorragende Zwischensnacks und ein Buffet zu quasi jeder\nTageszeit, natürlich unterschiedlichst befüllt. Hierfür muss ich großen Respekt zollen. Das war einfach unfassbar gut\nund lecker! Einzig eine Kleinigkeit der Verbesserung fällt mir noch ein. Man könnte Beschildern, was wirklich\nvegetarisch oder aber vegan ist. Das Personal am Buffet konnte aber nahezu jede Frage beantworten und so hatte man auch\nals Vegetarier das gute Gefühl ordentlich zu essen 🙂",[439,70626,70627],{},[990,70628,70629],{},"+1 für die JAX",[439,70631,70632],{},[448,70633,70634],{},"Technische Ausstattung:",[439,70636,70637],{},"Ja die technische Ausstattung ist natürlich so eine Sache. Die Devoxx ist hier klar im Vorteil durch die wirklich\ngeniale Location. Gegen solch einen Sound und eine Sicht und eine Lichtstärke der Beamer und Projektoren ist natürlich\nkein Gras gewachsen.",[439,70639,70640],{},"Vergleichbar sind also letztlich nur die kleineren Dinge wie Mikrofone oder aber die Gadgets. Auf beiden Seiten\narbeiteten aufopferungssvoll die Techniker an einer perfekten Situation. Ein kleiner Pluspunkt für die Devoxx vielleicht\naufgrund der sehr gut angenommenen Twitterwall und den einfach unschlagbar guten Apps für die Mobile Devices. Auf der\nDevoxx ist das WLan auch eindeutig besser, da man in einigen Sälen der JAX schlicht keines hatte (zumindest ging es mir\nund meinen Kollegen so für die Räume im Keller).",[439,70642,70643],{},[990,70644,70609],{},[439,70646,70647],{},[448,70648,70649],{},"Anspruch Sessions, Workshops:",[439,70651,70652],{},"Hier erspare ich mir einen Vergleich, da es einfach kaum zu vergleichen ist. Die Devoxx wird seit 2 Jahren von den\ngroßen Firmen hinter Java als Podest genutzt und ist einfach auch besser international erreichbar, daher sind die\nSpeaker rein namentlich schon kaum zu schlagen (für den der Wert darauf legt). Oracle als auch Google oder Springsource\nhaben regelmäßig ihre TopSpeaker an Bord. Auf der JAX wirken die Talks auch deutlich lösungsorientierter, während es auf\nder Devoxx sehr sehr viel um die reine Entwicklung geht. Hier spürt man meiner Meinung nach einfach auch die Geschichte\nbeider Konferenzen noch sehr. Die Devoxx welche aus der Community entstand, während die JAX ein kommerzielles Event ist.",[439,70654,70655],{},"Daher erspare ich mir auch den Vergleich der Kosten, das macht reichlich wenig Sinn und jeder sollte für sich\nentscheiden, was hier ein gutes Maß ist und was nicht. Eine Kleinigkeit aber, die die Devoxxianer bereits seit einigen\nJahren machen, sollte sich hier die JAX doch noch abschauen: Den Talks ein Level mitgeben, für wen sie geeignet sind (\nbsp. Beginner, Senior, Expert oder vgl..) Ich saß dann doch ein zwei Mal in einem Talk, wo einfach das eigene Wissen\ngrößer war als das des Speakers.",[439,70657,70658],{},[448,70659,70660],{},"Speaker:",[439,70662,70663],{},"Die Speaker sind natürlich eine sehr schwer vergleichbare Sache, alleine schon durch die oben bereits erwähnte Tatsache,\ndass die Devoxx von den großen drei als Bühne genutzt wird, während die JAX hier nicht so populär ist. Allerdings kann\nauch die JAX mit sehr namhaften und erfahrenen Speakern punkten und holt hier aus meiner Sicht Jahr für Jahr auf.",[439,70665,70666],{},"Ich mag hier keinen Punkt für die eine oder andere Seite geben.",[439,70668,70669],{},[448,70670,70671],{},"Umgebung:",[439,70673,70674],{},"Die JAX findet in einer echten Konferenzlocation statt, während die Devoxx, wie bereits erwähnt in einem der größten\nKinokomplexe Europas stattfindet. Das macht sich natürlich auch in der Umgebung bemerkbar, könnte man meinen. Aber: Das\nKino ist wirklich derart am Ende von Antwerpen, dass dies viel eher ein Nachteil ist, denn ein Vorteil. Die\nRheingoldhalle in der die JAX nun seit 2011 stattfindet ist deutlich direkter in der Stadt selbst, was für das Programm\ndanach sehr von Vorteil ist.",[439,70676,70677],{},"Für mich als Besucher aus Süddeutschland ist die JAX natürlich eine Top-Location, da ich sogar pendeln kann, wenn ich\nmag. Die Devoxx schlägt hier direkt noch mit einer 5 Stunden Anfahrt zu, ist allerdings auch per Flieger direkt zu\nerreichen, während man für Mainz wohl am besten über FFM fliegt und dann noch ein wenig die DB genießt. Allerdings kann\nder Vorteil des direkten Flughafens in der Stadt wohl doch bei internationalen Speakern punkten.",[439,70679,70680],{},"Dennoch gebe ich aufgrund meiner persönlichen Situation hier den Punkt an die JAX.",[439,70682,70683],{},[990,70684,70629],{},[439,70686,70687],{},"Zusammenfassend kann man wohl sagen, dass beide Konferenzen ihre Fans haben und sich auch nicht so ähnlich sind, dass\nman nicht beide besuchen könnte. Ich persönlich werde wohl auch dieses Jahr wieder die Devoxx besuchen und dann im\nFrühjahr 2013 wieder die JAX… 😉",{"title":469,"searchDepth":507,"depth":507,"links":70689},[],[1031],"2012-04-25T09:44:39","Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\\nich auf der JAX’2012 leider nur 2 Tage sein konnte.","https://synyx.de/blog/my-take-on-things-java-community-events-vs-java-conferences/",{},"/blog/my-take-on-things-java-community-events-vs-java-conferences",{"title":70587,"description":70596},"blog/my-take-on-things-java-community-events-vs-java-conferences",[7288,8276,37618,711,9556,14723],"Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb Europas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte,…","LlCT-W2p4EzOxwXO4tBpBQx5VG80g60BW-CYXAspPV0",{"id":70702,"title":70703,"author":70704,"body":70705,"category":70794,"date":70795,"description":70796,"extension":1034,"link":70797,"meta":70798,"navigation":916,"path":70799,"seo":70800,"slug":70709,"stem":70802,"tags":70803,"teaser":70805,"__hash__":70806},"blog/blog/meine-erste-konferenz-jax-2012.md","Meine erste Konferenz: JAX 2012",[83],{"type":432,"value":70706,"toc":70789},[70707,70710,70719,70725,70734,70737,70743,70778,70782],[435,70708,70703],{"id":70709},"meine-erste-konferenz-jax-2012",[439,70711,13082,70712,70718],{},[1002,70713,70717],{"href":70714,"rel":70715,"title":70716},"http://jax.de/",[1006],"JAX2012","JAX 2012"," war durch ihren (für uns) günstig gelegenen Veranstaltungsort in Mainz mit\neiner Anfahrtszeit von unter zwei Stunden ein leicht zu erreichendes Ziel und daher entschloss auch ich mich an der\nKonferenz teilzunehmen. Eine Unterkunft hatte ich mir auch schnell besorgt, da ein alter Freund von mir in Mainz wohnt.\nIch hatte mich für den JavaEE Workshop von Adam Bien am Montag entschieden und wollte dann noch den ersten Tag der\nHauptkonferenz am Dienstag besuchen. Da dies meine erste Konferenz war, war ich sehr gespannt, was mich erwarten würde.",[3938,70720,70722],{"id":70721},"montag",[448,70723,70724],{},"Montag",[439,70726,70727,70733],{},[1002,70728,70732],{"href":70729,"rel":70730,"title":70731},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=21327",[1006],"\"Java EE: einfacher ist unmöglich\"","“Java EE: einfacher ist unmöglich”","\nmit Adam Bien",[439,70735,70736],{},"Obwohl wir bei synyx nicht mit JavaEE arbeiten, sondern in erster Line auf Spring aufbauen, erwartete ich einige\nwertvolle Erkenntnisse aus dem Workshop mitnehmen zu können. Zum einen wurde mir Adam Bien als ein sehr pragmatischer\nund praxisnaher Redner empfohlen und zum anderen sollte man als Softwareentwickler neben den Frameworks, die man selbst\nverwendet, auch immer die Alternativen im Auge behalten. Von Adam Bien als Leiter des Workshops war ich schnell\nüberzeugt. Man merkte sofort, dass er ein hohes Maß an technischem Know-how und Projekterfahrung mitbringt und er\nversteht es durch einen lockeren und offenen Vortragsstil sein Wissen an seine Zuhörer weiterzugeben. Nicht zuletzt sind\nes auch seine pragmatischen und teilweise recht provokanten Ansichten, welche seine Vorträge interessant machen. So\nbekam ich an diesem Tag viele interessante Denkanstöße und musste auch einige meiner Ansichten überdenken. Ein Beispiel\nwar hier etwa die Nutzung von externen Libraries immer konsequent nach ihrer Notwendigkeit und dem tatsächlichen Nutzen\nfür das Projekt zu hinterfragen und im Zweifelsfall immer auf zusätzliche Abhängigkeiten zu verzichten. Die konkreten\nJavaEE Themen brachten mir zwar weniger relevante Erkenntnisse als ich anfangs gehofft hatte, aber ich konnte trotzdem\neinige Ideen für konkret anstehende Probleme in unseren Projekten übernehmen. Ein Problem während des Workshops war das\nhohe Vortragstempo. Zwar konnte ich ihm stets folgen und meist alles nachvollziehen, allerdings dauerte es immer eine\nWeile bis ich das Gesagte so weit nachvollziehen konnte, dass ich mir Konsequenzen und entsprechende Fragen dazu\nüberlegen konnte. Da wir bis dahin meist schon längst bei einem ganz anderen Thema angelangt waren, wurden relativ wenig\nFragen gestellt, obwohl Adam Bien ständig für solche offen war. Praktisch war hier allerdings, dass er den Code, den er\nwährend des Workshops mit uns entwickelte, sofort Online stellte und auch in den Pausen für Fragen zur Verfügung stand.",[3938,70738,70740],{"id":70739},"dienstag",[448,70741,70742],{},"Dienstag",[439,70744,70745,70746,70751,70752,70757,70758,70763,70764,70770,70771,70777],{},"Der Dienstag startete mit einer offiziellen Begrüßung der Teilnehmer zur Hauptkonferenz und der\nanschließenden ",[1002,70747,70750],{"href":70748,"rel":70749,"title":70750},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=21733",[1006],"Keynote von Dennis Leung",".\nNach dem aus meiner Sicht etwas trägen Vortrag freute ich mich auf einen weiteren Vortrag von Adam Bien zum\nThema ",[1002,70753,70756],{"href":70754,"rel":70755,"title":70756},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=21328",[1006],"Stresstests",".\nDieser gefiel mir auch inhaltlich sehr. Zum einen beleuchtete Bien den Umgang mit Unittests und prangerte an, dass in\nvielen Unternehmen nur die reinen Prozentzahlen für Codeabdeckung betrachtet würden und nicht darauf geachtet würde, die\ntatsächlich kritischen Stellen im Code zu testen. Zum anderen ging er auf die Vorteile von regelmäßigen Stresstests ein\nund warum diese schon beim Start eines Projekts viele wichtige Erkenntnisse bringen können. Während die naive\nBetrachtung von Codeabeckung für synyx kein Thema sein dürfte, hoffe ich meine gewonnen Erkenntnisse zu Stresstests in\nmeine aktuelle Projektarbeit einbringen zu können. In den Pausen hatte ich die Gelegenheit mir die unterschiedlichen\nStände der anwesenden Unternehmen anzuschauen und mehr als einmal wurde ich mit Einstellungen und Philosophien\nkonfrontiert, mit denen ich mich gar nicht anfreunden konnte. Die wichtigste Erkenntnis für mich war hier: Ich bin froh,\ndass ich bei synyx arbeite! Ein Highlight an diesem Tag war dann\nder ",[1002,70759,70762],{"href":70760,"rel":70761,"title":70762},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=21679",[1006],"Vortrag über Big Data","\nvon Chris Blessington im Rahmen der Key Note nach dem Mittagessen (welches übrigens wie die gesamte Verpflegung während\nder beiden Tage sehr gelungen war). Obwohl hier wenig technisches Wissen vermittelt wurde, war der Vortrag für mich eine\nsehr gute Einführung in eine Thematik, mit der ich mich bisher noch kaum befasst habe. Außerdem waren die vorgestellten\nMöglichkeiten und Visionen mit Big Data sehr beeindruckend. Anschließend besuchte ich den Vortrag von Michael Plöd und\nOlaf Siefart zum\nThema ",[1002,70765,70769],{"href":70766,"rel":70767,"title":70768},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=21067",[1006],"\"Best Practices für große Wicket-Anwendungen\"","“Best Practices für große Wicket-Anwendungen”",",\nwelcher der Hauptgrund für meine Teilnahme an diesem Konferenztag war. Wir setzen Wicket in unserem aktuellen\nCode-Clinic Projekt ein und daher konnte ich einige nützliche Hinweise für unsere tägliche Arbeit mitnehmen. Mein\nletzter Vortrag für diesen Tag\nwar ",[1002,70772,70776],{"href":70773,"rel":70774,"title":70775},"http://it-republik.de/konferenzen/ext_scripts/v2/php/sessions-popup.php?module=jax2012&id=20784",[1006],"\"Java Persistence: Standard meets Reality\"","“Java Persistence: Standard meets Reality”","\nvon Arne Limburg. Leider hatte ich übersehen, dass dieser Vortrag Teil des JavaEE days war und daher wenig allgemeine\nInformationen zu JPA enthielt. Trotzdem war es auch hier interessant den Vergleich zur Nutzung von JPA in Spring und\nJavaEE zu sehen.",[3938,70779,70780],{"id":1384},[448,70781,1385],{},[439,70783,70784,70785,70788],{},"Die JAX war auf jeden Fall eine sehr interessante Erfahrung für mich und ich freue mich schon auf meine nächste\nKonferenz. Ich habe viele Eindrücke zu Themen sammeln können, von denen ich vorher noch kaum etwas gehört habe.\nBesonders intensiv war hierbei der Einblick in die JavaEE Welt, was mir vor allem eine andere Sichtweise auf das von uns\neingesetzte Spring Framework eröffnete. Trotzdem war meine Themenauswahl hier wohl etwas ungünstig, da in den JavaEE\nVorträgen sowie im Workshop doch sehr viele Details vorgestellt wurden, die für meine tägliche Arbeit kaum relevant\nsind. Dennoch konnte ich auch für die konkret anstehenden Probleme in unserem Projekt durch die Vorträge von Adam Bien\nund den Vortrag über Best Practices mit Wicket einige Ideen sammeln, was mich die ",[1002,70786,70717],{"href":70714,"rel":70787,"title":70716},[1006],"\nschlussendlich als Erfolg verbuchen lässt.",{"title":469,"searchDepth":507,"depth":507,"links":70790},[70791,70792,70793],{"id":70721,"depth":507,"text":70724},{"id":70739,"depth":507,"text":70742},{"id":1384,"depth":507,"text":1385},[1031],"2012-04-19T10:03:46","Die JAX 2012 war durch ihren (für uns) günstig gelegenen Veranstaltungsort in Mainz mit\\neiner Anfahrtszeit von unter zwei Stunden ein leicht zu erreichendes Ziel und daher entschloss auch ich mich an der\\nKonferenz teilzunehmen. Eine Unterkunft hatte ich mir auch schnell besorgt, da ein alter Freund von mir in Mainz wohnt.\\nIch hatte mich für den JavaEE Workshop von Adam Bien am Montag entschieden und wollte dann noch den ersten Tag der\\nHauptkonferenz am Dienstag besuchen. Da dies meine erste Konferenz war, war ich sehr gespannt, was mich erwarten würde.","https://synyx.de/blog/meine-erste-konferenz-jax-2012/",{},"/blog/meine-erste-konferenz-jax-2012",{"title":70703,"description":70801},"Die JAX 2012 war durch ihren (für uns) günstig gelegenen Veranstaltungsort in Mainz mit\neiner Anfahrtszeit von unter zwei Stunden ein leicht zu erreichendes Ziel und daher entschloss auch ich mich an der\nKonferenz teilzunehmen. Eine Unterkunft hatte ich mir auch schnell besorgt, da ein alter Freund von mir in Mainz wohnt.\nIch hatte mich für den JavaEE Workshop von Adam Bien am Montag entschieden und wollte dann noch den ersten Tag der\nHauptkonferenz am Dienstag besuchen. Da dies meine erste Konferenz war, war ich sehr gespannt, was mich erwarten würde.","blog/meine-erste-konferenz-jax-2012",[9555,70804,9556],"jax","Die JAX 2012 war durch ihren (für uns) günstig gelegenen Veranstaltungsort in Mainz mit einer Anfahrtszeit von unter zwei Stunden ein leicht zu erreichendes Ziel und daher entschloss auch ich…","_1ARPKZ3-ver_QDrFlZLPWO43tcULzzRbItAFTc9o9I",{"id":70808,"title":70809,"author":70810,"body":70811,"category":71019,"date":71020,"description":469,"extension":1034,"link":71021,"meta":71022,"navigation":916,"path":71023,"seo":71024,"slug":70815,"stem":71025,"tags":71026,"teaser":71027,"__hash__":71028},"blog/blog/konferenz-logbuch-bedcon-2012.md","Konferenz-Logbuch BedCon 2012",[166],{"type":432,"value":70812,"toc":71012},[70813,70816,70818,70827,70831,70834,70837,70840,70843,70852,70855,70858,70862,70865,70872,70875,70878,70881,70884,70893,70896,70905,70908,70911,70914,70917,70920,70923,70926,70929,70938,70947,70950,70954,70957,70960,70963,70966,70969,70976,70979,70982,70985,70993,71002,71005,71008],[435,70814,70809],{"id":70815},"konferenz-logbuch-bedcon-2012",[1065,70817,70742],{"id":70739},[439,70819,70820,70821,70826],{},"15:32 Rebecca gibt mir die Unterlagen, Tickets und Infos zur ",[1002,70822,70825],{"href":70823,"rel":70824},"http://bed-con.org",[1006],"BedCon",". Perfekt organisiert – sie hat\nsich bereits um alles gekümmert.",[1065,70828,70830],{"id":70829},"mittwoch-anreise","Mittwoch – Anreise",[439,70832,70833],{},"16:32 Flo, Aljona und ich brechen mit Sack und Pack und einer Ikea-Tasche mit Bier in Richtung Hbf auf. Unterwegs stößt\nMarkus dazu.",[439,70835,70836],{},"17:00 Wir sind im Zug und es geht los. Wir öffnen ein Bier – eine Stunde vor der offiziellen synyx Bier-Time.",[439,70838,70839],{},"17:50 Mittlerweile sind wir in Mannheim umgestiegen in den ICE Sprinter der nur noch in Frankfurt hält und dann bis\nBerlin durchfährt. Die Zugbegleiterin weist bereits zum dritten mal auf diesen Umstand hin. Markus hat uns ein\nunreserviertes Abteil gefunden, und wir machen es uns gemütlich. Kurz darauf halten wir doch nochmal um verpeilte\nFahrgäste rauszulassen die nicht nach Berlin wollen. Es folgen viele Gespräche – Gradle, synyx, Projekte, Konferenzen,\nPrivates. Markus recherchiert gefühlt 70% der Zeit die Verbindung vom Bahnhof zum Hotel per Google Maps, DB App und\nverschiedenen anderen Hilfsmitteln.",[439,70841,70842],{},"21:07 Unser Reiseproviant ist leer, wir siedeln um ins Bordbistro.",[439,70844,70845,70846,70851],{},"22:10 Markus’ intensive Recherche zahlt sich aus, denn er navigiert uns astrein zum Hotel. Das Zimmer\nbzw. ",[1002,70847,70850],{"href":70848,"rel":70849},"http://www.residenz-2000.de/",[1006],"das Hotel"," allgemein ist super.",[439,70853,70854],{},"22:30 Wir sind schon wieder raus. Auf der Suche nach Nahrung gehen wir zu einem Italiener um die Ecke. Top Location.\nGemütlich und sehr gutes Essen.",[439,70856,70857],{},"1:14 Uhr die Italiener wollen Feierabend machen, kehren uns quasi raus. Markus verlangt noch ein letztes Glas Wein, wir\nbekommen noch einen guten Grappa und verschwinden dann ins Bett.",[1065,70859,70861],{"id":70860},"donnerstag-erster-konferenztag","Donnerstag – Erster Konferenztag",[439,70863,70864],{},"6:45 Mein Wecker klingelt, ich bin müde. Ich quäle mich unter die Dusche, mache mich fertig und bin um 7:30 unten beim\nFrühstück. Anschließend brechen wir auf.",[439,70866,70867,70868,70871],{},"8:40 Wir kommen nach einer kurzen Suche im Informatikerbau der ",[1002,70869,36430],{"href":36428,"rel":70870},[1006]," an\nund holen unsere Badges ab.",[439,70873,70874],{},"9:00 Ich sitze mit Aljona im ersten Talk: “Wie wird mein Code testbar?”. Ein guter Talk, für unsere Arbeitsweise nichts\nneues, jedoch eine Bestätigung.",[439,70876,70877],{},"9:45 Der Talk ist zu Ende, es gibt recht spannende Fragen und Diskussionen zum Thema Testen, TDD und Methodik.",[439,70879,70880],{},"10:15 Wir hören Philosophie. Kann Software schön sein? Interessante Aspekte, es geht um Kant, Schönheit im Bezug auf\nSoftware. Der Speaker ist extrem gut und das Thema ist ansprechend. Ich bin jetzt schon von der BedCon begeistert.",[439,70882,70883],{},"11:24 Modularisierung wagen – Recht witzig gemachte Two Man Show über die Modularisierung von monolitischen Builds,\nquasi ein Erfahrungsbericht.",[439,70885,70886,70887,70892],{},"11:40 Die Erfahrungen der Jungs decken sich mehr und mehr mit denen die ich bei unseren Builds bei synyx auch gemacht\nhabe. Es gibt dennoch immer wieder neue Ideen und Anregungen die ich mir merke. Ich nehme mir vor, endlich\ndas ",[1002,70888,70891],{"href":70889,"rel":70890},"http://maven.apache.org/plugins/maven-enforcer-plugin/",[1006],"Maven Enforcer Plugin"," einzusetzen.",[439,70894,70895],{},"13:24 Was tut ein guter Softwarearchitekt? Eberhard erklärt mir mein Aufgabengebiet. Seine Vorstellungen und Meinungen\nsind interessant und decken sich großteils mit meinen Vorstellungen und Tätigkeiten.",[439,70897,70898,70899,70904],{},"13:42 Ich werde an ",[1002,70900,70903],{"href":70901,"rel":70902},"http://www.hello2morrow.com/products/sonargraph",[1006],"SonarGraph"," erinnert und daran, dass ich ein Tool\nzur Architekturanalyse / Architekturforcierung probieren will. Man merkt dass im Projektgeschäft doch leider oft die\nZeit fehlt neues wirklich auszuprobieren und umzusetzen.",[439,70906,70907],{},"14:05 Ich fühle mich alt weil der Speaker gerade das Lemmings (Computerspiel) erklärt. Es ist offentsichtlich nicht mehr\nselbstverständlich das zu kennen.",[439,70909,70910],{},"14:37 Continuous Delivery in der Praxis – Ein Erfahrungsbericht mit vielen interessanten Punkten. Spannendster Aspekt\nfür mich ist mit CD direkt bei Projektbeginn anzufangen und es mit dem Projekt iterativ wachsen zu lassen (aus einem\nShellskript-Commithook mit scp wird irgendwann eine Build-Pipeline).",[439,70912,70913],{},"15:37 Auf dem Gang verwickle ich mich in Gespräche über die Arbeitsweise in anderen Firmen und freue mich, bei synyx zu\narbeiten.",[439,70915,70916],{},"16:00 Matt in drei Iterationen – Ein unterhaltsamer Talk über die iterative Entwicklung einer Schach-Engine. Ich lerne\nviel über Schach und ein kleines bisschen was über Architektur.",[439,70918,70919],{},"16:34 Mir fällt auf dass die Schach-Engine wohl das beste Beispiel für Code / Entwicklung etc ist, das ich bislang\ngesehen habe, weil es das perfekte Maß an Komplexität hat.",[439,70921,70922],{},"17:10 Letzter Talk für heute: Design Thinking. Mein Kopf ist langsam voll. Wenig Bezug zur Software aber dennoch\ninteressant. Es geht darum, wie man interdisziplinär wirklich neue Ideen entwickleln kann.",[439,70924,70925],{},"18:12 Wahnsinn. Erst in der Nachspielzeit des Talks fällt bei mir der Groschen: Strukturen aufbrechen, kreativer werden,\neine neue (firmen)Kultur kreieren. Darum geht es hier. Ich glaube der Bezug zu meinem Job ist doch gar nicht so gering.",[439,70927,70928],{},"19:00 Treffen in der Lobby des Hotels zum Abmarsch nach Kreuzberg zur Nahrnugsaufnahme.",[439,70930,70931,70932,70937],{},"20:12 Wir landen in einem Laden namens “",[1002,70933,70936],{"href":70934,"rel":70935},"http://www.knofi.de/",[1006],"Knofi","“. Es gibt leckeres orientalisches Essen. Alle sind\nbegeistert, nur es war kaum Knoblauch drin.",[439,70939,70940,70941,70946],{},"21:32 Lars von Upstruct stößt zu uns, wir bestellen noch ein Bier und lassen uns etwas später von ihm in eine\nKreuzberger Kneipe führen: Das ",[1002,70942,70945],{"href":70943,"rel":70944},"http://franken-bar.de/",[1006],"Franken",". Wir trinken Bier und Cuba Libre.",[439,70948,70949],{},"2:34 Wir kommen mit dem Taxi nach Hause und ich falle totmüde ins Bett.",[1065,70951,70953],{"id":70952},"freitag-zweiter-konferenztag","Freitag – Zweiter Konferenztag",[439,70955,70956],{},"6:45 zzzZzzZz grmpf zzZzz argh grml. Aufstehen, duschen, Frühstück, packen. Hmpf.",[439,70958,70959],{},"7:55 Gefühlte 10 russische Schulklassen versperren die komplette Hotel-Lobby. Wir freuen uns, dass die nicht alle beim\nFrühstück waren und quetschen uns durch in Richtung BedCon.",[439,70961,70962],{},"9:01 Wir sitzen im ersten Talk – eine Einführung in Scala. Da ich mich bislang noch nicht mit der Sprache beschäftigt\nhabe schaue ich es mir an. Ganz interessant, ich sehe aber derzeit keinen Bedarf für mich. Ich beschließe irgendwann mir\nden weiterführenden Talk direkt im Anschluß daher nicht anzuschauen. Außerdem versuche ich die Müdigkeit irgendwie zu\nbekämpfen.",[439,70964,70965],{},"10:12 Ich sitze in einem Talk über JBoss 7 (Blazing Fast). zzzZzzZz. Es interessiert mich nicht wirklich, ob der\nApplicationserver in 2.4 oder 1.8 Sekunden hochfährt. Der Speaker gibt einen ganz guten Überblick über die neuen\nFeatures. Ich stelle aber fest, dass mich Applicationserver einfach nicht so wahnsinnig interessieren.",[439,70967,70968],{},"10:40 Der Jboss-Talk ist extrem zu früh fertig und ich treffe Markus im Chillout Raum. Ich setze mich mit meinem\nSchwarztee auf einen Sitzsack und wir unterhalten uns.",[439,70970,70971,70972,70975],{},"11:20 Wir bekommen einen groben Überblick über ",[1002,70973,53946],{"href":53944,"rel":70974},[1006]," und andere Tools zur ordentlichen\nund kontrollierten Datenbank-Schemamigration. Leider wenig neues im Vergleich zu dem, was Jochen letztes Jahr bei\nSchule@Synyx über Liquibase erzählt hat. Es gibt aber spannende Diskussionen zwischendurch.",[439,70977,70978],{},"12:38 In der Mittagspause beschließen Markus und ich was warmes bei einem Restaurant um die Ecke zu essen. Auf die Frage\nob Flo mitkommt antwortet er “Ich bleib hier”. Markus versteht das wohl falsch und sagt nur “Natürlich trinken wir auch\nn Bier.”. Ich verstehe nicht wie man schon wieder an Bier denken kann.",[439,70980,70981],{},"13:18 Der JPA-Vortrag ist dermaßen voll dass ich zum CoffeeScript-Talk gehe: Warum Kaffee gut für Entwickler ist.",[439,70983,70984],{},"13:23 Erstmal geht es 5 Minuten um Kaffee und dessen Zubereitung. Einige sind verwirrt, alle anderen sind begeistert von\neinem unglaublich unterhaltsamen weil humorvollen Talk der zudem inhaltlich gut ist.",[439,70986,70987,70988,70992],{},"13:34 Ich habe jetzt natürlich auf einmal Lust auf nen guten Espresso. Immerhin: Der Kaffee auf der BedCon ist\nverhältnismäßig gut. Ansonsten läuft eine kleine Diskussionsrunde über JavaScript\nund ",[1002,70989,52408],{"href":70990,"rel":70991},"http://coffeescript.org/",[1006],". Die setzen wir nach dem Talk auf dem Gang noch etwas fort.",[439,70994,70995,70996,71001],{},"14:30 Schemaevolution in einer ",[1002,70997,71000],{"href":70998,"rel":70999},"http://www.mongodb.org/",[1006],"MongoDB",". Ein Erfahrungsbericht aus dem selben Projekt wie am\nDonnerstag der Bericht über Continuous Delivery. Es gab spannende Erkenntnisse durch den guten Anwendungsfall für eine\nschemafreie Datenbank. Auch die Kombination mit CD ist schlüssig und sinnvoll.",[439,71003,71004],{},"15:31 Wir warten auf den letzten Talk: “Move fast and break Things”. Ein Bericht aus der Arbeit bei Soundcloud inclusive\ninteressanter Meinungen zur Kultur und Arbeitsweise von SW-Entwicklern. Man erkennt viele Parallelen zu unserer\nEntwicklungskultur und es gibt dennoch weitere neue Ideen.",[439,71006,71007],{},"16:36 Die Konferenz ist zu Ende, wir laufen diskutierend Richtung Hotel. Es geht um mögliche Verbesserungen einiger\nDetails bei synyx. Ich freue mich, Erkenntnisse aus der Konferenz direkt anzudenken und einzusetzen. Alles in allem bin\nich unendlich müde und auch sehr zufrieden. Die BedCon hat sich für mich definitiv gelohnt. Der beste Talk war eindeutig\nder Philosophie-Vortrag, gefolgt von CoffeeScript und Schach.",[1065,71009,71011],{"id":71010},"eindrücke","Eindrücke",{"title":469,"searchDepth":507,"depth":507,"links":71013},[71014,71015,71016,71017,71018],{"id":70739,"depth":547,"text":70742},{"id":70829,"depth":547,"text":70830},{"id":70860,"depth":547,"text":70861},{"id":70952,"depth":547,"text":70953},{"id":71010,"depth":547,"text":71011},[1031],"2012-04-02T13:40:16","https://synyx.de/blog/konferenz-logbuch-bedcon-2012/",{},"/blog/konferenz-logbuch-bedcon-2012",{"title":70809,"description":469},"blog/konferenz-logbuch-bedcon-2012",[16640,8276,9555,9556],"Dienstag 15:32 Rebecca gibt mir die Unterlagen, Tickets und Infos zur BedCon. Perfekt organisiert – sie hat sich bereits um alles gekümmert. Mittwoch – Anreise 16:32 Flo, Aljona und ich…","Ggx5_3JaegPMY935P22HEDM5t41_bL2Gz_4pUVU6xuE",{"id":71030,"title":71031,"author":71032,"body":71033,"category":71046,"date":71047,"description":469,"extension":1034,"link":71048,"meta":71049,"navigation":916,"path":71050,"seo":71051,"slug":71052,"stem":71053,"tags":71054,"teaser":71056,"__hash__":71057},"blog/blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben.md","Unsere Azubis erzählen wie sie ihre Ausbildung bei synyx erleben",[172],{"type":432,"value":71034,"toc":71044},[71035,71038],[435,71036,71031],{"id":71037},"unsere-azubis-erzählen-wie-sie-ihre-ausbildung-bei-synyx-erleben",[439,71039,71040],{},[2205,71041],{"alt":469,"src":71042,"title":71043},"https://media.synyx.de/uploads//2012/03/aljona_matze.jpg","aljona_matze",{"title":469,"searchDepth":507,"depth":507,"links":71045},[],[13208],"2012-03-28T14:39:21","https://synyx.de/blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben/",{},"/blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben",{"title":71031,"description":469},"unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben","blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben",[13219,71055],"fachinformatiker","Aljona und Matze sind seit einem Jahr bei uns in der Ausbildung zum Fachinformatiker. Aljona in der Fachrichtung Anwendungsentwicklung und Matze in der Fachrichtung Systemintegration. Wie haben die Zwei das…","irV41VhJdkbWPC5X0WonFp2Gc8ftceFhZ3LgipCBKYw",{"id":71059,"title":71060,"author":71061,"body":71062,"category":71207,"date":71208,"description":71209,"extension":1034,"link":71210,"meta":71211,"navigation":916,"path":71212,"seo":71213,"slug":71066,"stem":71214,"tags":71215,"teaser":71216,"__hash__":71217},"blog/blog/works-on-my-machine-developing-and-testing-continuous-delivery-with-vagrant.md","'Works on my machine!' – Developing and Testing Continuous Delivery with Vagrant",[27],{"type":432,"value":71063,"toc":71205},[71064,71068,71071,71079,71088,71091,71117,71120,71132,71135,71197,71200,71203],[435,71065,71067],{"id":71066},"works-on-my-machine-developing-and-testing-continuous-delivery-with-vagrant","\"Works on my machine!\" – Developing and Testing Continuous Delivery with Vagrant",[439,71069,71070],{},"I still hear it often in teams, even in agile ones where unit tests, integration tests and continuous integration are\nintegrated in daily work. One team member says it’s working and another with slightly different local environment says\nit’s not. Even more serious: local environments often don’t reflect the deployment environments, for example because the\nfavorite Linux distribution of the developer is not the required server distribution or it just has slightly different\nconfiguration and software versions.",[439,71072,71073,71074,71078],{},"To solve these issues and for testing continuous deployments including provisioning I had an eye\non ",[1002,71075,51329],{"href":71076,"rel":71077},"http://vagrantup.com/",[1006]," for a while. But with limited time I only tried small test installations until\nrecently.",[439,71080,71081,71082,71087],{},"Our marketing department is quite technical compared to other companies, yet still using non-Linux OS for various\nreasons. To edit and test new content for\nour ",[1002,71083,71086],{"href":71084,"rel":71085},"http://blog.synyx.de/2012/03/new-homepage-with-nanoc-twitter-bootstrap-less-and-git/",[1006],"homepage"," the local\ncompilation didn’t always work out as expected (tested and deployed on Linux by developers). Perfect testbed for a real\nworld usage of Vagrant.",[439,71089,71090],{},"The goal: Editors just installing Vagrant, updating their repository and starting via vagrant:",[464,71092,71094],{"className":54685,"code":71093,"language":54687,"meta":469,"style":469},"\ngit clone git://some.repository/path.git\nvagrant up\n\n",[471,71095,71096,71100,71109],{"__ignoreMap":469},[474,71097,71098],{"class":476,"line":477},[474,71099,917],{"emptyLinePlaceholder":916},[474,71101,71102,71104,71106],{"class":476,"line":507},[474,71103,19320],{"class":480},[474,71105,57210],{"class":484},[474,71107,71108],{"class":484}," git://some.repository/path.git\n",[474,71110,71111,71114],{"class":476,"line":547},[474,71112,71113],{"class":480},"vagrant",[474,71115,71116],{"class":484}," up\n",[439,71118,71119],{},"And everything is ready for usage in a started VM, first website already compiled and can be viewed by typing the VM’s\nIP into my local browser.",[439,71121,71122,71123,25806,71127,1402],{},"On way might be to prepare a VirtualBox image with everything preinstalled, but the disadvantage is I need to maintain\nthe image with software updates, changes etc. manually. With Vagrant I tell it to use a basic small Debian or Ubuntu box\nwhich is downloaded from given URL and everything is provisioned via ",[1002,71124,51207],{"href":71125,"rel":71126},"https://github.com/puppetlabs/puppet",[1006],[1002,71128,71131],{"href":71129,"rel":71130},"https://github.com/opscode/chef",[1006],"Chef",[439,71133,71134],{},"Preparation was a combination of a small Vagrantfile and provisioning via Puppet. My Vagrantfile:",[464,71136,71138],{"className":46996,"code":71137,"language":46998,"meta":469,"style":469},"\nVagrant::Config.run do |config|.\n config.vm.box = \"lucid32\"\n config.vm.box_url = \"http://files.vagrantup.com/lucid32.box\"\n config.vm.boot_mode = :gui\n config.vm.network :hostonly, \"192.168.33.10\"\n config.vm.provision :puppet do |puppet|\n puppet.manifests_path = \"puppet/manifests\"\n puppet.module_path = \"puppet/modules\"\n puppet.manifest_file = \"init.pp\"\n end\nend\n\n",[471,71139,71140,71144,71149,71154,71159,71164,71169,71174,71179,71184,71189,71193],{"__ignoreMap":469},[474,71141,71142],{"class":476,"line":477},[474,71143,917],{"emptyLinePlaceholder":916},[474,71145,71146],{"class":476,"line":507},[474,71147,71148],{},"Vagrant::Config.run do |config|.\n",[474,71150,71151],{"class":476,"line":547},[474,71152,71153],{}," config.vm.box = \"lucid32\"\n",[474,71155,71156],{"class":476,"line":584},[474,71157,71158],{}," config.vm.box_url = \"http://files.vagrantup.com/lucid32.box\"\n",[474,71160,71161],{"class":476,"line":607},[474,71162,71163],{}," config.vm.boot_mode = :gui\n",[474,71165,71166],{"class":476,"line":642},[474,71167,71168],{}," config.vm.network :hostonly, \"192.168.33.10\"\n",[474,71170,71171],{"class":476,"line":663},[474,71172,71173],{}," config.vm.provision :puppet do |puppet|\n",[474,71175,71176],{"class":476,"line":694},[474,71177,71178],{}," puppet.manifests_path = \"puppet/manifests\"\n",[474,71180,71181],{"class":476,"line":700},[474,71182,71183],{}," puppet.module_path = \"puppet/modules\"\n",[474,71185,71186],{"class":476,"line":913},[474,71187,71188],{}," puppet.manifest_file = \"init.pp\"\n",[474,71190,71191],{"class":476,"line":920},[474,71192,47024],{},[474,71194,71195],{"class":476,"line":926},[474,71196,47103],{},[439,71198,71199],{},"The rest is installed and prepared via puppet modules and manifests on first “vagrant up” which I provide in the\nprojects repository in a puppet/ directory.",[439,71201,71202],{},"With the release of Vagrant 1.0 not long ago I think this has potential for a wider adoption of not only local setups\nof one or multiple VMs for development, but also to test more automation of various kinds for developers, testers,\nsysadmins -> DevOps. On the way to better Continuous Delivery.",[1024,71204,20482],{},{"title":469,"searchDepth":507,"depth":507,"links":71206},[],[1030],"2012-03-23T17:49:14","I still hear it often in teams, even in agile ones where unit tests, integration tests and continuous integration are\\nintegrated in daily work. One team member says it’s working and another with slightly different local environment says\\nit’s not. Even more serious: local environments often don’t reflect the deployment environments, for example because the\\nfavorite Linux distribution of the developer is not the required server distribution or it just has slightly different\\nconfiguration and software versions.","https://synyx.de/blog/works-on-my-machine-developing-and-testing-continuous-delivery-with-vagrant/",{},"/blog/works-on-my-machine-developing-and-testing-continuous-delivery-with-vagrant",{"title":71060,"description":71070},"blog/works-on-my-machine-developing-and-testing-continuous-delivery-with-vagrant",[],"I still hear it often in teams, even in agile ones where unit tests, integration tests and continuous integration are integrated in daily work. One team member says it’s working…","JHm6eMBEOQWSrFbmkFAlA1f-fwO1HK1qxC3rdquHHOU",{"id":71219,"title":71220,"author":71221,"body":71222,"category":71370,"date":71371,"description":71372,"extension":1034,"link":71373,"meta":71374,"navigation":916,"path":71375,"seo":71376,"slug":71226,"stem":71377,"tags":71378,"teaser":71381,"__hash__":71382},"blog/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git.md","New Homepage with nanoc, Twitter Bootstrap, LESS and Git",[27],{"type":432,"value":71223,"toc":71361},[71224,71227,71230,71238,71241,71245,71248,71257,71260,71264,71282,71286,71289,71293,71301,71305,71314,71318,71321,71330,71333,71336,71345,71347,71356,71359],[435,71225,71220],{"id":71226},"new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[439,71228,71229],{},"With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic\ndecisions filling our needs.",[439,71231,71232,71233,71237],{},"Our previous websites were always implemented in OpenCms since we are ",[1002,71234,71236],{"href":13089,"rel":71235},[1006],"OpenCms","\nSolution Provider and contributor. Using a CMS like OpenCms seemed the natural decision and it worked well for several\nversions of our homepage. But given the amount of work needed to set it up, develop templates and administrate it, was\nit really what we needed?",[439,71239,71240],{},"A CMS is useful if you have many editors, granular access management with user roles, editor/publisher workflows etc.\nBut we are a team of technically versed people, even our marketing prefers to edit HTML directly instead of rely on a\nWYSIWYG editor where you don’t always know the resulting markup. So OpenCms was a little heavy-weight for our homepage\nwhere we didn’t need most of it’s features.",[3938,71242,71244],{"id":71243},"finding-the-right-tool-for-our-needs","Finding the right tool for our needs",[439,71246,71247],{},"Result of analyzing the old homepage was that 99% of it was static content. The only dynamic parts were blog aggregation\nand a contact form. Only for these two it wasn’t worth using a CMS or application framework. Yet we still wanted some\ntemplating and generate static HTML.",[439,71249,71250,71251,71256],{},"There are many static HTML generation tools. I’m not going into detail on which we looked into, just the solution we\ncame up with. Some evaluation lead to ",[1002,71252,71255],{"href":71253,"rel":71254},"http://nanoc.stoneship.org/",[1006],"nanoc"," which is simple to set-up, supports various\ntemplating formats, is very flexible, has good documentation and is easy to use.",[439,71258,71259],{},"What about the contact form and blog aggregation? Contact forms that only send an email are no added value to an email\naddress. Analysis of it’s usage on the old homepage resulted in that it attracted mainly spam and very few real\nmessages. Blog aggregation is sufficient every few minutes and doesn’t need to be live. So instead of dynamically\nfetching and rendering, it can be done by a cronjob and regenerating server-side.",[3938,71261,71263],{"id":71262},"templating","Templating",[439,71265,71266,71267,520,71272,7267,71276,71281],{},"For templating we\nuse ",[1002,71268,71271],{"href":71269,"rel":71270},"https://ruby-doc.org/stdlib/libdoc/erb/rdoc/ERB.html",[1006],"“ERB”",[1002,71273,71275],{"href":63663,"rel":71274},[1006],"“Twitter Bootstrap”",[1002,71277,71280],{"href":71278,"rel":71279},"https://lesscss.org",[1006],"“LESS”"," for CSS. Nanoc supports precompiling LESS and compressing CSS.",[1065,71283,71285],{"id":71284},"erb","ERB",[439,71287,71288],{},"ERB is part of Ruby’s standard library and quite similar to JSP templating in the Java world. All we need is HTML, some\nmeta data replacement from pages (e. g. a title) and snippet inclusion for which nanoc provides a rendering helper.",[1065,71290,71292],{"id":71291},"less","LESS",[439,71294,71295,71296,71300],{},"LESS enables a more maintainable way of writing CSS since it extends CSS by dynamic behavior such as variables, mixins,\noperations and functions. We chose it over ",[1002,71297,71299],{"href":44696,"rel":71298},[1006],"SCSS"," because we also use Twitter Bootstrap. I think\nin modern CSS styling you should use either of them to make your CSS maintainable and readable. For example defining our\nnew CI colors as variables or mixins for common button stylings is a good use of these features.",[1065,71302,71304],{"id":71303},"twitter-bootstrap","Twitter Bootstrap",[439,71306,71307,71308,71313],{},"The latest popular layouting/css toolkit for common things like a grid layout. In my point of view the main advantage to\nolder CSS frameworks like ",[1002,71309,71312],{"href":71310,"rel":71311},"http://960.gs/",[1006],"960 Grid System"," is the use of LESS in Twitter Bootstrap, so for example the\ngrid columns can be altered by changing variables.",[3938,71315,71317],{"id":71316},"deployment-and-publishing","Deployment and Publishing",[439,71319,71320],{},"We set-up deployment via Git. Basically a Git post-receive hook calls a shell script that loads the correct Ruby\nversion via rvm and executes a rake task:",[464,71322,71324],{"className":16895,"code":71323,"language":16897,"meta":469,"style":469},"rake deploy:post_receive\n",[471,71325,71326],{"__ignoreMap":469},[474,71327,71328],{"class":476,"line":477},[474,71329,71323],{},[439,71331,71332],{},"The rake task then updates dependencies via ‘bundle install’, fetches blog posts via RSS and Tweets via Twitter API,\ncleans up old files and compiles the site via nanoc.",[439,71334,71335],{},"So adding new content or changing some styling is as easy as doing that locally, commiting and publishing by",[464,71337,71339],{"className":16895,"code":71338,"language":16897,"meta":469,"style":469},"git push stage\n",[471,71340,71341],{"__ignoreMap":469},[474,71342,71343],{"class":476,"line":477},[474,71344,71338],{},[439,71346,20416],{},[464,71348,71350],{"className":16895,"code":71349,"language":16897,"meta":469,"style":469},"git push live\n",[471,71351,71352],{"__ignoreMap":469},[474,71353,71354],{"class":476,"line":477},[474,71355,71349],{},[439,71357,71358],{},"The advantage of using Git in this way is not just the easy publishing, but also it’s main feature: versioning. Content\nchanges can be followed, reproduced and rolled back. For example if you have a marketing campaign and styled your page\nin X-mas colors, you can do so in a branch and after New Years you can switch back to the usual styling by switching\nthe branch again.",[1024,71360,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":71362},[71363,71364,71369],{"id":71243,"depth":507,"text":71244},{"id":71262,"depth":507,"text":71263,"children":71365},[71366,71367,71368],{"id":71284,"depth":547,"text":71285},{"id":71291,"depth":547,"text":71292},{"id":71303,"depth":547,"text":71304},{"id":71316,"depth":507,"text":71317},[1030],"2012-03-06T10:59:57","With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic\\ndecisions filling our needs.","https://synyx.de/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git/",{},"/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git",{"title":71220,"description":71229},"blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[71379,22162,19320,71086,71291,71255,26636,64517,46998,71380,389],"cms","styling","With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic decisions filling our needs. Our previous websites were always implemented in OpenCms…","QKXG5zey8Vf0GGkf5yVxdjTPxZR1fNOEpeJFFbIENL4",{"id":71384,"title":71385,"author":71386,"body":71387,"category":71418,"date":71419,"description":71420,"extension":1034,"link":71421,"meta":71422,"navigation":916,"path":71423,"seo":71424,"slug":71425,"stem":71426,"tags":71427,"teaser":71428,"__hash__":71429},"blog/blog/schlag-den-chef-oder-schlagt-er-dich.md","Schlag den Chef oder schlägt er dich?",[262],{"type":432,"value":71388,"toc":71416},[71389,71392,71395,71398,71401,71404,71407,71410],[435,71390,71385],{"id":71391},"schlag-den-chef-oder-schlägt-er-dich",[439,71393,71394],{},"Samstag gegen späten Nachmittag haben sich wohl einige Leute gewundert, was sich dort in der Albert-Schweitzer-Halle\nam Mühlburger Tor abspielt. Ein Haufen junger und nicht mehr ganz so junger, verrückt aussehender Leute tummelten sich\nhier. Umherfliegende Seifenblasen machten schon von Weitem darauf aufmerksam, dass dort irgendetwas passiert.",[439,71396,71397],{},"Sollte es hier nun einen Aufstand geben?",[439,71399,71400],{},"Nein! Ganz mit der Ruhe. Es war nur die synyx’sche Winterfeier, die hier stattfinden sollte.",[439,71402,71403],{},"Lange haben die Mitarbeiter auf das Event gewartet. Für mich selbst war es die erste Winterfeier im Hause synyx und man\nhat so einige Geschichten über die vorangegangenen Feiern gehört. Die Sommerfeier war für mich schon ein Highlight das,\nmeiner Meinung nach, kaum noch zu toppen war. Auch gingen vorab viele Gerüchte umher was denn so geplant sei für die\nWinterfeier. Man munkelte, es würde getanzt werden und viele fiese Gemeinheiten für die Mitarbeiter solle man sich\nausgedacht haben. Auch wurde gerätselt, wozu denn Schneebesen, Messbecher, Silikonschläuche und Lebensmittelfarben\nbenötigt würden.",[439,71405,71406],{},"Aber alles der Reihe nach. Genau wie wir Mitarbeiter müsst nun auch ihr Leser euch ein wenig gedulden.",[439,71408,71409],{},"Empfangen wurden die Mitarbeiter also in der Albert-Schweitzer-Halle. Vorne über der Bühne wurde ein großes\n„Willkommen“ projeziert. Schaute man sich so um wirkte das ganze erstmal, bis auf ein paar wenige Ausnahmen, wie eine\ndoch recht formelle Firmenfeier. Aber das sollte sich bald ändern.",[439,71411,71412],{},[2205,71413],{"alt":469,"src":71414,"title":71415},"https://media.synyx.de/uploads//2012/02/P1050328-150x150.jpg","P1050328",{"title":469,"searchDepth":507,"depth":507,"links":71417},[],[1031],"2012-02-06T11:30:06","Samstag gegen späten Nachmittag haben sich wohl einige Leute gewundert, was sich dort in der Albert-Schweitzer-Halle\\nam Mühlburger Tor abspielt. Ein Haufen junger und nicht mehr ganz so junger, verrückt aussehender Leute tummelten sich\\nhier. Umherfliegende Seifenblasen machten schon von Weitem darauf aufmerksam, dass dort irgendetwas passiert.","https://synyx.de/blog/schlag-den-chef-oder-schlagt-er-dich/",{},"/blog/schlag-den-chef-oder-schlagt-er-dich",{"title":71385,"description":71394},"schlag-den-chef-oder-schlagt-er-dich","blog/schlag-den-chef-oder-schlagt-er-dich",[62431,389],"Samstag gegen späten Nachmittag haben sich wohl einige Leute gewundert, was sich dort in der Albert-Schweitzer-Halle am Mühlburger Tor abspielt. Ein Haufen junger und nicht mehr ganz so junger, verrückt…","kOs-n6cDtO_n6u_ZIMPPFh6_WpupLN6dvr9-qBqfGeU",{"id":71431,"title":71432,"author":71433,"body":71434,"category":71459,"date":71460,"description":71461,"extension":1034,"link":71462,"meta":71463,"navigation":916,"path":71464,"seo":71465,"slug":71466,"stem":71467,"tags":71468,"teaser":71469,"__hash__":71470},"blog/blog/10-jahre-synyx-ein-besonderes-jubilaum.md","10 Jahre synyx – ein besonderes Jubiläum",[172],{"type":432,"value":71435,"toc":71457},[71436,71439,71442,71445,71448,71451,71454],[435,71437,71432],{"id":71438},"_10-jahre-synyx-ein-besonderes-jubiläum",[439,71440,71441],{},"Es ist kaum zu glauben, dass es nun schon 10 Jahre her ist, als synyx durch Thomas Kraft, Markus Daniel und Joachim\nArrasz gegründet wurde. Damals – alle drei waren noch Studenten – trafen sie sich, um ihre gemeinsame Vision einer\neigenen Firma in allen Details zu diskutieren. Schnell wurden sie sich einig und so wurde die synyx, damals noch als\nOHG, gegründet.",[439,71443,71444],{},"Zunächst wurde das erste Büro in der Technologiefabrik bezogen, erste Projekte liefen an. 2004 zog synyx in die\nAugustastraße und der erste Student wurde eingestellt. Zu den Bereichen Individualsoftware und Content Management, kam\nein weiteres Standbein “Mobile Solutions” hinzu. Bereits ein Jahr später konnte sich synyx im neuen Geschäftsfeld “Code\nClinic” erfolgreich unter Beweis stellen.",[439,71446,71447],{},"Nur zwei Jahre später, 2006, wurde die synyx OHG in die GmbH & Co. KG umgewandelt. Es wurden immer mehr Mitarbeiter\neingestellt und synyx bezog die Büroräume in der Karlstraße. 2007 konnte synyx ihr 5-jähriges Bestehen feiern. Nachdem\nsynyx bereits Diplomanden betreute, wurde in diesem Jahr auch der erste Azubi eingestellt. Im folgenden Jahr hatte synyx\n20 Mitarbeiter und es wurde sportlich: synyx nahm am Baden-Marathon teil und das „synoccer“ wurde eingeführt. D.h. ein\nMal im Monat treffen sich die Mitarbeiter, um gemeinsam Indoor-Fussball zu spielen. Das war eine willkommene\nAbwechslung zum ständigen Sitzen am Rechner.",[439,71449,71450],{},"2009 wurde das Büro um ein zweites Stockwerk erweitert und ein paar Monate später wurde dort die Cafe Lounge eröffnet.\nSeit diesem Jahr sponsort synyx die Java User Group Karlsruhe und unterstützt das Kiva Projekt. 2011 stand dann ganz\nunter dem Zeichen der Öffentlichkeitsarbeit. synyx engagierte sich im Social Media Bereich und forcierte mehr\nPressearbeit. Für die Mitarbeiter gab es ein weiteres Goodie, die Kochmuddi. Sie kocht seitdem für synyx zwei Mal in der\nWoche zum Selbstkostenpreis.",[439,71452,71453],{},"In der zweiten Hälfte des letzten Jahres wurde dann kräftig an der neuen Corporate Identity von synyx gearbeitet. Das\nLogo, das Layout der Website und die Geschäftsausstattung wurde von upstruct kreiert. Pünktlich zum 10-jährigen\nJubiläum geht synyx im neuen Look online.",[439,71455,71456],{},"Wir sind gespannt was die nächsten 10 Jahre bringen. Auf jeden Fall werden wir dieses Ereignis im Sommer 2012 gebührend\nfeiern.",{"title":469,"searchDepth":507,"depth":507,"links":71458},[],[1031],"2012-01-02T14:57:49","Es ist kaum zu glauben, dass es nun schon 10 Jahre her ist, als synyx durch Thomas Kraft, Markus Daniel und Joachim\\nArrasz gegründet wurde. Damals – alle drei waren noch Studenten – trafen sie sich, um ihre gemeinsame Vision einer\\neigenen Firma in allen Details zu diskutieren. Schnell wurden sie sich einig und so wurde die synyx, damals noch als\\nOHG, gegründet.","https://synyx.de/blog/10-jahre-synyx-ein-besonderes-jubilaum/",{},"/blog/10-jahre-synyx-ein-besonderes-jubilaum",{"title":71432,"description":71441},"10-jahre-synyx-ein-besonderes-jubilaum","blog/10-jahre-synyx-ein-besonderes-jubilaum",[389],"Es ist kaum zu glauben, dass es nun schon 10 Jahre her ist, als synyx durch Thomas Kraft, Markus Daniel und Joachim Arrasz gegründet wurde. Damals – alle drei waren…","8i8BZDHmaxnFYm0B4DSkx4mxh1_389zDZYmb0DYy2m0",{"id":71472,"title":71473,"author":71474,"body":71475,"category":71543,"date":71544,"description":71482,"extension":1034,"link":71545,"meta":71546,"navigation":916,"path":71547,"seo":71548,"slug":71479,"stem":71549,"tags":71550,"teaser":71551,"__hash__":71552},"blog/blog/wie-man-mitarbeiter-ohne-brettspiele-plant.md","Wie man Mitarbeiter ohne Brettspiele plant",[9],{"type":432,"value":71476,"toc":71541},[71477,71480,71483,71490,71493,71502,71539],[435,71478,71473],{"id":71479},"wie-man-mitarbeiter-ohne-brettspiele-plant",[439,71481,71482],{},"Ressourcenplanung Classic",[439,71484,71485,71486,71489],{},"Es ist Freitag Vormittag, Anfang Oktober 2011. Sechs Mitarbeiter und einer unserer Geschäftsführer versammeln sich um\neine große Tafel, die auf dem Tisch in der Mitte unseres Besprechungsraums liegt. Auf der Tafel haften fast 1.000\ndaumennagelgroße Plättchen, jedes farbig umrahmt und beklebt mit dem Bild von einem unserer 23 Mitarbeiter.\nEiner der versammelten Mitarbeiter ist der",[990,71487,71488],{},"Moderator"," und erhält von den umstehenden Teilnehmern fortlaufend\nAnweisungen. Jemand möchte, dass eine bestimmte Anzahl von Plättchen auf der Tafel von einer Region in eine andere\ngeschoben wird. Der nächste Mitspieler schaut sich das in Ruhe an, überlegt kurz, und will, dass andere Plättchen auf\neinen anderen Teil der Tafel geschoben werden. Der Nächste ist damit nicht ganz einverstanden und fordert, dass die\nPlättchen wieder zurück kommen. Der Vierte ist sich nicht ganz sicher, ob überhaupt die richtige Anzahl von Plättchen\nauf dem Tisch liegt.\nNach nach etwa 45 Minuten haben sich alle mehr oder weniger auf eine Anordnung geeinigt. Der Moderator notiert auf einem\nZettel, wie viele Plättchen mit welcher Farbe und Bildchen an welchem Ort befindet und schickt das Ergebnis in einer\nEmail an die Mitarbeiter. Das Spiel ist beendet. Es gibt keinen Gewinner.\nWas wie lahme Version von Risiko aussieht, ist die Planung unserer Mitarbeiter (Unternehmerdeutsch: Ressourcenplanung).\nJedes der Plättchen repräsentiert zwei Stunden Arbeitszeit des Mitarbeiters, die gekennzeichneten Bereiche sind die\nProjekte, für er oder sie arbeiten soll. Ein rotes Plättchen steht für die nächste Kalenderwoche, ein Gelbes für die\nübernächste, ein Schwarzes für die Woche danach. Die Projektleiter und die Geschäftsführung verhandeln dann, welcher\nMitarbeiter in den nächsten drei Wochen in welchem Projekt arbeiten soll. Dazu muss man allerdings wissen, ob ein\nMitarbeiter zum Beispiel Urlaub hat oder es einen Feiertag gibt. Die entsprechende Anzahl von Plättchen mit dem\nrichtigen Bild und der richtigen Farbe muss also ausgerechnet und anschließend auf die Tafel gelegt werden. Die\nProjektleiter haben auf ihrem Zettel notiert, wieviel Arbeitszeit sie von ihren Mitarbeitern für die nächsten Wochen\nbenötigen, der Moderator schiebt sie dann auf der Tafel zu den Projekten, für die ein Mitarbeiter arbeiten soll.\nUrsprünglich gab es bei Synyx keine explizite Planung der Ressourcen, und die Arbeit wurde ad hoc aufgeteilt. Mit der\nZeit wurde dieses System durch eine gemeinsame Besprechung ersetzt, bei der informell die Arbeitsplanung auf einem\nWhiteboard festgehalten wurde. Dieser Prozess wurde von einem Student der Wirtschafts-Informatik an der Hochschule\nKarlsruhe in seiner Bachelor-Arbeit analysiert und verbessert.\nErgebnis ist das eben beschriebene System, ein gewaltiger Fortschritt, aber trotzdem mit Nachteilen verbunden: Die\nPlättchen müssen angefertigt werden, neue Mitarbeiter müssen fotografiert, Bilder gedruckt und auf die Plättchen geklebt\nwerden. Die Fotos sind recht klein und nicht immer leicht zu unterscheiden, was das Zählen schwierig macht. Die\nVerfügbarkeit der Mitarbeiter wird im Vorfeld ermittelt und die errechnete Anzahl von Plättchen auf die Tafel gebracht.\nDiese Vorbereitungen beschäftigen die Mitarbeiter eine ganze Weile vor Beginn der eigentlichen Planung.",[439,71491,71492],{},"Ressourcenplanung on Crack",[439,71494,71495,71496,71501],{},"Seit Oktober bin ich Praktikant bei synyx. Meine Aufgabe ist es, die Ressourcenplanung ins 21. Jahrhundert zu bringen.\nKeine Plättchen, keine Tafel, keine Zettel und kein Nachrechnen mehr.\nAuf der synyx-Plattform ",[1002,71497,71500],{"href":71498,"rel":71499},"https://github.com/synyx/minos",[1006],"Minos","basierend habe ich in den letzten vier Monaten eine\nWeb-Applikation entwickelt, die es uns erlaubt, die Mitarbeiter einfach und bequem im Browser zu planen. Im Laufe der\nWoche tragen die Projektleiter ihre Anforderungen in einer Maske ein.\nAm Freitag versammeln sich wie früher alle, die bei der Planung mitzureden haben, und legen fest wo welcher Mitarbeiter\narbeitet. Anforderungen sind womöglich unvollständig, oder es wird mehr angefordert, als es die Verfügbarkeit des\nMitarbeiters erlaubt. So können wir statt Plättchen auf dem Tisch zu schieben auf die Leinwand schauen, wo zum Beispiel\ndiese Ansicht projiziert wird",[439,71503,71504,71505,71508,71509,71511,71512,520,71517,1489,71522,71527,71528,71533,71534,71538],{},"Hier stehen übersichtlich die Anforderungen und die Zuteilungen für das Projekt ",[990,71506,71507],{},"Synovierung","gegenüber. Die Zuteilungen\nwerden dann von einem zum ",[990,71510,71488],{},"ernannten Mitarbeiter bearbeitet. Wenn jemand also für die nächste Woche noch\nArbeitszeit übrig hat, wird sein Name blau hinterlegt (Matthias). Der Moderator erhöht dann die Zuteilung für einen\nArbeitstag, bis der Name grau erscheint (wie bei Aljona). Wenn jemand überbucht ist, wird die Arbeitszeit für ein\nProjekt reduziert. Zum Schluss speichert der Moderator die Zuteilung für dieses Projekt, und steuert das nächste an.\nDie Planungs- und Vorbereitungszeit ist jetzt ein Bruchteil im Vergleich zum alten System.\nHinter dieser Software steckt natürlich eine Menge\nArbeit: ",[1002,71513,71516],{"href":71514,"rel":71515},"http://blog.synyx.de/autoren/?uid=14",[1006],"Fabian",[1002,71518,71521],{"href":71519,"rel":71520},"http://blog.synyx.de/autoren/?uid=12",[1006],"Sebastian",[1002,71523,71526],{"href":71524,"rel":71525},"http://blog.synyx.de/autoren/?uid=16",[1006],"Marc","\nund ich haben uns eine ganze Weile überlegt, was überhaupt zu machen ist: Was sind die Planungseinheiten? Woher kommen\ndie Informationen über die Verfügbarkeit? Wie soll die Oberfläche aussehen? Die Implementierung als Web-Applikation\numfasst neben Minos, Hades, Spring, HTML5/JavaScript viele weitere Technologien. Permanent wird von abstrakten Konzepten\nbis zu Implementierungs-Details an allem gearbeitet. Das Wissen, das ich mir dabei angeeignet habe, kann man unmöglich\nin einem Blog-Post unterbringen, daher werden bestimmt noch einige folgen.\nDank voller Punktzahl beim ",[1002,71529,71532],{"href":71530,"rel":71531},"http://www.joelonsoftware.com/articles/fog0000000043.html",[1006],"Joel-Test","ist synyx genau das\nrichtige Unternehmen für dieses Projekt. Wenn der Code Anfang nächsten Jahres auf",[1002,71535,27413],{"href":71536,"rel":71537},"http://github.com",[1006],"steht, könnt\nihr euch selbst davon überzeugen.",[17894,71540],{},{"title":469,"searchDepth":507,"depth":507,"links":71542},[],[13208],"2011-12-23T09:52:33","https://synyx.de/blog/wie-man-mitarbeiter-ohne-brettspiele-plant/",{},"/blog/wie-man-mitarbeiter-ohne-brettspiele-plant",{"title":71473,"description":71482},"blog/wie-man-mitarbeiter-ohne-brettspiele-plant",[],"Ressourcenplanung Classic Es ist Freitag Vormittag, Anfang Oktober 2011. Sechs Mitarbeiter und einer unserer Geschäftsführer versammeln sich um eine große Tafel, die auf dem Tisch in der Mitte unseres…","RI-eiyuhMrK8Ib9Z8nX_0wzPzLZsE5q9s213Vlfj30M",{"id":71554,"title":71555,"author":71556,"body":71557,"category":71905,"date":71906,"description":71907,"extension":1034,"link":71908,"meta":71909,"navigation":916,"path":71910,"seo":71911,"slug":71561,"stem":71912,"tags":71913,"teaser":71914,"__hash__":71915},"blog/blog/developers-developers-developers.md","Developers, Developers, Developers!",[365],{"type":432,"value":71558,"toc":71900},[71559,71562,71565,71584,71592,71609,71619,71668,71678,71681,71685,71699,71709,71730,71739,71747,71750,71754,71763,71773,71788,71803,71818,71833,71842,71863,71878,71882,71891,71894],[435,71560,71555],{"id":71561},"developers-developers-developers",[439,71563,71564],{},"Belgium, November 2011. More than three and a half thousand people are surging into Antwerp’s megaplex, the Kinepolis.\nIt’s Devoxx-time!",[439,71566,71567,48178,71572,71577,71578,71583],{},[1002,71568,71571],{"href":71569,"rel":71570},"http://vimeo.com/32170355",[1006],"Devoxx 2011",[1002,71573,71576],{"href":71574,"rel":71575},"http://vimeo.com/royvanrijn",[1006],"Roy van Rijn"," on ",[1002,71579,71582],{"href":71580,"rel":71581},"http://vimeo.com",[1006],"Vimeo"," (\nMit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von Vimeo).",[439,71585,71586,71587,1402],{},"From November 14th to November 18th Europe’s biggest community-organized (i.e. not organized by a big corporation) Java\nconference took place in its traditional venue in Antwerp and of course Synyx was there, too. Marc already wrote about\nwhy we like attending Devoxx in ",[1002,71588,71591],{"href":71589,"rel":71590},"http://blog.synyx.de/2011/11/reasons-why-i-go-to-devoxx/",[1006],"Reasons why I go to Devoxx",[994,71593,71594,71597,71600],{},[997,71595,71596],{},"Few to almost no marketing talks. In contrast to many other conferences there were refreshingly few marketing talks\nfrom the sponsors or other companies. At least the ones I’ve attended were pretty much free of targeted marketing but\nmore stuffed with technical details. Exactly what you like to see as a developer.",[997,71598,71599],{},"Great diversity in intelligent and friendly attendees, which also showed on the ad-hoc polls on the whiteboards\nprovided in the hallway.",[997,71601,71602,71603,71608],{},"Awesome venue for conducting a conference. The cinema chairs are really comfortable – which is too bad if you’re hung\nover from the night before and try to focus on the talk. Also the big screens and powerful sound systems are a great\nasset and I’m sure that the speakers also enjoyed the large rooms where everyone could easily see and hear them.\nBetween talks they showed a big ",[1002,71604,71607],{"href":71605,"rel":71606},"https://web.archive.org/web/20171113063602/http://wall.devoxx.com:80/",[1006],"Twitter wall","\non the screens in the rooms and on another big screen in the hallway which was nice for having quick feedback how\npeople liked the previous talk or which rooms got crowded very fast.",[439,71610,71611],{},[1002,71612,71615],{"href":71613,"rel":71614},"https://media.synyx.de/uploads//2011/11/devoxx11_twitterwall.jpg",[1006],[2205,71616],{"alt":71617,"src":71618,"title":71617},"Devoxx 2011 - Twitter wall","https://media.synyx.de/uploads//2011/11/devoxx11_twitterwall-300x225.jpg",[994,71620,71621,71659,71662,71665],{},[997,71622,71623,71624,520,71629,520,71633,520,71638,520,71643,520,71648,45657,71653,71658],{},"Large spectrum of (Java- and JVM-related) topics with great diversity. There were a lot of talks about alternative\nlanguages on the JVM\nlike ",[1002,71625,71628],{"href":71626,"rel":71627},"http://www.scala-lang.org/",[1006],"Scala",[1002,71630,71632],{"href":47173,"rel":71631},[1006],"JRuby",[1002,71634,71637],{"href":71635,"rel":71636},"http://clojure.org/",[1006],"Clojure",[1002,71639,71642],{"href":71640,"rel":71641},"https://groovy-lang.org/",[1006],"Groovy",[1002,71644,71647],{"href":71645,"rel":71646},"http://confluence.jetbrains.net/display/Kotlin/Welcome",[1006],"Kotlin",[1002,71649,71652],{"href":71650,"rel":71651},"http://fantom.org/",[1006],"Fantom",[1002,71654,71657],{"href":71655,"rel":71656},"http://ceylon-lang.org/",[1006],"Ceylon",". There were also many talks on Android (a little too many if you’d ask me) and\nHTML5; both topics were dominated by speakers from Google, which was a Premium Partner for the first time.",[997,71660,71661],{},"Free and (mostly) stable WiFi. The organizers did a great job with the conference WiFi which is not easy to operate\nfor more than 3,500 people, many with more than one device and eager to use it. Although some people had problems\ngetting a connection the wireless pretty much just worked for me over the course of the five days.",[997,71663,71664],{},"Free mugs and bags. In celebration of the 10th anniversary of Javapolis Javoxx Devoxx there were complementary coffee\nmugs with three different motives (HTML5, Android, or Java) available to pick up for the conference attendees. Instead\nof the traditional backpacks, the organizers prepared messenger bags this time for the conference attendees.",[997,71666,71667],{},"Merchandise. Like on every other conference there were quite a few exhibiting companies with which you could trade\nyour personal data (very convenient with the bar codes and QR code on the wristband) for a T-Shirt, a stress ball, or\nsome other form of merchandise.",[439,71669,71670],{},[1002,71671,71674],{"href":71672,"rel":71673},"https://media.synyx.de/uploads//2011/11/devoxx11_screen1.jpg",[1006],[2205,71675],{"alt":71676,"src":71677,"title":71676},"Devoxx 2011 - Big screen","https://media.synyx.de/uploads//2011/11/devoxx11_screen1-300x225.jpg",[439,71679,71680],{},"My highlights this year were mainly during the university days of Devoxx. The conference is split in two large parts,\nthe university and the conference days. Usually the university days are less packed and have longer (3 hours), in-depth\nsession while there are more 1 hour talks during the conference days.",[3938,71682,71684],{"id":71683},"university-days","University days",[439,71686,22944,71687,71692,71693,71698],{},[1002,71688,71691],{"href":71689,"rel":71690},"http://devoxx.com/display/DV11/Continuous+Delivery",[1006],"Continuous Delivery"," by David Farley was really good. He\nrecapped some things I already knew from reading the book he co-authored (see\nmy ",[1002,71694,71697],{"href":71695,"rel":71696},"http://blog.synyx.de/2011/08/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline/",[1006],"Review of Continuous Delivery",")\nbut also told a lot about how they implemented continuous delivery at LMAX.",[439,71700,71701],{},[1002,71702,71705],{"href":71703,"rel":71704},"https://media.synyx.de/uploads//2011/11/devoxx11_wellgrounded2.jpg",[1006],[2205,71706],{"alt":71707,"src":71708,"title":71707},"Devoxx 2011 - The well-grounded Java developer","https://media.synyx.de/uploads//2011/11/devoxx11_wellgrounded2-300x225.jpg",[439,71710,71711,71712,71717,71718,71723,71724,71729],{},"Another very informative and entertaining talk\nwas ",[1002,71713,71716],{"href":71714,"rel":71715},"http://devoxx.com/display/DV11/The+Well-Grounded+Java+Developer",[1006],"The Well-Grounded Java Developer"," by Martijn\nVerburg and Ben Evans. While it didn’t really contain any breaking news – you probably have heard or read about the\nfeatures of Java 7 around 365 times before and have heard about the concurrency\npackage ",[1002,71719,71722],{"href":71720,"rel":71721},"http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html",[1006],"java.util.concurrent"," as\nwell – it was a very well executed talk and forced the audience to pay attention to these important topics. Also the\nlive-action play with a set of Dukes was priceless as well as the cameos of the diabolical developer and\nthe ",[1002,71725,71728],{"href":71726,"rel":71727},"http://en.wikipedia.org/wiki/Otter",[1006],"otter"," metaphore for the concurrency part of the session.",[439,71731,71732],{},[1002,71733,71736],{"href":71734,"rel":71735},"https://media.synyx.de/uploads//2011/11/devoxx11_well_grounded1.jpg",[1006],[2205,71737],{"alt":71707,"src":71738,"title":71707},"https://media.synyx.de/uploads//2011/11/devoxx11_well_grounded1-225x300.jpg",[439,71740,71741,71746],{},[1002,71742,71745],{"href":71743,"rel":71744},"http://devoxx.com/display/DV11/Jenkins++From+Continuous+Integration+to+Continuous+Delivery",[1006],"Jenkins: From Continuous Integration to Continuous Delivery","\nin the Tools in Action track was also interesting as John Smart described and showed how to use Jenkins and a battery of\nplugins to implement Continuous Delivery. Since we use Jenkins internally as our CI product, I took some good ideas out\nof this talk.",[439,71748,71749],{},"In the evening of the second university day there was the Fireside chat with Tim Bray, Cameron Purdy, Mark Reinhold and\nHenrik Ståhl which is a very interesting format but started way too slow in my opinion. When the participants had warmed\nup after half an hour it got a lot better.",[3938,71751,71753],{"id":71752},"conference-days","Conference days",[439,71755,71756,71757,71762],{},"Wednesday was the first of three conference days (as opposed to university days) and started with three keynotes.\nFirst ",[1002,71758,71761],{"href":71759,"rel":71760},"http://twitter.com/stephan007",[1006],"Stephan Janssen"," started with his welcome speech in which he celebrated the 10th\nanniversary of Devoxx. This was followed by Henrik Ståhl’s keynote which positively surprised me. Mr. Ståhl hadn’t\nimpressed too much on the evening before during the fireside chat. The last keynote was held by Cameron Purdy about Java\nEE but it turned into a boring marketing talk after about half way through, which is sad because it started really\npromising.",[439,71764,71765],{},[1002,71766,71769],{"href":71767,"rel":71768},"https://media.synyx.de/uploads//2011/11/devoxx11_joshbloch1.jpg",[1006],[2205,71770],{"alt":71771,"src":71772,"title":71771},"Josh Bloch at Devoxx 2011","https://media.synyx.de/uploads//2011/11/devoxx11_joshbloch1-300x225.jpg",[439,71774,71775,71776,71781,71782,71787],{},"There were also awesome talks on this day,\nprominently ",[1002,71777,71780],{"href":71778,"rel":71779},"http://devoxx.com/display/DV11/Java++The+Good%2C+the+Bad%2C+and+the+Ugly+Parts",[1006],"Java: The Good, the Bad, and the Ugly Parts","\nby Joshua Bloch\nand ",[1002,71783,71786],{"href":71784,"rel":71785},"http://devoxx.com/display/DV11/Language+++Library+co-evolution+in+Java+SE+8",[1006],"Language / Library co-evolution in Java SE 8","\nby Brian Goetz, two Java veterans with a lot of conference speaking experience. As always with high quality talks and\nspeakers, the rooms were crowded during their performances.",[439,71789,71790,71791,71796,71797,71802],{},"Much of the charm of Devoxx are its informal BOF sessions. Particularly\nthe ",[1002,71792,71795],{"href":71793,"rel":71794},"http://devoxx.com/display/DV11/Less+annoying+Java+standards",[1006],"Less annoying Java standards"," BOF with Ben Evans and\nMartijn Verburg was very interesting. They presented\nthe ",[1002,71798,71801],{"href":71799,"rel":71800},"http://java.net/projects/ljc-london-jug/pages/AdoptAJSRProgram",[1006],"Adopt a JSR Program"," and discussed how the Java\ncommunity could participate on the JCP more easily.",[439,71804,71805,71806,71811,71812,71817],{},"The second conference day again started with a keynote, this time on Android by Tim Bray, and was packed with good\ntalks. Some talks I attended were really exceptional, so I want to name them quickly. Jonas Bonér\nof ",[1002,71807,71810],{"href":71808,"rel":71809},"http://typesafe.com/",[1006],"Typesafe"," held a talk about [Above the Clouds: Introducing Akka](",[1002,71813,71816],{"href":71814,"rel":71815},"http://akka.io/%3EAkka",[1006],"http://akka.io/>Akka","\ntitled \u003Ca href=). Although I don’t warm towards Scala as a programming language, Akka seems to be quite a killer\nframework for solving concurrency-related problems. It comes with a Scala and a Java API but the Scala API looks (\nnaturally?) more elegant.",[439,71819,71820,71821,71826,71827,71832],{},"Another very interesting talk\nwas ",[1002,71822,71825],{"href":71823,"rel":71824},"http://devoxx.com/display/DV11/The+Disruptor%2C+High+Performance+Inter-Thread+Messaging",[1006],"The Disruptor, High Performance Inter-Thread Messaging","\nwhich was scheduled for Monday (during the university days) but was cancelled on short notice. Fortunately that talk was\nrescheduled for Thursday during the lunch break. Michael Barker gave an introduction to\nthe ",[1002,71828,71831],{"href":71829,"rel":71830},"http://code.google.com/p/disruptor/",[1006],"Disruptor"," framework and walked the audience through an example utilizing it\nin a simple application.",[439,71834,71835,71836,71841],{},"And even more highlights took place on this day: Dick Wall (of Java Posse fame) talked\nabout ",[1002,71837,71840],{"href":71838,"rel":71839},"http://devoxx.com/display/DV11/Courage+in+Software+Development",[1006],"Courage in Software Development",". This was a\nnon-technical but very inspiring session and I recommend watching it as soon as it’s uploaded to Parleys.",[439,71843,71844,71845,71850,71851,71856,71857,71862],{},"Following the official conference program (in parallel to the BOF sessions) there was a screening of the recently\nreleased ",[1002,71846,71849],{"href":71847,"rel":71848},"https://web.archive.org/web/20151116171536/http://www.us.movie.tintin.com:80/",[1006],"Tintin movie"," and after that\nthe anniversary party in the nearby ",[1002,71852,71855],{"href":71853,"rel":71854},"https://web.archive.org/web/20120528164636/http://noxxantwerp.eu/",[1006],"Noxx"," night\nclub – of course with a dancing ",[1002,71858,71861],{"href":71859,"rel":71860},"http://kenai.com/projects/duke",[1006],"Duke",". 😉",[439,71864,71865,71866,71871,71872,71877],{},"On the fifth and last day we sadly had only time for\nthe ",[1002,71867,71870],{"href":71868,"rel":71869},"http://devoxx.com/display/DV11/Technical+Discussion+Panel",[1006],"Technical Discussion Panel"," and one more talk. Most of\nus\nchose ",[1002,71873,71876],{"href":71874,"rel":71875},"http://devoxx.com/display/DV11/The+Evolution+of+Java++Past%2C+Present%2C+and+Future",[1006],"The Evolution of Java: Past, Present, and Future","\nwhich was again very interresting and in which Joshua Bloch retrospectively reviewed changes to the Java language over\ntime and scored them from “horrible” to “best thing since sliced bread” (or something like that).",[3938,71879,71881],{"id":71880},"summary","Summary",[439,71883,71884,71885,71890],{},"In summary I really enjoyed Devoxx and learned a lot during these five extraordinary days. My days were quite stuffed\nand I wish I had more time for seeing Antwerp but you have to set priorities. I really recommend going to Devoxx (or to\nthe upcoming ",[1002,71886,71889],{"href":71887,"rel":71888},"http://www.devoxx.fr/",[1006],"Devoxx France",", if you are a French native speaker) to any serious Java (or\nJVM-based language) developer.",[439,71892,71893],{},"And in case you wondered about the title of this blog post:",[439,71895,71896,17782],{},[1002,71897,18674],{"href":71898,"rel":71899},"https://www.youtube.com/embed/8To-6VIJZRE",[1006],{"title":469,"searchDepth":507,"depth":507,"links":71901},[71902,71903,71904],{"id":71683,"depth":507,"text":71684},{"id":71752,"depth":507,"text":71753},{"id":71880,"depth":507,"text":71881},[1031],"2011-12-01T10:33:51","Belgium, November 2011. More than three and a half thousand people are surging into Antwerp’s megaplex, the Kinepolis.\\nIt’s Devoxx-time!","https://synyx.de/blog/developers-developers-developers/",{},"/blog/developers-developers-developers",{"title":71555,"description":71564},"blog/developers-developers-developers",[8276,37617],"Belgium, November 2011. More than three and a half thousand people are surging into Antwerp’s megaplex, the Kinepolis. It’s Devoxx-time! Mit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von…","pS5Entgz2VL_e1UkSscIdfRbp7Fqt3MJn7E3DUBG6Gk",{"id":71917,"title":71918,"author":71919,"body":71920,"category":72235,"date":72236,"description":72237,"extension":1034,"link":72238,"meta":72239,"navigation":916,"path":72240,"seo":72241,"slug":71924,"stem":72242,"tags":72243,"teaser":72245,"__hash__":72246},"blog/blog/android-listview-with-rounded-corners.md","Android: ListView with rounded corners",[190],{"type":432,"value":71921,"toc":72233},[71922,71925,71928,71931,71934,71937,72003,72006,72092,72095,72098,72141,72144,72147,72228,72231],[435,71923,71918],{"id":71924},"android-listview-with-rounded-corners",[439,71926,71927],{},"In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android\nand iPhone and they needed to look somewhat alike.",[439,71929,71930],{},"In this blogpost, I want to show you how I’ve implemented it and hopefully help some people who also want to use\nListViews with rounded corners:",[439,71932,71933],{},"First off, we need the drawables for the backgrounds of the Lists entries:",[439,71935,71936],{},"For the entries in the middle of the list, we don’t need rounded corners, so create a xml in your drawable folder\n“list_entry_middle.xml” with following content:",[464,71938,71940],{"className":6253,"code":71939,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem>\n \u003Cshape>\n \u003Cstroke android:width=\"1px\" android:color=\"#ffbbbbbb\"/>\n \u003C/shape>\n \u003C/item>\n \u003Citem android:bottom=\"1dp\" android:left=\"1dp\" android:right=\"1dp\">\n \u003Cshape>\n \u003Csolid android:color=\"#ffffffff\"/>\n \u003C/shape>\n \u003C/item>\n\u003C/layer-list>\n",[471,71941,71942,71946,71951,71956,71961,71966,71971,71976,71981,71985,71990,71994,71998],{"__ignoreMap":469},[474,71943,71944],{"class":476,"line":477},[474,71945,65310],{},[474,71947,71948],{"class":476,"line":507},[474,71949,71950],{},"\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n",[474,71952,71953],{"class":476,"line":547},[474,71954,71955],{}," \u003Citem>\n",[474,71957,71958],{"class":476,"line":584},[474,71959,71960],{}," \u003Cshape>\n",[474,71962,71963],{"class":476,"line":607},[474,71964,71965],{}," \u003Cstroke android:width=\"1px\" android:color=\"#ffbbbbbb\"/>\n",[474,71967,71968],{"class":476,"line":642},[474,71969,71970],{}," \u003C/shape>\n",[474,71972,71973],{"class":476,"line":663},[474,71974,71975],{}," \u003C/item>\n",[474,71977,71978],{"class":476,"line":694},[474,71979,71980],{}," \u003Citem android:bottom=\"1dp\" android:left=\"1dp\" android:right=\"1dp\">\n",[474,71982,71983],{"class":476,"line":700},[474,71984,71960],{},[474,71986,71987],{"class":476,"line":913},[474,71988,71989],{}," \u003Csolid android:color=\"#ffffffff\"/>\n",[474,71991,71992],{"class":476,"line":920},[474,71993,71970],{},[474,71995,71996],{"class":476,"line":926},[474,71997,71975],{},[474,71999,72000],{"class":476,"line":932},[474,72001,72002],{},"\u003C/layer-list>\n",[439,72004,72005],{},"For the rounded corners, create another xml, “rounded_corner_top.xml”:",[464,72007,72009],{"className":6253,"code":72008,"language":6255,"meta":469,"style":469},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem>\n \u003Cshape>\n \u003Cstroke android:width=\"1dp\" android:color=\"#ffbbbbbb\"/>\n \u003Ccorners android:topLeftRadius=\"20dp\"\n android:topRightRadius=\"20dp\"\n />\n \u003C/shape>\n \u003C/item>\n \u003Citem android:top=\"1dp\" android:left=\"1dp\" android:right=\"1dp\" android:bottom=\"1dp\">\n \u003Cshape>\n \u003Csolid android:color=\"#ffffffff\"/>\n \u003Ccorners android:topLeftRadius=\"20dp\"\n android:topRightRadius=\"20dp\"\n />\n \u003C/shape>\n \u003C/item>\n\u003C/layer-list>\n",[471,72010,72011,72015,72019,72023,72027,72032,72037,72042,72047,72051,72055,72060,72064,72068,72072,72076,72080,72084,72088],{"__ignoreMap":469},[474,72012,72013],{"class":476,"line":477},[474,72014,62648],{},[474,72016,72017],{"class":476,"line":507},[474,72018,71950],{},[474,72020,72021],{"class":476,"line":547},[474,72022,71955],{},[474,72024,72025],{"class":476,"line":584},[474,72026,71960],{},[474,72028,72029],{"class":476,"line":607},[474,72030,72031],{}," \u003Cstroke android:width=\"1dp\" android:color=\"#ffbbbbbb\"/>\n",[474,72033,72034],{"class":476,"line":642},[474,72035,72036],{}," \u003Ccorners android:topLeftRadius=\"20dp\"\n",[474,72038,72039],{"class":476,"line":663},[474,72040,72041],{}," android:topRightRadius=\"20dp\"\n",[474,72043,72044],{"class":476,"line":694},[474,72045,72046],{}," />\n",[474,72048,72049],{"class":476,"line":700},[474,72050,71970],{},[474,72052,72053],{"class":476,"line":913},[474,72054,71975],{},[474,72056,72057],{"class":476,"line":920},[474,72058,72059],{}," \u003Citem android:top=\"1dp\" android:left=\"1dp\" android:right=\"1dp\" android:bottom=\"1dp\">\n",[474,72061,72062],{"class":476,"line":926},[474,72063,71960],{},[474,72065,72066],{"class":476,"line":932},[474,72067,71989],{},[474,72069,72070],{"class":476,"line":938},[474,72071,72036],{},[474,72073,72074],{"class":476,"line":944},[474,72075,72041],{},[474,72077,72078],{"class":476,"line":950},[474,72079,72046],{},[474,72081,72082],{"class":476,"line":956},[474,72083,71970],{},[474,72085,72086],{"class":476,"line":962},[474,72087,71975],{},[474,72089,72090],{"class":476,"line":4876},[474,72091,72002],{},[439,72093,72094],{},"Implementing the bottom part is quite the same, just with bottomLeftRadius and bottomRightRadius. (maybe also create one\nwith all corners rounded, if the list only has one entry)",[439,72096,72097],{},"For better usability, also provide drawables with other colors for the different states, that the list item can have and\nreference them in another xml in the drawable folder (“selector_rounded_corner_top.xml”) as followed:",[464,72099,72101],{"className":6253,"code":72100,"language":6255,"meta":469,"style":469},"\n\u003Cselector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n android:state_pressed=\"true\"/>\n \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n android:state_focused=\"true\"/>\n \u003Citem android:drawable=\"@drawable/rounded_corner_top\"/>\n\u003C/selector>\n",[471,72102,72103,72107,72112,72117,72122,72126,72131,72136],{"__ignoreMap":469},[474,72104,72105],{"class":476,"line":477},[474,72106,917],{"emptyLinePlaceholder":916},[474,72108,72109],{"class":476,"line":507},[474,72110,72111],{},"\u003Cselector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n",[474,72113,72114],{"class":476,"line":547},[474,72115,72116],{}," \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n",[474,72118,72119],{"class":476,"line":584},[474,72120,72121],{}," android:state_pressed=\"true\"/>\n",[474,72123,72124],{"class":476,"line":607},[474,72125,72116],{},[474,72127,72128],{"class":476,"line":642},[474,72129,72130],{}," android:state_focused=\"true\"/>\n",[474,72132,72133],{"class":476,"line":663},[474,72134,72135],{}," \u003Citem android:drawable=\"@drawable/rounded_corner_top\"/>\n",[474,72137,72138],{"class":476,"line":694},[474,72139,72140],{},"\u003C/selector>\n",[439,72142,72143],{},"Now do the same for the other backgrounds of the list.",[439,72145,72146],{},"All that is left now, is to assign the right backgrounds in our ListAdapter like following:",[464,72148,72150],{"className":709,"code":72149,"language":711,"meta":469,"style":469},"@Override\npublic View getView(int position, View convertView, ViewGroup parent) {\n //...\n //skipping the view reuse stuff\n if (position == 0 && entry_list.size() == 1) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner);\n } else if (position == 0) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner_top);\n } else if (position == entry_list.size() - 1) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner_bottom);\n } else {\n view.setBackgroundResource(R.drawable.selector_middle);\n }\n //...\n //skipping the filling of the view\n}\n",[471,72151,72152,72156,72161,72166,72171,72176,72181,72186,72191,72196,72201,72206,72211,72215,72219,72224],{"__ignoreMap":469},[474,72153,72154],{"class":476,"line":477},[474,72155,29178],{},[474,72157,72158],{"class":476,"line":507},[474,72159,72160],{},"public View getView(int position, View convertView, ViewGroup parent) {\n",[474,72162,72163],{"class":476,"line":547},[474,72164,72165],{}," //...\n",[474,72167,72168],{"class":476,"line":584},[474,72169,72170],{}," //skipping the view reuse stuff\n",[474,72172,72173],{"class":476,"line":607},[474,72174,72175],{}," if (position == 0 && entry_list.size() == 1) {\n",[474,72177,72178],{"class":476,"line":642},[474,72179,72180],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner);\n",[474,72182,72183],{"class":476,"line":663},[474,72184,72185],{}," } else if (position == 0) {\n",[474,72187,72188],{"class":476,"line":694},[474,72189,72190],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner_top);\n",[474,72192,72193],{"class":476,"line":700},[474,72194,72195],{}," } else if (position == entry_list.size() - 1) {\n",[474,72197,72198],{"class":476,"line":913},[474,72199,72200],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner_bottom);\n",[474,72202,72203],{"class":476,"line":920},[474,72204,72205],{}," } else {\n",[474,72207,72208],{"class":476,"line":926},[474,72209,72210],{}," view.setBackgroundResource(R.drawable.selector_middle);\n",[474,72212,72213],{"class":476,"line":932},[474,72214,1276],{},[474,72216,72217],{"class":476,"line":938},[474,72218,72165],{},[474,72220,72221],{"class":476,"line":944},[474,72222,72223],{}," //skipping the filling of the view\n",[474,72225,72226],{"class":476,"line":950},[474,72227,703],{},[439,72229,72230],{},"Aaaand we’re done.",[1024,72232,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":72234},[],[11122,1413],"2011-11-21T10:38:22","In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android\\nand iPhone and they needed to look somewhat alike.","https://synyx.de/blog/android-listview-with-rounded-corners/",{},"/blog/android-listview-with-rounded-corners",{"title":71918,"description":71927},"blog/android-listview-with-rounded-corners",[11132,63100,72244,63101],"lists","In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android and iPhone and they needed to look somewhat…","YJnytc_0AozIrr4hpGG2OyB-5_wi2WxdvyjS_ltNk3E",{"id":72248,"title":71591,"author":72249,"body":72250,"category":72333,"date":72334,"description":72335,"extension":1034,"link":72336,"meta":72337,"navigation":916,"path":72338,"seo":72339,"slug":72254,"stem":72341,"tags":72342,"teaser":72343,"__hash__":72344},"blog/blog/reasons-why-i-go-to-devoxx.md",[166],{"type":432,"value":72251,"toc":72331},[72252,72255,72263,72270,72276,72292,72305,72318],[435,72253,71591],{"id":72254},"reasons-why-i-go-to-devoxx",[439,72256,72257,72258,72262],{},"Yet another year is almost over. One of the reasons I notice this is\nbecause ",[1002,72259,11462],{"href":72260,"rel":72261},"http://devoxx.com/display/DV11/Home",[1006]," is coming up again. And – of course – Synyx is going to be there.\nIn the last year four of Synyx’ employees attended the full conference. This year all of last years visitors are going\nagain, and even three more. So there has to be a reason why it’s so popular. This post is going to be about Devoxx and\nwhy I personally enjoy going there. Well… there are several reasons…",[439,72264,72265,72266,72269],{},"A big challenge in our business is ",[448,72267,72268],{},"staying up to date",". There are plenty of books, articles, tweets and blogs to read\nin order know what is going on in the world of software development. And there are even more things to filter out and\nforget (at least for a while) because they don’t apply to you and your daily work. Sometimes you just don’t have enough\ntime for this because you already have loads of work with current technologies in your day-to-day work.",[439,72271,72272,72275],{},[448,72273,72274],{},"The talks at Devoxx keep me in sync with what is going on"," and what is (probably) important. It has pre-selected\ntalks with relevance to Java or me as a Java developer / architect.",[439,72277,72278,72279,72284,72285,72288,72289],{},"Considering last year’s Devoxx there were many things that came to our knowledge and are in use at Synyx now. Some\naffected the ways we work at Synyx (Hello, ",[1002,72280,72283],{"href":72281,"rel":72282},"http://www.nealford.com/",[1006],"Neal Ford","), some the tools, libraries and\nframeworks we are using now (e. g. ",[1002,72286,66556],{"href":66553,"rel":72287},[1006],") and some simply increased our knowledge and\nbrought us up to date (like the “what’s new in” JPA, Spring or Java talks). ",[448,72290,72291],{},"Go see the stuff you can easily use in\nyour daily work.",[439,72293,72294,72295,72300,72301,72304],{},"In addition to the interesting topics of the talk you just have to look at\nthe ",[1002,72296,72299],{"href":72297,"rel":72298},"http://devoxx.com/display/DV11/Devoxxians",[1006],"Cast",". There are many well-known ",[448,72302,72303],{},"speakers"," which have great\nexperience with their topics and also know, how to present it to the audience. So it’s almost always a pleasure to\nlisten and learn from them because they are the best.",[439,72306,72307,72308,72311,72312,72317],{},"Another big thing that makes me really looking forward to next week is that I’ll spend a ",[448,72309,72310],{},"nice week in Antwerp with so\nmany of my co-workers and other friends",". I’m looking forward visiting ",[1002,72313,72316],{"href":72314,"rel":72315},"http://www.kellys.be/",[1006],"Kelly’s Irish Pub","\nagain, which is about 100m down the street from our hotel and has been our conference-table almost every evening/night\nlast year (and probably will be again this year).",[439,72319,72320,72321,72326,72327,72330],{},"And the last thing why I’m going there is optimism: As I requested in\nmy ",[1002,72322,72325],{"href":72323,"rel":72324},"http://blog.synyx.de/2010/12/devoxx-2010-revisited/",[1006],"last post about the conference"," they made the tickets more\nexpensive this year. So I hope they made this because they followed my suggestion to serve ",[448,72328,72329],{},"better coffee"," this\ntime :). Be sure, Synyx will report about it…",{"title":469,"searchDepth":507,"depth":507,"links":72332},[],[1030],"2011-11-10T09:42:44","Yet another year is almost over. One of the reasons I notice this is\\nbecause Devoxx is coming up again. And – of course – Synyx is going to be there.\\nIn the last year four of Synyx’ employees attended the full conference. This year all of last years visitors are going\\nagain, and even three more. So there has to be a reason why it’s so popular. This post is going to be about Devoxx and\\nwhy I personally enjoy going there. Well… there are several reasons…","https://synyx.de/blog/reasons-why-i-go-to-devoxx/",{},"/blog/reasons-why-i-go-to-devoxx",{"title":71591,"description":72340},"Yet another year is almost over. One of the reasons I notice this is\nbecause Devoxx is coming up again. And – of course – Synyx is going to be there.\nIn the last year four of Synyx’ employees attended the full conference. This year all of last years visitors are going\nagain, and even three more. So there has to be a reason why it’s so popular. This post is going to be about Devoxx and\nwhy I personally enjoy going there. Well… there are several reasons…","blog/reasons-why-i-go-to-devoxx",[8276,37617,711],"Yet another year is almost over. One of the reasons I notice this is because Devoxx is coming up again. And – of course – Synyx is going to be…","q1nNnb-qWWPQ3xGDD2js7mhC58DjQdJwUG3qIhu7ND8",{"id":72346,"title":72347,"author":72348,"body":72349,"category":72467,"date":72468,"description":469,"extension":1034,"link":72469,"meta":72470,"navigation":916,"path":72471,"seo":72472,"slug":72473,"stem":72474,"tags":72475,"teaser":72476,"__hash__":72477},"blog/blog/meine-ausbildung-bei-synyx-2.md","Meine Ausbildung bei Synyx",[190],{"type":432,"value":72350,"toc":72462},[72351,72353,72363,72365,72368,72373,72377,72380,72383,72386,72392,72396,72399,72402,72405,72409,72419,72421,72430,72437,72440,72449,72456,72459],[435,72352,72347],{"id":21415},[439,72354,72355],{},[1002,72356,72359],{"href":72357,"rel":72358},"https://media.synyx.de/uploads//2011/11/IMG_20111109_115806.jpg",[1006],[2205,72360],{"alt":72361,"src":72362,"title":72361},"Ausbildungswerkzeug 1","https://media.synyx.de/uploads//2011/11/IMG_20111109_115806-150x150.jpg",[439,72364,72361],{},[439,72366,72367],{},"Nach etwas mehr als 2 Jahren nehme ich mir mal die Zeit, etwas über meine Ausbildung bei Synyx zu schreiben (Und das\nnicht nur, weil es sonst mit dem Paddel gibt ",[439,72369,72370],{},[990,72371,72372],{},"Vor der Ausbildung wusste ich noch nicht so recht, was mich erwarten wird. Ich hatte vorher zwar schon einmal ein\nmehrwöchiges Praktikum bei einer anderen Firma absolviert, aber darin hatte ich nicht wirklich viel vom Beruf des\nFachinformatikers mitbekommen. Programmieren hatte ich vorher schon ein bisschen in der Schule und da mir das gefiel,\nentschied ich mich für diesen Beruf. Außerdem tat ich mich zu der Zeit noch recht schwer mit neuen Leuten konfrontiert\nzu werden, auch wenn ich die meisten schon auf der vorherigen Sommerfeier kennen lernen konnte.",[3938,72374,72376],{"id":72375},"erstes-jahr","Erstes Jahr",[439,72378,72379],{},"Im September 2009 begann meine Ausbildung bei Synyx damit, meinen Rechner einzurichten. Dafür hatte mir mein Ausbilder\nAlex einen Zettel mit ein paar Anweisungen und Tipps + eine IP-Adresse gegeben, von der aus ich Linux per netinstall\ninstallieren konnte.",[439,72381,72382],{},"In den folgenden Wochen und Monaten bekam ich von Alex die Grundlagen für die Entwicklung mit Java und auch mit JSP,\nHTML und CSS gezeigt. Die Zeit, in der er mit anderen Sachen beschäftigt war, konnte ich damit verbringen, Bücher zu\nlesen und kleine Übungen und Testprojekte anzugehen, um mehr Übung mit dem Programmieren zu bekommen. Wenn ich mit etwas\nnicht weiterkam, konnte ich stets Alex oder andere Mitarbeiter fragen und bekam von ihnen alles super erklärt.",[439,72384,72385],{},"Die Berufsschule war von Anfang an überhaupt kein Problem, da ich das Meiste vorher schon einmal in der Schule, oder,\nwie beim Programmieren, bei Synyx gelernt hatte.",[439,72387,72388],{},[2205,72389],{"alt":469,"src":72390,"title":72391},"https://media.synyx.de/uploads//2011/11/sudoku_screenshot2-200x300.png","sudoku_screenshot2",[3938,72393,72395],{"id":72394},"zweites-jahr","Zweites Jahr",[439,72397,72398],{},"In meinem zweiten Jahr wurde ich einem Produktivprojekt, das auf OpenCMS aufbaut, zugeteilt und habe so einerseits den\nUmgang mit OpenCMS, viel wichtiger jedoch, das Arbeiten im Team gelernt. Auch hier wurde mir jederzeit unter die Arme\ngegriffen, wenn ich Hilfe benötigte und so fand ich mich gut im Projekt ein. In diesem Projekt war ich auch erstmals bei\nMeetings mit Kunden dabei, was mir anfangs noch ziemlich unangenehm war. Nach ein paar Meetings, und als ich mehr Ahnung\nvom Projekt hatte, war das aber auch kein Problem mehr.",[439,72400,72401],{},"Teilweise arbeitete ich nebenbei an I-think-I-spider und anderen kleinen Sachen weiter. Zeit zum Lernen wurde mir\naber auch weiterhin noch gegeben.",[439,72403,72404],{},"Im Laufe des Jahres kam noch ein weiteres OpenCMS Projekt hinzu, das aber etwas anders aufgebaut war und somit doch\nwieder reichlich neue Erfahrungen brachte. Das Projekt wurde vorher von einer anderen Firma geleitet und nun sollten wir\ndaran weiter programmieren. Interessant war dabei, zu sehen, wie unterschiedlich die Ansätze der anderen Firma für\nverschiedene Sachen waren, obwohl die vorhandene Technik eigentlich dieselbe war.",[3938,72406,72408],{"id":72407},"heute","Heute",[439,72410,72411],{},[1002,72412,72415],{"href":72413,"rel":72414},"https://media.synyx.de/uploads//2011/11/IMG_20111109_120228.jpg",[1006],[2205,72416],{"alt":72417,"src":72418,"title":72417},"Ausbildungswerkzeug 2","https://media.synyx.de/uploads//2011/11/IMG_20111109_120228-150x150.jpg",[439,72420,72417],{},[439,72422,72423,72424,72429],{},"Nun stehe ich am Anfang meines dritten Ausbildungsjahres und wurde auch hier wieder einer neuen Abteilung bei uns,\nder ",[1002,72425,18566],{"href":72426,"rel":72427,"title":72428},"http://www.synyx.de/de/leistungen/code-clinic.html",[1006],"Synyx Code Clinic"," zugeteilt. Hier ist das\nProgramm, alte, gegen die Wand gefahrene Projekte von anderen Firmen zu retten und zu verarzten. Somit lerne ich wieder\neine ganz andere Seite des Berufes kennen: Eine ganz andere Projektstruktur, als ich sie bisher gesehen hatte, nur sehr\nwenig sauberer und verständlicher Code, alles komplett veraltet und viel Politik. Einerseits ist es anstrengender, als\ndie bisherigen Projekte, andererseits lerne ich aber auch sehr viel dabei.",[439,72431,72432],{},[448,72433,72434],{},[990,72435,72436],{},"-Ein paar Wochen später-",[439,72438,72439],{},"Wie bereits erwartet, ist es etwas ganz anderes, sich in ein so großes Projekt einzuarbeiten, als es bei meinen\nbisherigen Projekten der Fall war. Vor allem, weil viele Techniken eingesetzt werden, die ich bisher noch nicht\nverwendet hatte, aber auch, weil es von anderern programmiert wurde und der Code einfach einen ganz anderen Stil (und\nQualität…) hat, als ich es von Synyx gewohnt bin.",[439,72441,72442,72443,72448],{},"Die Problematik des Einarbeitens in ein fremdes Projekt wurde mir nochmals bewusst, als ich ein Android Projekt für das\nZKM in Karlsruhe übernahm –\ndie ",[1002,72444,72447],{"href":72445,"rel":72446,"title":72447},"http://www.karlsruhe.de/b1/kultur/kulturinkarlsruhe/kulturapp.de",[1006],"KulturApp Karlsruhe",". Hier\nwar es weniger die Größe, sondern vielmehr alle anderen oben genannten Punkte. Schmerzhaft musste ich in diesem Projekt\nfeststellen, wie man sich bei einem solchen Vorhaben dann auch mal mit dem Zeitaufwand verschätzen kann, wenn man diese\nFaktoren nicht genügend berücksichtigt. Davon schreibe ich jedoch später eventuell mehr in einem anderen Blogeintrag. Um\neinige Erfahrungen reicher bin ich durch dieses Projekt aber auf jeden Fall geworden und werde mich in zukünftigen\nProjekten besser vor den genannten Problemen hüten.",[439,72450,72451],{},[448,72452,72453],{},[990,72454,72455],{},"So…",[439,72457,72458],{},"das war es vorerst einmal von meiner Ausbildung, die manchmal ein wenig chaotisch abläuft, die aber (oder vielleicht\ngerade deshalb) doch sehr viel Spaß macht. Synyx ist ein lustiger Haufen, man lernt hier sehr viel und bekommt jederzeit\ngeholfen. Da hatte ich echt Glück, dass mir die erste Ausbildungsstelle, die ich fast bekommen hätte, im letzten\nAugenblick doch abgesagt wurde.",[439,72460,72461],{},"Ich hoffe ich habe euch hiermit einen guten Einblick verschafft und dass ich vielleicht den ein oder anderen dazu\nbewege, sich auch bei Synyx zu bewerben. 😛",{"title":469,"searchDepth":507,"depth":507,"links":72463},[72464,72465,72466],{"id":72375,"depth":507,"text":72376},{"id":72394,"depth":507,"text":72395},{"id":72407,"depth":507,"text":72408},[13208],"2011-11-09T12:28:33","https://synyx.de/blog/meine-ausbildung-bei-synyx-2/",{},"/blog/meine-ausbildung-bei-synyx-2",{"title":72347,"description":469},"meine-ausbildung-bei-synyx-2","blog/meine-ausbildung-bei-synyx-2",[13219],"Ausbildungswerkzeug 1 Nach etwas mehr als 2 Jahren nehme ich mir mal die Zeit, etwas über meine Ausbildung bei Synyx zu schreiben (Und das nicht nur, weil es sonst mit…","d9AsFLJtjPvjY5DlmUd-t6DFIXaERGlN1RlSUFfWxfI",{"id":72479,"title":72480,"author":72481,"body":72482,"category":72604,"date":72605,"description":72606,"extension":1034,"link":72607,"meta":72608,"navigation":916,"path":72609,"seo":72610,"slug":72486,"stem":72611,"tags":72612,"teaser":72615,"__hash__":72616},"blog/blog/elektronische-urlaubsverwaltung-made-by-youngsters.md","Elektronische Urlaubsverwaltung made by Youngsters",[30],{"type":432,"value":72483,"toc":72602},[72484,72487,72490,72493,72496,72501,72504,72507,72510,72513,72516,72536,72539,72542,72545,72548,72551,72554,72557,72560,72563,72572,72575,72578,72581,72584,72587,72590,72593,72596,72599],[435,72485,72480],{"id":72486},"elektronische-urlaubsverwaltung-made-by-youngsters",[439,72488,72489],{},"Für die erfolgreiche Genehmigung von Urlaub muss der synyx’sche Mitarbeiter sich bisher mit einem Stück Papier alias\n‘schriftlich ausgefüllter Urlaubsantrag’ bewaffnen und sich an einen der drei Chefs anpirschen, um eine Unterschrift zu\nergattern.",[439,72491,72492],{},"Dies ist nicht nur zeitaufwändig, sondern auch einfach nicht zeitgemäß für eine junge Software-Schmiede wie Synyx.",[439,72494,72495],{},"So erhielten Johannes Reuter (Studentische Hilfskraft seit 1. August 2011) und ich, Aljona Murygina (Auszubildende zur\nFachinformatikerin seit 1. August 2011), den Auftrag, das ganze Prozedere zu modernisieren, wir befinden uns\nschließlich im Jahr 2011:",[439,72497,72498],{},[448,72499,72500],{},"Ein Urlaubsverwaltungs-Tool als Webapplikation muss her.",[439,72502,72503],{},"Als Grundgerüst der Applikation wurden drei verschiedene Arten von Usern festgelegt:",[439,72505,72506],{},"der normale User (Antragssteller: Urlaub beantragen),",[439,72508,72509],{},"der Chef (Vergabeberechtigter: Urlaubsantrag genehmigen/ablehnen) und",[439,72511,72512],{},"das Office (eine Art Super-User mit Verwaltungs- und Editierfunktion: Urlaubsantrag bearbeiten).",[439,72514,72515],{},"Essentielle Grundfunktionen des Urlaubsverwaltungs-Tool sollen sein:",[994,72517,72518,72521,72524,72527,72530,72533],{},[997,72519,72520],{},"Die Authentifizierung soll mittels bereits vorhandenen LDAP-Benutzerdaten der Mitarbeiter erfolgen.",[997,72522,72523],{},"Urlaubsanträge sollen digital validiert werden, das heißt Antragssteller und Antraggenehmigender müssen jeweils über\neinen einzigartigen Identifikationsnachweis (‘digitale Unterschrift’) verfügen.",[997,72525,72526],{},"Eine Interaktion mit dem firmeninternen Google-Kalender soll für automatisierte Übersichtlichkeit sorgen, welche\nMitarbeiter in welchem Zeitraum im Urlaub sind.",[997,72528,72529],{},"Transparenz ist die Quintessenz einer Open Source Firma und hört auch nicht bei firmeninternen Vorgängen auf. Daher\nsollen jegliche Schritte der Bearbeitung von Anträgen durch eine Log-Datei nachvollziehbar sein können.",[997,72531,72532],{},"Via E-Mail sollen die jeweils Beteiligten über den laufenden Antragsvorgang informiert werden, ob zum Beispiel ein\nneuer zu genehmigender Antrag vorliegt oder ein Antrag genehmigt wurde.",[997,72534,72535],{},"Urlaubstage per Hand abzuzählen ist ziemlich retro, weshalb im digitalen Urlaubsantrag nur noch via Datumseingabe ein\nZeitraum angegeben werden soll. Über einen Feiertagskalender berechnet dann die Applikation statt der Antragssteller,\nwie viele Urlaubstage netto für diesen Zeitraum vom Urlaubskonto des Mitarbeiters abgezogen werden.",[439,72537,72538],{},"Was die programmatischen Werkzeuge betrifft, wurde uns freie Hand gelassen.",[439,72540,72541],{},"Wir entschieden uns, den Kern des Tools in Java zu schreiben und für die ‘äußere Hülle’ der Webapplikation das Framework\nSpring MVC zu benutzen.",[439,72543,72544],{},"Zunächst erschien uns der Arbeitsauftrag ziemlich trivial.",[439,72546,72547],{},"Bei der Konzeption des Tools allerdings stießen wir doch schnell an einige Stolpersteine.",[439,72549,72550],{},"Zu nennen wäre hier beispielsweise:",[439,72552,72553],{},"Die LDAP-Authentifizierung erschien auf den ersten Blick einfacher, bis wir feststellten, was für ein komplexes und\nweites Feld Spring Security eigentlich darstellt.",[439,72555,72556],{},"Bei der digitalen Validierung wechselten wir mehrmals unsere Meinung. Zur Auswahl verfügbar wäre erstens ein Photo der\neigenen Unterschrift, verknüpft mit der jeweiligen Person, oder zweitens die Signatur mittels eines persönlichen\nPGP-Schlüssels. (momentan favorisieren wir letztere Möglichkeit)",[439,72558,72559],{},"Auch die Interaktion mit einem Google-Kalender ist ein Feld, in das es sich noch einzulesen gilt.",[439,72561,72562],{},"Den inneren Kern unseres Tools, die Domain-Objekte, haben wir nun letztendlich in die Klassen Antrag, Person,\nUrlaubsanspruch und Urlaubskonto gegliedert.",[439,72564,72565],{},[1002,72566,72569],{"href":72567,"rel":72568},"https://media.synyx.de/uploads//2011/11/Domainobjekte1.png",[1006],[2205,72570],{"alt":469,"src":72567,"title":72571},"Domainobjekte",[439,72573,72574],{},"Als formelle Stolpersteine bei der Konzeption und Implementierung der Domain-Objekte und Services erwies sich die\nUnterscheidung in ‘normalen’ Urlaub und Resturlaub.",[439,72576,72577],{},"Resturlaub ist übrig gebliebener Jahresurlaub aus dem vorherigen Jahr, den ein Mitarbeiter mit ins neue Jahr nimmt.",[439,72579,72580],{},"Zwei wichtige Daten sind also der 1. Januar und der 1. April.",[439,72582,72583],{},"Am 1. Januar wird aus dem verbliebenem Urlaub des vorigen Jahres Resturlaub, während am 1. April dieser Resturlaub\nverfällt.",[439,72585,72586],{},"Wenn der Urlaubszeitraum also über einen dieser beiden Termine fällt (z.B. Urlaub vom 25.03. – 04.04.), erfordert\ndie Berechnung des Urlaubskontos des Mitarbeiters eine besondere Logik.",[439,72588,72589],{},"Die entsprechenden Services (AntragService, PersonService, UrlaubskontoService) sind großteils schon ausimplementiert,\njedoch ergeben sich aufgrund der Herausforderung der besonderen datumsabhängigen Logik immer wieder Veränderungen im\nCode.",[439,72591,72592],{},"Der MailService ist bisher gelöst mit dem JavaMailSender und MimeMessagePreparator (\norg.springframework.mail.javamail.*), benötigt sicherlich jedoch auch noch eine Überarbeitung der genauen Details.",[439,72594,72595],{},"Controller und die zugehörigen JSPs sind ebenfalls bereits großteils funktionsfähig, z.B. Darstellung von Mitarbeiter-\noder Antragslisten (zum Beispiel nach Status, Person, Datum), Formular zur Antragsstellung.",[439,72597,72598],{},"Das Design betreffend (z.B. dynamische Menü-Navigation oder Kalenderdarstellung ‘DatePicker’) werden wir aller\nWahrscheinlichkeit nach zu Bibliotheken von jQuery greifen.",[439,72600,72601],{},"Bevor wir allerdings überhaupt an das i-Tüpfelchen Design denken können, haben wir noch einen langen Weg vor uns….",{"title":469,"searchDepth":507,"depth":507,"links":72603},[],[13208],"2011-11-07T20:13:06","Für die erfolgreiche Genehmigung von Urlaub muss der synyx’sche Mitarbeiter sich bisher mit einem Stück Papier alias\\n‘schriftlich ausgefüllter Urlaubsantrag’ bewaffnen und sich an einen der drei Chefs anpirschen, um eine Unterschrift zu\\nergattern.","https://synyx.de/blog/elektronische-urlaubsverwaltung-made-by-youngsters/",{},"/blog/elektronische-urlaubsverwaltung-made-by-youngsters",{"title":72480,"description":72489},"blog/elektronische-urlaubsverwaltung-made-by-youngsters",[13219,66603,72613,389,41847,26638,72614],"spring-mvc","webapplikation","Für die erfolgreiche Genehmigung von Urlaub muss der synyx’sche Mitarbeiter sich bisher mit einem Stück Papier alias ‘schriftlich ausgefüllter Urlaubsantrag’ bewaffnen und sich an einen der drei Chefs anpirschen, um…","VLhJJgt1EJV3Q05LFRVda85eH6Z9QI41MQpIS-n4Qzo",{"id":72618,"title":72619,"author":72620,"body":72621,"category":72713,"date":72714,"description":72715,"extension":1034,"link":72716,"meta":72717,"navigation":916,"path":72718,"seo":72719,"slug":72720,"stem":72721,"tags":72722,"teaser":72724,"__hash__":72725},"blog/blog/schoener-schaukeln-mit-gradle.md","Schöner schaukeln mit Gradle?",[247],{"type":432,"value":72622,"toc":72711},[72623,72626,72629,72636,72658,72667,72685,72696,72699,72708],[435,72624,72619],{"id":72625},"schöner-schaukeln-mit-gradle",[439,72627,72628],{},"Die Konstruktion qualitativ hochwertiger Software setzt den Einsatz geeigneter Prozesse und Werkzeuge voraus. Von\nessentieller Bedeutung hinsichtlich der Qualität des Produkts ist der Build-Prozess: eine definierte Folge von\nSchritten die erforderlich sind, um aus einer Menge von Sourcecodedateien und sonstiger Ressourcen – sowie unter\nBerücksichtigung der Abhängigkeiten von Bibliotheken oder zwischen einzelnen Projektteilen – ein funktionierendes Ganzes\nzu bauen. Vereinfacht gesagt, denn dazu kommt noch die Ausführung einer Vielzahl von Unit- und Integrationstests (\nsowohl auf den Entwicklermaschinen als auch in Continuous Integration Umgebungen), das Erzeugen von Dokumentation,\nReleasemanagement, usw.",[439,72630,72631,72632,72635],{},"Schon bei Projekten mit überschaubarem Umfang müssen die Builds automatisiert werden (",[990,72633,72634],{},"build automation","). Dies ist der\neinzige Weg die richtige und fehlerfreie Abfolge der Schritte, welche den Build-Prozess ausmachen, zu gewährleisten.\nBuild-Performance spielt dabei eine nicht unwesentliche Rolle. Agile Prozesse (wie z.B. Scrum) mit ihren kurzen\nReleasezyklen werden erst durch die Automatisierung von Build-Prozessen möglich. Wir setzen derzeit für die meisten\nProjekte das Build-Tool Maven ein.",[439,72637,72638,72639,72641,72642,72647,72648,72651,72652,72657],{},"Passend zum Thema ",[990,72640,72634],{}," stellte Hans Dockter letzte Woche im Rahmen der regelmäßig stattfindenden Java User\nGroup (JUG KA) Veranstaltung das Build-Tool ",[1002,72643,72646],{"href":72644,"rel":72645},"http://www.gradle.org/",[1006],"Gradle"," vor: ",[990,72649,72650],{},"“Gradle wird den Build schon\nschaukeln”"," war der hoffnungsvoll klingende Titel des Vortrags. Hans Dockter ist Initiator des Gradle Projekts sowie CEO\nvon ",[1002,72653,72656],{"href":72654,"rel":72655},"http://www.gradleware.com/",[1006],"Gradleware",". In etwa zweieinhalb Stunden gab es Einblicke in die Konzepte und die\nFunktionsweise von Gradle, einige Anwendungsbeispiele, Antworten auf Fragen, Diskussionen sowie einen Ausblick auf\ngeplante Features in zukünftigen Releases (derzeit sind lediglich Milestone-Releases veröffentlicht; aktueller\nMilestone-Release ist Gradle 1.0-milestone-5).",[439,72659,72660,72666],{},[1002,72661,72664],{"href":72662,"rel":72663},"https://media.synyx.de/uploads//2011/11/gradle_logo.gif",[1006],[2205,72665],{"alt":469,"src":72662,"title":64516},"\nDaß der Gradle-Zug schon ordentlich Fahrt aufgenommen hat ist mittlerweile nicht mehr zu übersehen: Hibernate, Spring\nSecurity und Grails beispielsweise sind auf Gradle-basierte Builds umgestiegen. Stellt sich noch die Frage, ob es sich\nlohnt jetzt auf den Zug aufzuspringen und Gradle als Build-Tool einzusetzen. Das hängt natürlich vom jeweiligen Projekt\nund den Anforderungen an den Build ab; und bei existierenden Projekten sicher auch vom bisher eingesetzten Build-Tool.\nEinen funktionierenden Maven-basierten Build möchte man nicht ersetzen, oder doch?",[439,72668,72669,72670,72673,72674,72677,72678,72680,72681,72684],{},"Maven verfolgt einen ",[990,72671,72672],{},"deklarativen"," Ansatz: es wird definiert ",[990,72675,72676],{},"was"," in den einzelnen Phasen des Builds zu tun ist und\nnicht ",[990,72679,17374],{}," es zu tun ist. Vergleicht man diesen Ansatz mit dem ",[990,72682,72683],{},"imperativen"," Ansatz von Ant (dort wird für jeden\nSchritt beschrieben wie etwas zu tun ist), erkennt man schnell den Vorteil den Maven gegenüber Ant bietet: erhöhte\nLesbarkeit und Wartbarkeit. Die Anforderungen an einen Build-Prozess ändern sich aber mit der Zeit. Erweiterbarkeit\nspielt dementsprechend eine wichtige Rolle. Und Flexibilität. Was Maven-Builds betrifft: Flexibilität ist hier leider\nein Problem.",[439,72686,72687,72688,72691,72692,72695],{},"Auch Gradle ist ein deklaratives Build-System. Bei der Beschreibung des Build-Prozesses steht immer das Was – und\nnicht das Wie – im Vordergrund. Builds werden mit einer ",[1002,72689,71642],{"href":71640,"rel":72690},[1006],"-DSL (Domain Specific\nLanguage) deklarativ beschrieben. Die DSL ist aber beliebig erweiterbar, d.h. daß im imperativen Stil neue ",[990,72693,72694],{},"Tasks","\ndefiniert werden können, welche im Build-Script als deklarative Elemente verwendbar sind. Der imperative Aspekt\ngarantiert hohe Flexibilität. Durch die Möglichkeit die DSL zu erweitern ist eine Trennung von imperativer Schicht und\ndeklarativer Schicht gegeben.",[439,72697,72698],{},"Maven dagegen zwingt dem Build ein starres (Phasen-)Modell auf, was Maven-Builds unflexibel macht. Scheinbar einfache\nAnforderungen wie das Handling von Unit- und Integrationstests werden zu nervigen Angelegenheiten und sind oftmals nur\numständlich zu erreichen. Darunter leidet dann wieder die Les- und Wartbarkeit. Andere Aufgaben lassen sich nur durch\nAnpassungen an Maven-Plugins oder durch Einbindung von Ant-Tasks umsetzen (zwar wird das Ausführen von Groovy Scripts\nunterstützt, aber ein Ausbrechen aus dem starren Phasenmodell von Maven ist auch damit nicht möglich).",[439,72700,72701,72702,72707],{},"Gradle integriert mit Build-Systemen wie Ant und Maven. Das macht Sinn, da die Build-Landschaft heterogen ist. So\nkönnen sowohl Ivy- als auch Maven-Repositories genutzt werden. Die aktive und schnell wachsende Community wird die\nIntegration mit anderen Tools (wie z.B. Entwicklungsumgebungen) weiter vorantreiben. Geplant ist ausserdem ein Portal\nfür Open Source Gradle Plugins. Ideale Voraussetzungen also um den Start mit Gradle zu wagen. Der Einstieg ist nicht\nschwer: der ",[1002,72703,72706],{"href":72704,"rel":72705},"http://www.gradle.org/current/docs/userguide/userguide.html",[1006],"Gradle User Guide"," ist hier ein guter\nAusgangspunkt.",[439,72709,72710],{},"Gradle ist Open Source und steht unter der Apache Software License Version 2.0",{"title":469,"searchDepth":507,"depth":507,"links":72712},[],[1030],"2011-11-07T18:46:28","Die Konstruktion qualitativ hochwertiger Software setzt den Einsatz geeigneter Prozesse und Werkzeuge voraus. Von\\nessentieller Bedeutung hinsichtlich der Qualität des Produkts ist der Build-Prozess: eine definierte Folge von\\nSchritten die erforderlich sind, um aus einer Menge von Sourcecodedateien und sonstiger Ressourcen – sowie unter\\nBerücksichtigung der Abhängigkeiten von Bibliotheken oder zwischen einzelnen Projektteilen – ein funktionierendes Ganzes\\nzu bauen. Vereinfacht gesagt, denn dazu kommt noch die Ausführung einer Vielzahl von Unit- und Integrationstests (\\nsowohl auf den Entwicklermaschinen als auch in Continuous Integration Umgebungen), das Erzeugen von Dokumentation,\\nReleasemanagement, usw.","https://synyx.de/blog/schoener-schaukeln-mit-gradle/",{},"/blog/schoener-schaukeln-mit-gradle",{"title":72619,"description":72628},"schoener-schaukeln-mit-gradle","blog/schoener-schaukeln-mit-gradle",[50285,72723,64516],"build-automation","Die Konstruktion qualitativ hochwertiger Software setzt den Einsatz geeigneter Prozesse und Werkzeuge voraus. Von essentieller Bedeutung hinsichtlich der Qualität des Produkts ist der Build-Prozess: eine definierte Folge von Schritten die…","KoDKbKm5VdnuH24cWk8_udpHn2iofV1pqTioyZBrOvg",{"id":72727,"title":72728,"author":72729,"body":72730,"category":73049,"date":73050,"description":73051,"extension":1034,"link":73052,"meta":73053,"navigation":916,"path":73054,"seo":73055,"slug":72734,"stem":73056,"tags":73057,"teaser":73059,"__hash__":73060},"blog/blog/make-software-projects-fit-for-git.md","Make software-projects fit for git",[314],{"type":432,"value":72731,"toc":73047},[72732,72735,72738,72747,72752,72773,72776,72785,72790,72800,72809,72814,72817,72844,72849,72852,72881,72884,72912,72915,72918,72985,72988,72991,72996,72999,73002,73005,73035,73038,73041,73044],[435,72733,72728],{"id":72734},"make-software-projects-fit-for-git",[439,72736,72737],{},"More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…",[439,72739,72740],{},[1002,72741,72744],{"href":72742,"rel":72743},"https://media.synyx.de/uploads//2011/10/git-logo.png",[1006],[2205,72745],{"alt":469,"src":72742,"title":72746},"git-logo",[439,72748,72749,1402],{},[990,72750,72751],{},"Source code has to be accessible",[439,72753,72754,72755,72761,72762,72768,72769,72772],{},"We are used to use subversion, a central repository with the leading stage of developement, when using git – all\nrepositories are equal. To take the best of both worlds we host a git-repository, which is defined to be leading (only\nby convention). We like to have things under control, so we\nuse ",[1002,72756,72760],{"href":72757,"rel":72758,"title":72759},"http://eagain.net/gitweb/?p=gitosis.git",[1006],"Gitosis download","gitosis"," to serve these repositories, but we think\nabout using ",[1002,72763,72767],{"href":72764,"rel":72765,"title":72766},"https://github.com/sitaramc/gitolite/wiki",[1006],"gitolite on GitHub","gitolite"," because of better/easier\naccess-management. You can also host at ",[1002,72770,4042],{"href":58864,"rel":72771,"title":27413},[1006],", they do great work and it´s their daily\nbusiness.",[439,72774,72775],{},"What else do we need to develop an amazing piece of software in addition to good code and a working methodology? Which\ntools assist the development process and need capability to handle git-repositories?",[439,72777,72778],{},[1002,72779,72782],{"href":72780,"rel":72781},"https://media.synyx.de/uploads//2011/10/chiliproject-logo.png",[1006],[2205,72783],{"alt":469,"src":72780,"title":72784},"chiliproject-logo",[439,72786,72787,1402],{},[990,72788,72789],{},"Software should do what it is intended for",[439,72791,72792,72793,72799],{},"To reach this goal we collect production requirements and fragment the subsequent work into processable packets with a *\n*project-management tool** called redmine, more precisely ",[1002,72794,72798],{"href":72795,"rel":72796,"title":72797},"https://www.chiliproject.org/",[1006],"ChiliProject","**chiliproject\n**",", an rapidly evolving fork of redmine.",[439,72801,72802],{},[1002,72803,72806],{"href":72804,"rel":72805},"https://media.synyx.de/uploads//2011/10/jenkins_logo.png",[1006],[2205,72807],{"alt":469,"src":72804,"title":72808},"jenkins_logo",[439,72810,72811,1489],{},[990,72812,72813],{},"Software is something executable",[439,72815,72816],{},"plain source-code is for documentation purposes 😉",[439,72818,72819,72820,72824,72825,72828,72829,72833,72834,72840,72841,17548],{},"We have to build it. Most of our projects are written in Java and built\nwith ",[1002,72821,72823],{"href":47203,"rel":72822,"title":66749},[1006],"Apache Maven",". To build the written code automatically and pursue the process of\n",[448,72826,72827],{},"continuous integration"," we utilize a tool named hudson, more precisely ",[1002,72830,72832],{"href":60342,"rel":72831,"title":60344},[1006],"**Jenkins\n**",", an Oracle-independent fork (yeah we like using forks, especially when main\ndevelopers from the origin project are switching to the new fork, if you are interested in all of our reasons read\nthe ",[1002,72835,72839],{"href":72836,"rel":72837,"title":72838},"http://blog.synyx.de/2011/05/opensource-is-not-just-about-the-license/",[1006],"opensource is not just about the license","blogpost","\nwritten by ",[1002,72842,71516],{"href":71514,"rel":72843,"title":28},[1006],[439,72845,72846],{},[448,72847,72848],{},"So first mission is to make ChiliProject fit for git.",[439,72850,72851],{},"Luckily ChiliProject can handle git-repositories out of the box, but the repositories have to be cloned to localhost(\nthe machine running chiliproject). This can be achieved by giving read-rights to the user running chiliproject, in our\ncase this is generating a passwordless ssh-keypair, deploy the public part of it to gitosis and explicitly give rights\nto this public-key. To use the generated private-key every time we use git (in conjunction with ssh) we have to modify\nthe file ~/.ssh/config:",[464,72853,72855],{"className":466,"code":72854,"language":468,"meta":469,"style":469},"Host git.domain.tld\nUser git\nIdentityFile ~/.ssh/git_key.priv\n",[471,72856,72857,72865,72873],{"__ignoreMap":469},[474,72858,72859,72862],{"class":476,"line":477},[474,72860,72861],{"class":480},"Host",[474,72863,72864],{"class":484}," git.domain.tld\n",[474,72866,72867,72870],{"class":476,"line":507},[474,72868,72869],{"class":480},"User",[474,72871,72872],{"class":484}," git\n",[474,72874,72875,72878],{"class":476,"line":547},[474,72876,72877],{"class":480},"IdentityFile",[474,72879,72880],{"class":484}," ~/.ssh/git_key.priv\n",[439,72882,72883],{},"Now we need to clone the repository manually by login to the machine running Chili and do:",[464,72885,72887],{"className":466,"code":72886,"language":468,"meta":469,"style":469},"mkdir /path/to/git/repositories/\ncd /path/to/git/repositories/\ngit clone git@gitosis.domain.tld:gitrepo.git\n",[471,72888,72889,72897,72903],{"__ignoreMap":469},[474,72890,72891,72894],{"class":476,"line":477},[474,72892,72893],{"class":480},"mkdir",[474,72895,72896],{"class":484}," /path/to/git/repositories/\n",[474,72898,72899,72901],{"class":476,"line":507},[474,72900,14718],{"class":510},[474,72902,72896],{"class":484},[474,72904,72905,72907,72909],{"class":476,"line":547},[474,72906,19320],{"class":480},[474,72908,57210],{"class":484},[474,72910,72911],{"class":484}," git@gitosis.domain.tld:gitrepo.git\n",[439,72913,72914],{},"for sure git has to be installed on the server running Chili. And the repository already exists…",[439,72916,72917],{},"but how do we keep this cloned repository up to date? We solved this by installing a cronjob running as the user,\nrunning Chili, every 5 minutes:",[464,72919,72921],{"className":466,"code":72920,"language":468,"meta":469,"style":469},"*/5 * * * * for i in /path/to/git/repositories/*/; do cd $i && git fetch; git reset refs/remotes/origin/master; done >>/dev/null 2>&1\n",[471,72922,72923],{"__ignoreMap":469},[474,72924,72925,72927,72930,72932,72934,72936,72938,72940,72942,72944,72947,72949,72952,72954,72957,72959,72962,72964,72966,72969,72972,72974,72976,72979,72982],{"class":476,"line":477},[474,72926,11737],{"class":810},[474,72928,72929],{"class":503},"/5 ",[474,72931,11737],{"class":810},[474,72933,1474],{"class":810},[474,72935,1474],{"class":810},[474,72937,1474],{"class":810},[474,72939,565],{"class":810},[474,72941,2677],{"class":503},[474,72943,2680],{"class":810},[474,72945,72946],{"class":484}," /path/to/git/repositories/*/",[474,72948,55136],{"class":503},[474,72950,72951],{"class":810},"do",[474,72953,57312],{"class":510},[474,72955,72956],{"class":503}," $i && ",[474,72958,19320],{"class":480},[474,72960,72961],{"class":484}," fetch",[474,72963,55136],{"class":503},[474,72965,19320],{"class":480},[474,72967,72968],{"class":484}," reset",[474,72970,72971],{"class":484}," refs/remotes/origin/master",[474,72973,55136],{"class":503},[474,72975,29454],{"class":810},[474,72977,72978],{"class":810}," >>",[474,72980,72981],{"class":503},"/dev/null ",[474,72983,72984],{"class":810},"2>&1\n",[439,72986,72987],{},"jap, you can log into a file instead of /dev/null, but we trust… 🙂",[439,72989,72990],{},"that´s it you can now add the local repository to your project in ChiliProject, but give full path including the\n“.git”-folder, it is little fussy in this point.",[439,72992,72993],{},[448,72994,72995],{},"Get Jenkins to work with git",[439,72997,72998],{},"First, we have to do the same things done for ChiliProject, install the git-binary on the system running Jenkins,\ngenerate ssh-keypair, give read-rights to the user(possible stumbling block: we are running jenkins in a\njava-servlet-container so it´s the user running this container!)",[439,73000,73001],{},"modify ~/.ssh/config. Now we should be able to manually clone the targeted repositories, but that´s not what we want (\nremember automatically and continous integration?)",[439,73003,73004],{},"In order to be able to tag the built release version, the user running jenkins needs to give an author to the\ngit-repository, so modify/create ~/.gitconfig of this user:",[464,73006,73008],{"className":466,"code":73007,"language":468,"meta":469,"style":469},"[user]\n name = \"Jenkins\"\n email = \"jenkins@jenkins.domain.tld\"\n",[471,73009,73010,73015,73025],{"__ignoreMap":469},[474,73011,73012],{"class":476,"line":477},[474,73013,73014],{"class":503},"[user]\n",[474,73016,73017,73020,73022],{"class":476,"line":507},[474,73018,73019],{"class":480}," name",[474,73021,1661],{"class":484},[474,73023,73024],{"class":484}," \"Jenkins\"\n",[474,73026,73027,73030,73032],{"class":476,"line":547},[474,73028,73029],{"class":480}," email",[474,73031,1661],{"class":484},[474,73033,73034],{"class":484}," \"jenkins@jenkins.domain.tld\"\n",[439,73036,73037],{},"Jenkins is not able to handle git by default, we have to install a plugin: login -> Jenkins -> manage Jenkins ->\nmanage plugins -> available -> Git plugin (that´s easy to remember)",[439,73039,73040],{},"After restarting Jenkins you´ll find, under “projectX”/configure -> Source Code Management, the new section git where\nyou can insert the url of your repository -> save",[439,73042,73043],{},"Finally you can build the project(small prayer could help 😉 ) and enjoy the built software and Jenkins´ expandable\nworkflow…",[1024,73045,73046],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":469,"searchDepth":507,"depth":507,"links":73048},[],[9045,1030,1412],"2011-10-25T12:34:45","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…","https://synyx.de/blog/make-software-projects-fit-for-git/",{},"/blog/make-software-projects-fit-for-git",{"title":72728,"description":72737},"blog/make-software-projects-fit-for-git",[73058,711,22988,26636,47537,14723],"apache","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to make a complete software-project fit for git, by not…","sICL3OIOU6memjPOaPYhGVLZV5fDKo4gCXX82-SqzDI",{"id":73062,"title":73063,"author":73064,"body":73065,"category":73679,"date":73680,"description":73681,"extension":1034,"link":73682,"meta":73683,"navigation":916,"path":73684,"seo":73685,"slug":73069,"stem":73686,"tags":73687,"teaser":73688,"__hash__":73689},"blog/blog/sending-jms-from-oracledb-to-external-activemq-broker.md","Sending JMS from OracleDB to external ActiveMQ Broker",[241],{"type":432,"value":73066,"toc":73673},[73067,73070,73073,73076,73080,73087,73090,73101,73105,73108,73111,73128,73131,73151,73160,73164,73167,73279,73282,73375,73378,73486,73489,73541,73545,73548,73556,73559,73562,73595,73624,73634,73653,73656,73670],[435,73068,73063],{"id":73069},"sending-jms-from-oracledb-to-external-activemq-broker",[439,73071,73072],{},"After taking over a legacy application of which a huge part of the business logic is formed by triggers and procedures\ninside an Oracle DB, we faced the task of a step-by-step migration of that logic to Java code. Due to the complete\nlack of a defined and sophisticated service layer and having other systems connected using several autonomous interfaces\nwhich directly access the underlying database this migration is quite complicated.",[439,73074,73075],{},"An idea popped up. As a intermediate step on the way to move the business logic back to the Java side, why not port the\ntrigger PL/SQL to Java (behind a new created service layer) and let the trigger fire JMS messages to trigger the\nexecution of that Java code?",[3938,73077,73079],{"id":73078},"prerequisites","Prerequisites",[439,73081,73082,73083,1402],{},"Since Oracle 9i it is possible to use Java code in the Oracle DB. However the provided (not replaceable) JDK lacks\nbehind the current standards. On top of that there are some specifics to be considered. Details on that topic can be\nfound ",[1002,73084,22916],{"href":73085,"rel":73086},"http://download.oracle.com/docs/cd/B28359_01/java.111/b31225/chtwo.htm",[1006],[439,73088,73089],{},"Our setup consists of the following components:",[994,73091,73092,73095,73098],{},[997,73093,73094],{},"Oracle 11gR2 (providing JDK 1.5)",[997,73096,73097],{},"Active MQ 5.4.2 (the last version that can be build using JDK 1.5)",[997,73099,73100],{},"Spring 3.0.5",[3938,73102,73104],{"id":73103},"preparation-of-user-privileges","Preparation of user privileges",[439,73106,73107],{},"The following privileges must be granted to the database user (in our demonstration: TEST) from which the sending of JMS\nmessages shall occur.",[439,73109,73110],{},"Direct access to the class loader and system properties:",[464,73112,73116],{"className":73113,"code":73114,"language":73115,"meta":469,"style":469},"language-sql shiki shiki-themes github-light github-dark","call dbms_java.grant_permission( 'TEST', 'SYS:java.lang.RuntimePermission', 'getClassLoader', '' );\ncall dbms_java.grant_permission( 'TEST', 'SYS:java.util.PropertyPermission', '*', 'read,write' );\n","sql",[471,73117,73118,73123],{"__ignoreMap":469},[474,73119,73120],{"class":476,"line":477},[474,73121,73122],{},"call dbms_java.grant_permission( 'TEST', 'SYS:java.lang.RuntimePermission', 'getClassLoader', '' );\n",[474,73124,73125],{"class":476,"line":507},[474,73126,73127],{},"call dbms_java.grant_permission( 'TEST', 'SYS:java.util.PropertyPermission', '*', 'read,write' );\n",[439,73129,73130],{},"Creating a socket connection and full access to it:",[464,73132,73134],{"className":73113,"code":73133,"language":73115,"meta":469,"style":469},"call dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'listen,resolve');\ncall dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'accept,resolve');\ncall dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'connect,resolve');\n",[471,73135,73136,73141,73146],{"__ignoreMap":469},[474,73137,73138],{"class":476,"line":477},[474,73139,73140],{},"call dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'listen,resolve');\n",[474,73142,73143],{"class":476,"line":507},[474,73144,73145],{},"call dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'accept,resolve');\n",[474,73147,73148],{"class":476,"line":547},[474,73149,73150],{},"call dbms_java.grant_permission( 'TEST', 'SYS:java.net.SocketPermission', '*', 'connect,resolve');\n",[439,73152,73153,73155,73156,73159],{},[448,73154,20916],{}," The placeholder ",[471,73157,73158],{},"'*'"," allows the creation of socket connections to any target host and port. In production\nsystems that shall be reduced to the IP range needed.",[3938,73161,73163],{"id":73162},"a-small-prototype","A small prototype",[439,73165,73166],{},"For demonstration purposes we use Spring to fire up an ActiveMQ broker, initialize a test queue and add a simple\nlistener. Here’s the corresponding part of the application context configuration.",[464,73168,73170],{"className":6253,"code":73169,"language":6255,"meta":469,"style":469},"\n\u003C!-- create message broker - instead of using a somewhere centralized activemq server -->\n\u003Cbean id=\"broker\" class=\"org.apache.activemq.xbean.BrokerFactoryBean\">\n \u003Cproperty name=\"config\" value=\"classpath:META-INF/activemq.xml\" />\n \u003Cproperty name=\"start\" value=\"true\" />\n\u003C/bean>\n\u003C!-- a simple test queue - the queue name is specified by the bean id -->\n\u003Cbean id=\"testQueue\" class=\"org.apache.activemq.command.ActiveMQQueue\" />\n\u003C!-- JMS connection factory (wrapped into a pooling connection factory) -->\n\u003Cbean id=\"jmsFactory\" class=\"org.apache.activemq.pool.PooledConnectionFactory\" destroy-method=\"stop\">\n \u003Cproperty name=\"connectionFactory\">\n \u003Cbean class=\"org.apache.activemq.ActiveMQConnectionFactory\">\n \u003Cproperty name=\"brokerURL\" value=\"tcp://localhost:61616\" />\n \u003C/bean>\n \u003C/property>\n\u003C/bean>\n\u003C!-- simple message listener just logging received message to stdout -->\n\u003Cbean id=\"simpleListener\" class=\"com.synyx.prototype.jms.SimpleMessageListener\" />\n\u003C!-- listener container delegating to listener instances and wiring them to their destinations -->\n\u003Cjms:listener-container concurrency=\"10\" connection-factory=\"jmsFactory\">\n \u003Cjms:listener id=\"queueListener\" destination=\"testQueue\" ref=\"simpleListener\" />\n\u003C/jms:listener-container>\n\n",[471,73171,73172,73176,73181,73186,73191,73196,73200,73205,73210,73215,73220,73225,73230,73235,73240,73245,73249,73254,73259,73264,73269,73274],{"__ignoreMap":469},[474,73173,73174],{"class":476,"line":477},[474,73175,917],{"emptyLinePlaceholder":916},[474,73177,73178],{"class":476,"line":507},[474,73179,73180],{},"\u003C!-- create message broker - instead of using a somewhere centralized activemq server -->\n",[474,73182,73183],{"class":476,"line":547},[474,73184,73185],{},"\u003Cbean id=\"broker\" class=\"org.apache.activemq.xbean.BrokerFactoryBean\">\n",[474,73187,73188],{"class":476,"line":584},[474,73189,73190],{}," \u003Cproperty name=\"config\" value=\"classpath:META-INF/activemq.xml\" />\n",[474,73192,73193],{"class":476,"line":607},[474,73194,73195],{}," \u003Cproperty name=\"start\" value=\"true\" />\n",[474,73197,73198],{"class":476,"line":642},[474,73199,69979],{},[474,73201,73202],{"class":476,"line":663},[474,73203,73204],{},"\u003C!-- a simple test queue - the queue name is specified by the bean id -->\n",[474,73206,73207],{"class":476,"line":694},[474,73208,73209],{},"\u003Cbean id=\"testQueue\" class=\"org.apache.activemq.command.ActiveMQQueue\" />\n",[474,73211,73212],{"class":476,"line":700},[474,73213,73214],{},"\u003C!-- JMS connection factory (wrapped into a pooling connection factory) -->\n",[474,73216,73217],{"class":476,"line":913},[474,73218,73219],{},"\u003Cbean id=\"jmsFactory\" class=\"org.apache.activemq.pool.PooledConnectionFactory\" destroy-method=\"stop\">\n",[474,73221,73222],{"class":476,"line":920},[474,73223,73224],{}," \u003Cproperty name=\"connectionFactory\">\n",[474,73226,73227],{"class":476,"line":926},[474,73228,73229],{}," \u003Cbean class=\"org.apache.activemq.ActiveMQConnectionFactory\">\n",[474,73231,73232],{"class":476,"line":932},[474,73233,73234],{}," \u003Cproperty name=\"brokerURL\" value=\"tcp://localhost:61616\" />\n",[474,73236,73237],{"class":476,"line":938},[474,73238,73239],{}," \u003C/bean>\n",[474,73241,73242],{"class":476,"line":944},[474,73243,73244],{}," \u003C/property>\n",[474,73246,73247],{"class":476,"line":950},[474,73248,69979],{},[474,73250,73251],{"class":476,"line":956},[474,73252,73253],{},"\u003C!-- simple message listener just logging received message to stdout -->\n",[474,73255,73256],{"class":476,"line":962},[474,73257,73258],{},"\u003Cbean id=\"simpleListener\" class=\"com.synyx.prototype.jms.SimpleMessageListener\" />\n",[474,73260,73261],{"class":476,"line":4876},[474,73262,73263],{},"\u003C!-- listener container delegating to listener instances and wiring them to their destinations -->\n",[474,73265,73266],{"class":476,"line":4888},[474,73267,73268],{},"\u003Cjms:listener-container concurrency=\"10\" connection-factory=\"jmsFactory\">\n",[474,73270,73271],{"class":476,"line":4900},[474,73272,73273],{}," \u003Cjms:listener id=\"queueListener\" destination=\"testQueue\" ref=\"simpleListener\" />\n",[474,73275,73276],{"class":476,"line":4913},[474,73277,73278],{},"\u003C/jms:listener-container>\n",[439,73280,73281],{},"On the side of the message producer a QueueConnectionFactory implementation provides the connectivity to the ActiveMQ\nbroker. For our prototype it lacks any authentication mechanisms.",[464,73283,73285],{"className":709,"code":73284,"language":711,"meta":469,"style":469},"\npublic class ActiveMQQueueConnectionFactory implements QueueConnectionFactory {\n private ConnectionFactory connectionFactory = null;\n public ActiveMQQueueConnectionFactory(String brokerUrl) {\n this.connectionFactory = new ActiveMQConnectionFactory(brokerUrl);\n }\n public QueueConnection createQueueConnection() throws JMSException {\n return (QueueConnection) createConnection();\n }\n public QueueConnection createQueueConnection(String username, String password) throws JMSException {\n return createQueueConnection();\n }\n public Connection createConnection() throws JMSException {\n return this.connectionFactory.createConnection();\n }\n public Connection createConnection(String username, String password) throws JMSException {\n return createConnection();\n }\n}\n\n",[471,73286,73287,73291,73296,73301,73306,73311,73315,73320,73325,73329,73334,73339,73343,73348,73353,73357,73362,73367,73371],{"__ignoreMap":469},[474,73288,73289],{"class":476,"line":477},[474,73290,917],{"emptyLinePlaceholder":916},[474,73292,73293],{"class":476,"line":507},[474,73294,73295],{},"public class ActiveMQQueueConnectionFactory implements QueueConnectionFactory {\n",[474,73297,73298],{"class":476,"line":547},[474,73299,73300],{}," private ConnectionFactory connectionFactory = null;\n",[474,73302,73303],{"class":476,"line":584},[474,73304,73305],{}," public ActiveMQQueueConnectionFactory(String brokerUrl) {\n",[474,73307,73308],{"class":476,"line":607},[474,73309,73310],{}," this.connectionFactory = new ActiveMQConnectionFactory(brokerUrl);\n",[474,73312,73313],{"class":476,"line":642},[474,73314,1276],{},[474,73316,73317],{"class":476,"line":663},[474,73318,73319],{}," public QueueConnection createQueueConnection() throws JMSException {\n",[474,73321,73322],{"class":476,"line":694},[474,73323,73324],{}," return (QueueConnection) createConnection();\n",[474,73326,73327],{"class":476,"line":700},[474,73328,1276],{},[474,73330,73331],{"class":476,"line":913},[474,73332,73333],{}," public QueueConnection createQueueConnection(String username, String password) throws JMSException {\n",[474,73335,73336],{"class":476,"line":920},[474,73337,73338],{}," return createQueueConnection();\n",[474,73340,73341],{"class":476,"line":926},[474,73342,1276],{},[474,73344,73345],{"class":476,"line":932},[474,73346,73347],{}," public Connection createConnection() throws JMSException {\n",[474,73349,73350],{"class":476,"line":938},[474,73351,73352],{}," return this.connectionFactory.createConnection();\n",[474,73354,73355],{"class":476,"line":944},[474,73356,1276],{},[474,73358,73359],{"class":476,"line":950},[474,73360,73361],{}," public Connection createConnection(String username, String password) throws JMSException {\n",[474,73363,73364],{"class":476,"line":956},[474,73365,73366],{}," return createConnection();\n",[474,73368,73369],{"class":476,"line":962},[474,73370,1276],{},[474,73372,73373],{"class":476,"line":4876},[474,73374,703],{},[439,73376,73377],{},"The class QueueMessageSender implements a simple text message producer. Note that the JMSException isn’t caught but\npropageted to the caller. Exceptions are finally handled by the global Oracle VM Exception Handler. That way, in case of\nan exception, the exceptions message ends up in the ORA-XXXX error designation and the full stack trace is stated in\nthe user’s session log.",[464,73379,73381],{"className":709,"code":73380,"language":711,"meta":469,"style":469},"\npublic class QueueMessageSender {\n private QueueConnectionFactory connectionFactory = null;\n public QueueMessageSender(QueueConnectionFactory connectionFactory) {\n this.connectionFactory = connectionFactory;\n }\n public void sendMessage(String destination, String message) throws JMSException {\n QueueConnection connection = null;\n try {\n connection = this.connectionFactory.createQueueConnection();\n QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);\n Queue queue = session.createQueue(destination);\n QueueSender sender = session.createSender(queue);\n TextMessage textMessage = session.createTextMessage(message);\n sender.send(textMessage);\n } finally {\n if (null != connection) {\n connection.close();\n }\n }\n }\n}\n\n",[471,73382,73383,73387,73392,73397,73402,73407,73411,73416,73421,73425,73430,73435,73440,73445,73450,73455,73460,73465,73470,73474,73478,73482],{"__ignoreMap":469},[474,73384,73385],{"class":476,"line":477},[474,73386,917],{"emptyLinePlaceholder":916},[474,73388,73389],{"class":476,"line":507},[474,73390,73391],{},"public class QueueMessageSender {\n",[474,73393,73394],{"class":476,"line":547},[474,73395,73396],{}," private QueueConnectionFactory connectionFactory = null;\n",[474,73398,73399],{"class":476,"line":584},[474,73400,73401],{}," public QueueMessageSender(QueueConnectionFactory connectionFactory) {\n",[474,73403,73404],{"class":476,"line":607},[474,73405,73406],{}," this.connectionFactory = connectionFactory;\n",[474,73408,73409],{"class":476,"line":642},[474,73410,1276],{},[474,73412,73413],{"class":476,"line":663},[474,73414,73415],{}," public void sendMessage(String destination, String message) throws JMSException {\n",[474,73417,73418],{"class":476,"line":694},[474,73419,73420],{}," QueueConnection connection = null;\n",[474,73422,73423],{"class":476,"line":700},[474,73424,61985],{},[474,73426,73427],{"class":476,"line":913},[474,73428,73429],{}," connection = this.connectionFactory.createQueueConnection();\n",[474,73431,73432],{"class":476,"line":920},[474,73433,73434],{}," QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);\n",[474,73436,73437],{"class":476,"line":926},[474,73438,73439],{}," Queue queue = session.createQueue(destination);\n",[474,73441,73442],{"class":476,"line":932},[474,73443,73444],{}," QueueSender sender = session.createSender(queue);\n",[474,73446,73447],{"class":476,"line":938},[474,73448,73449],{}," TextMessage textMessage = session.createTextMessage(message);\n",[474,73451,73452],{"class":476,"line":944},[474,73453,73454],{}," sender.send(textMessage);\n",[474,73456,73457],{"class":476,"line":950},[474,73458,73459],{}," } finally {\n",[474,73461,73462],{"class":476,"line":956},[474,73463,73464],{}," if (null != connection) {\n",[474,73466,73467],{"class":476,"line":962},[474,73468,73469],{}," connection.close();\n",[474,73471,73472],{"class":476,"line":4876},[474,73473,42564],{},[474,73475,73476],{"class":476,"line":4888},[474,73477,5704],{},[474,73479,73480],{"class":476,"line":4900},[474,73481,1276],{},[474,73483,73484],{"class":476,"line":4913},[474,73485,703],{},[439,73487,73488],{},"The following class provides the entry point to be called from PL/SQL (i.e. sending a message triggering the service\ncall replacing the legacy trigger code). For demonstration purposes it is kept simple like the rest of the code\nexamples. Note that the method acting as entry point must be static for beeing callable from PL/SQL.",[464,73490,73492],{"className":709,"code":73491,"language":711,"meta":469,"style":469},"\npublic class JMSFromOracleTest {\n private static final String BROKER_URL = \"tcp://192.168.x.x:61616\";\n private static final String QUEUE_NAME = \"testQueue\";\n public static void sendMessage(String message) throws JMSException {\n QueueConnectionFactory connectionFactory = new ActiveMQQueueConnectionFactory(BROKER_URL);\n QueueMessageSender sender = new QueueMessageSender(connectionFactory);\n sender.sendMessage(QUEUE_NAME, message);\n }\n}\n\n",[471,73493,73494,73498,73503,73508,73513,73518,73523,73528,73533,73537],{"__ignoreMap":469},[474,73495,73496],{"class":476,"line":477},[474,73497,917],{"emptyLinePlaceholder":916},[474,73499,73500],{"class":476,"line":507},[474,73501,73502],{},"public class JMSFromOracleTest {\n",[474,73504,73505],{"class":476,"line":547},[474,73506,73507],{}," private static final String BROKER_URL = \"tcp://192.168.x.x:61616\";\n",[474,73509,73510],{"class":476,"line":584},[474,73511,73512],{}," private static final String QUEUE_NAME = \"testQueue\";\n",[474,73514,73515],{"class":476,"line":607},[474,73516,73517],{}," public static void sendMessage(String message) throws JMSException {\n",[474,73519,73520],{"class":476,"line":642},[474,73521,73522],{}," QueueConnectionFactory connectionFactory = new ActiveMQQueueConnectionFactory(BROKER_URL);\n",[474,73524,73525],{"class":476,"line":663},[474,73526,73527],{}," QueueMessageSender sender = new QueueMessageSender(connectionFactory);\n",[474,73529,73530],{"class":476,"line":694},[474,73531,73532],{}," sender.sendMessage(QUEUE_NAME, message);\n",[474,73534,73535],{"class":476,"line":700},[474,73536,1276],{},[474,73538,73539],{"class":476,"line":913},[474,73540,703],{},[3938,73542,73544],{"id":73543},"packaging-and-deployment-to-the-oracle-db","Packaging and Deployment to the Oracle DB",[439,73546,73547],{},"To simplify the deployment we created a small Maven project covering the producer code and used the Maven Assembly\nplugin for packaging the producer classes and all dependencies into a single JAR file. Again, to keep things simple we\nadded the activemq-all distribution as the only dependency.",[439,73549,73550,73552,73553,73555],{},[448,73551,20916],{}," All classes (the producer classes and ",[990,73554,38086],{}," dependencies) need to be compiled using/for JDK 1.5 (class\nversion \u003C= 49.0).",[439,73557,73558],{},"Oracle keeps all Java class files and resources in the database. As the name implies, the command line tool “loadjava”\nis used to load Java resources into the db. This command must be issued on the Oracle DB server itself. For that, the\nenvironment variable ORACLE_HOME must be correctly set. In reverse, the tool “dropjava” provides an easy way to unload\nJava resources from the DB.",[439,73560,73561],{},"Issuing the following command loads all resources contained in our JAR file into the Oracle DB.",[464,73563,73565],{"className":466,"code":73564,"language":468,"meta":469,"style":469},"\nloadjava -v -r -u test/12345 -resolver \"((* TEST) (* PUBLIC) (* -))\" JMSSender-1.0-SNAPSHOT-jar-with-dependencies.jar\n\n",[471,73566,73567,73571],{"__ignoreMap":469},[474,73568,73569],{"class":476,"line":477},[474,73570,917],{"emptyLinePlaceholder":916},[474,73572,73573,73576,73578,73580,73583,73586,73589,73592],{"class":476,"line":507},[474,73574,73575],{"class":480},"loadjava",[474,73577,2645],{"class":510},[474,73579,2449],{"class":510},[474,73581,73582],{"class":510}," -u",[474,73584,73585],{"class":484}," test/12345",[474,73587,73588],{"class":510}," -resolver",[474,73590,73591],{"class":484}," \"((* TEST) (* PUBLIC) (* -))\"",[474,73593,73594],{"class":484}," JMSSender-1.0-SNAPSHOT-jar-with-dependencies.jar\n",[439,73596,73597,73598,73601,73602,73605,73606,73608,73609,73612,73613,18175,73616,73619,73620,73623],{},"The switch ",[471,73599,73600],{},"-r"," enables the resolving of all classes references by the loaded classes. If any reference made by a class\ncould not be resolved, that class is marked ",[471,73603,73604],{},"INVALID",". Classes referencing invalid classes are also marked ",[471,73607,73604],{},".\nNote that we declared our own resolver using the ",[471,73610,73611],{},"-resolver"," parameter. Using the parameter as stated above all classes\nin any package declared in the SCHEMA ",[471,73614,73615],{},"TEST",[471,73617,73618],{},"PUBLIC"," are allowed to reference unresolved dependencies. That way\nfeatures and connectors of ActiveMQ not used (and therefore lack the required dependencies) do not invalidate the core\nclasses. The parameter ",[471,73621,73622],{},"-u"," is followed by the user credentials (username/password) of the user to whose default schema\nthe resources are deployed.",[439,73625,73626,73627,73630,73631,1402],{},"After all resources contained in the JAR file are deployed (this may take a while – but keeps you entertained because of\nthe ",[471,73628,73629],{},"-v"," parameter), we need to create a stored procedure usable from PL/SQL that directs the call to the entry point\nmethod of our Java implementation. For general information on calling Java Methods from PL/SQL (i.e. referencing\nparameters and return values) see ",[1002,73632,22916],{"href":73085,"rel":73633},[1006],[464,73635,73637],{"className":73113,"code":73636,"language":73115,"meta":469,"style":469},"\nCREATE OR REPLACE PROCEDURE sendJmsMessage(message IN VARCHAR2)\nAS LANGUAGE JAVA NAME 'com.synyx.prototype.jms.JMSFromOracleTest.sendMessage(java.lang.String)';\n\n",[471,73638,73639,73643,73648],{"__ignoreMap":469},[474,73640,73641],{"class":476,"line":477},[474,73642,917],{"emptyLinePlaceholder":916},[474,73644,73645],{"class":476,"line":507},[474,73646,73647],{},"CREATE OR REPLACE PROCEDURE sendJmsMessage(message IN VARCHAR2)\n",[474,73649,73650],{"class":476,"line":547},[474,73651,73652],{},"AS LANGUAGE JAVA NAME 'com.synyx.prototype.jms.JMSFromOracleTest.sendMessage(java.lang.String)';\n",[439,73654,73655],{},"Finally, we are done. Calling the procedure from PL/SQL will invoke our Java method and a text message containing the\ngiven text will be posted.",[464,73657,73659],{"className":73113,"code":73658,"language":73115,"meta":469,"style":469},"\ncall sendjmsmessage('hello from oracle');\n\n",[471,73660,73661,73665],{"__ignoreMap":469},[474,73662,73663],{"class":476,"line":477},[474,73664,917],{"emptyLinePlaceholder":916},[474,73666,73667],{"class":476,"line":507},[474,73668,73669],{},"call sendjmsmessage('hello from oracle');\n",[1024,73671,73672],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":469,"searchDepth":507,"depth":507,"links":73674},[73675,73676,73677,73678],{"id":73078,"depth":507,"text":73079},{"id":73103,"depth":507,"text":73104},{"id":73162,"depth":507,"text":73163},{"id":73543,"depth":507,"text":73544},[1030],"2011-10-18T17:37:19","After taking over a legacy application of which a huge part of the business logic is formed by triggers and procedures\\ninside an Oracle DB, we faced the task of a step-by-step migration of that logic to Java code. Due to the complete\\nlack of a defined and sophisticated service layer and having other systems connected using several autonomous interfaces\\nwhich directly access the underlying database this migration is quite complicated.","https://synyx.de/blog/sending-jms-from-oracledb-to-external-activemq-broker/",{},"/blog/sending-jms-from-oracledb-to-external-activemq-broker",{"title":73063,"description":73072},"blog/sending-jms-from-oracledb-to-external-activemq-broker",[],"After taking over a legacy application of which a huge part of the business logic is formed by triggers and procedures inside an Oracle DB, we faced the task of…","8b5tf03Ms7jGKzpnvc9ei6GFG6iPwFbrgDPlVaOGXeQ",{"id":73691,"title":73692,"author":73693,"body":73694,"category":74023,"date":74024,"description":74025,"extension":1034,"link":74026,"meta":74027,"navigation":916,"path":74028,"seo":74029,"slug":74031,"stem":74032,"tags":74033,"teaser":74034,"__hash__":74035},"blog/blog/testing-webapp-startup-on-jenkins-with-maven-tomcat-and-web-driver.md","Testing webapp startup on Jenkins using Maven, Tomcat and Web Driver",[148],{"type":432,"value":73695,"toc":74021},[73696,73699,73717,73720,73726,73729,73732,73746,73778,73781,73921,73924,74016,74019],[435,73697,73692],{"id":73698},"testing-webapp-startup-on-jenkins-using-maven-tomcat-and-web-driver",[439,73700,73701,73702,73707,73708,73711,73712,1402],{},"Modern web applications often consist of quite some configuration files that should at least be tested for validity.\nThink\nof ",[1002,73703,73706],{"href":73704,"rel":73705},"http://static.springsource.org/spring/docs/3.0.6.RELEASE/spring-framework-reference/html/mvc.html",[1006],"Spring controller configurations",",\nweb application descriptors and the like that can’t be tested easily using Unit Tests. Fortunately it’s quite easy to\nstart a tomcat instance on your CI system (",[1002,73709,60344],{"href":60342,"rel":73710},[1006]," or Hudson) using\nthe ",[1002,73713,73716],{"href":73714,"rel":73715},"https://web.archive.org/web/20150531090420/http://mojo.codehaus.org/tomcat-maven-plugin/",[1006],"Tomcat Maven Plugin",[439,73718,73719],{},"As you probably don’t want to start and stop the server on every test run it’s a good idea to bind it to the\nintegration-test phase, probably even to a separate profile that is only triggered on the continuos integration\nmachine. This is what the plugin configuration might look like:",[464,73721,73724],{"className":73722,"code":73723,"language":14509},[14508],"\n\u003Cplugin>\n \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n \u003Cversion>1.1\u003C/version>\n \u003Cconfiguration>\n \u003Cfork>true\u003C/fork>\n \u003Cport>8081\u003C/port>\n \u003C/configuration>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>start-tc\u003C/id>\n \u003Cphase>pre-integration-test\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>run-war-only\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003Cexecution>\n \u003Cid>stop-tc\u003C/id>\n \u003Cphase>post-integration-test\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>shutdown\u003C/goal>\n \u003C/goals>\n \u003C/execution>\n \u003C/executions>\n\u003C/plugin>\n",[471,73725,73723],{"__ignoreMap":469},[439,73727,73728],{},"We bind the startup to the pre-integration-test phase, which is triggered just before running the integration tests.\nIn post-integration-test we shutdown the server. When running on a CI webapp it’s important to choose a different port\nthan the one that is used for the CI Server as startup will fail when the port is already in use. We are forking to\ncontinue with the Maven execution, if you skip this parameter Maven will just stop after it started Tomcat.",[439,73730,73731],{},"If you run the plugin with a failing web configuration (e.g. when you Spring web context breaks) the failures will be\nlogged to the console output. Unfortunately this doesn’t break the build and you won’t notice that there might be a\nproblem.",[439,73733,73734,73735,73740,73741,73745],{},"One way to have the build fail is to add a Test Case that issues a HTTP call to you web application. A good tool for\ndoing this is the ",[1002,73736,73739],{"href":73737,"rel":73738},"http://seleniumhq.org/projects/webdriver/",[1006],"Web Driver project"," which merged\nwith ",[1002,73742,73744],{"href":60771,"rel":73743},[1006],"Selenium"," in Selenium 2.0. Add the dependency to your build:",[464,73747,73749],{"className":6253,"code":73748,"language":6255,"meta":469,"style":469},"\u003Cdependency>\n \u003CgroupId>org.seleniumhq.selenium\u003C/groupId>\n \u003CartifactId>selenium-htmlunit-driver\u003C/artifactId>\n \u003Cversion>2.3.1\u003C/version>\n \u003Cscope>test\u003C/scope>\n\u003C/dependency>\n",[471,73750,73751,73755,73760,73765,73770,73774],{"__ignoreMap":469},[474,73752,73753],{"class":476,"line":477},[474,73754,65654],{},[474,73756,73757],{"class":476,"line":507},[474,73758,73759],{}," \u003CgroupId>org.seleniumhq.selenium\u003C/groupId>\n",[474,73761,73762],{"class":476,"line":547},[474,73763,73764],{}," \u003CartifactId>selenium-htmlunit-driver\u003C/artifactId>\n",[474,73766,73767],{"class":476,"line":584},[474,73768,73769],{}," \u003Cversion>2.3.1\u003C/version>\n",[474,73771,73772],{"class":476,"line":607},[474,73773,63264],{},[474,73775,73776],{"class":476,"line":642},[474,73777,65674],{},[439,73779,73780],{},"A simple webtest that just calls a page and checks for a certain html element might look like this:",[464,73782,73784],{"className":709,"code":73783,"language":711,"meta":469,"style":469},"import org.junit.Test;\nimport org.openqa.selenium.By;\nimport org.openqa.selenium.NoSuchElementException;\nimport org.openqa.selenium.WebDriver;\nimport org.openqa.selenium.WebElement;\nimport org.openqa.selenium.htmlunit.HtmlUnitDriver;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.fail;\n/**\n * Simple web test that just queries the login page through the controller.\n * @author Florian Hopf, Synyx GmbH & Co. KG, hopf@synyx.de\n */\npublic class LoginPageWebtest {\n @Test\n public void testPage() {\n WebDriver driver = new HtmlUnitDriver();\n driver.get(\"http://localhost:8081/url/that/redirects/to/login/\");\n try {\n // Find the text input element by its name\n WebElement element = driver.findElement(By.name(\"username\"));\n assertNotNull(element);\n } catch (NoSuchElementException ex) {\n fail(\"Startup of context failed. See console output for more information, : \" + ex.getMessage());\n }\n //Close the browser\n driver.quit();\n }\n}\n",[471,73785,73786,73791,73796,73801,73806,73811,73816,73821,73826,73831,73836,73841,73846,73851,73855,73860,73865,73870,73874,73879,73884,73889,73894,73899,73903,73908,73913,73917],{"__ignoreMap":469},[474,73787,73788],{"class":476,"line":477},[474,73789,73790],{},"import org.junit.Test;\n",[474,73792,73793],{"class":476,"line":507},[474,73794,73795],{},"import org.openqa.selenium.By;\n",[474,73797,73798],{"class":476,"line":547},[474,73799,73800],{},"import org.openqa.selenium.NoSuchElementException;\n",[474,73802,73803],{"class":476,"line":584},[474,73804,73805],{},"import org.openqa.selenium.WebDriver;\n",[474,73807,73808],{"class":476,"line":607},[474,73809,73810],{},"import org.openqa.selenium.WebElement;\n",[474,73812,73813],{"class":476,"line":642},[474,73814,73815],{},"import org.openqa.selenium.htmlunit.HtmlUnitDriver;\n",[474,73817,73818],{"class":476,"line":663},[474,73819,73820],{},"import static org.junit.Assert.assertNotNull;\n",[474,73822,73823],{"class":476,"line":694},[474,73824,73825],{},"import static org.junit.Assert.fail;\n",[474,73827,73828],{"class":476,"line":700},[474,73829,73830],{},"/**\n",[474,73832,73833],{"class":476,"line":913},[474,73834,73835],{}," * Simple web test that just queries the login page through the controller.\n",[474,73837,73838],{"class":476,"line":920},[474,73839,73840],{}," * @author Florian Hopf, Synyx GmbH & Co. KG, hopf@synyx.de\n",[474,73842,73843],{"class":476,"line":926},[474,73844,73845],{}," */\n",[474,73847,73848],{"class":476,"line":932},[474,73849,73850],{},"public class LoginPageWebtest {\n",[474,73852,73853],{"class":476,"line":938},[474,73854,1344],{},[474,73856,73857],{"class":476,"line":944},[474,73858,73859],{}," public void testPage() {\n",[474,73861,73862],{"class":476,"line":950},[474,73863,73864],{}," WebDriver driver = new HtmlUnitDriver();\n",[474,73866,73867],{"class":476,"line":956},[474,73868,73869],{}," driver.get(\"http://localhost:8081/url/that/redirects/to/login/\");\n",[474,73871,73872],{"class":476,"line":962},[474,73873,61985],{},[474,73875,73876],{"class":476,"line":4876},[474,73877,73878],{}," // Find the text input element by its name\n",[474,73880,73881],{"class":476,"line":4888},[474,73882,73883],{}," WebElement element = driver.findElement(By.name(\"username\"));\n",[474,73885,73886],{"class":476,"line":4900},[474,73887,73888],{}," assertNotNull(element);\n",[474,73890,73891],{"class":476,"line":4913},[474,73892,73893],{}," } catch (NoSuchElementException ex) {\n",[474,73895,73896],{"class":476,"line":4921},[474,73897,73898],{}," fail(\"Startup of context failed. See console output for more information, : \" + ex.getMessage());\n",[474,73900,73901],{"class":476,"line":4932},[474,73902,5704],{},[474,73904,73905],{"class":476,"line":4938},[474,73906,73907],{}," //Close the browser\n",[474,73909,73910],{"class":476,"line":4946},[474,73911,73912],{}," driver.quit();\n",[474,73914,73915],{"class":476,"line":4952},[474,73916,1276],{},[474,73918,73919],{"class":476,"line":4957},[474,73920,703],{},[439,73922,73923],{},"Execute the Webtest classes in your profile (we are using a naming convention to distinguish web tests from normal unit\ntests):",[464,73925,73927],{"className":6253,"code":73926,"language":6255,"meta":469,"style":469},"\u003Cplugin>\n \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n \u003Cexecutions>\n \u003Cexecution>\n \u003Cid>run-webtests\u003C/id>\n \u003Cphase>integration-test\u003C/phase>\n \u003Cgoals>\n \u003Cgoal>test\u003C/goal>\n \u003C/goals>\n \u003Cconfiguration>\n \u003Cincludes>\n \u003Cinclude>**/*Webtest.java\u003C/include>\n \u003C/includes>\n \u003Cskip>false\u003C/skip>\n \u003C/configuration>\n \u003C/execution>\n \u003C/executions>\n\u003C/plugin>\n",[471,73928,73929,73933,73938,73943,73947,73951,73956,73961,73966,73971,73976,73981,73985,73990,73994,73999,74004,74008,74012],{"__ignoreMap":469},[474,73930,73931],{"class":476,"line":477},[474,73932,44754],{},[474,73934,73935],{"class":476,"line":507},[474,73936,73937],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[474,73939,73940],{"class":476,"line":547},[474,73941,73942],{}," \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n",[474,73944,73945],{"class":476,"line":584},[474,73946,67086],{},[474,73948,73949],{"class":476,"line":607},[474,73950,44799],{},[474,73952,73953],{"class":476,"line":642},[474,73954,73955],{}," \u003Cid>run-webtests\u003C/id>\n",[474,73957,73958],{"class":476,"line":663},[474,73959,73960],{}," \u003Cphase>integration-test\u003C/phase>\n",[474,73962,73963],{"class":476,"line":694},[474,73964,73965],{}," \u003Cgoals>\n",[474,73967,73968],{"class":476,"line":700},[474,73969,73970],{}," \u003Cgoal>test\u003C/goal>\n",[474,73972,73973],{"class":476,"line":913},[474,73974,73975],{}," \u003C/goals>\n",[474,73977,73978],{"class":476,"line":920},[474,73979,73980],{}," \u003Cconfiguration>\n",[474,73982,73983],{"class":476,"line":926},[474,73984,65807],{},[474,73986,73987],{"class":476,"line":932},[474,73988,73989],{}," \u003Cinclude>**/*Webtest.java\u003C/include>\n",[474,73991,73992],{"class":476,"line":938},[474,73993,65817],{},[474,73995,73996],{"class":476,"line":944},[474,73997,73998],{}," \u003Cskip>false\u003C/skip>\n",[474,74000,74001],{"class":476,"line":950},[474,74002,74003],{}," \u003C/configuration>\n",[474,74005,74006],{"class":476,"line":956},[474,74007,44824],{},[474,74009,74010],{"class":476,"line":962},[474,74011,67119],{},[474,74013,74014],{"class":476,"line":4876},[474,74015,44834],{},[439,74017,74018],{},"That’s all. In case there is an error in your configuration you will be notified by your CI server that the webtest\nfailed.",[1024,74020,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":74022},[],[1030],"2011-10-08T12:31:45","Modern web applications often consist of quite some configuration files that should at least be tested for validity.\\nThink\\nof Spring controller configurations,\\nweb application descriptors and the like that can’t be tested easily using Unit Tests. Fortunately it’s quite easy to\\nstart a tomcat instance on your CI system (Jenkins or Hudson) using\\nthe Tomcat Maven Plugin.","https://synyx.de/blog/testing-webapp-startup-on-jenkins-with-maven-tomcat-and-web-driver/",{},"/blog/testing-webapp-startup-on-jenkins-with-maven-tomcat-and-web-driver",{"title":73692,"description":74030},"Modern web applications often consist of quite some configuration files that should at least be tested for validity.\nThink\nof Spring controller configurations,\nweb application descriptors and the like that can’t be tested easily using Unit Tests. Fortunately it’s quite easy to\nstart a tomcat instance on your CI system (Jenkins or Hudson) using\nthe Tomcat Maven Plugin.","testing-webapp-startup-on-jenkins-with-maven-tomcat-and-web-driver","blog/testing-webapp-startup-on-jenkins-with-maven-tomcat-and-web-driver",[44011,22988,18497,51154,60697],"Modern web applications often consist of quite some configuration files that should at least be tested for validity. Think of Spring controller configurations, web application descriptors and the like that…","FKN3Kcm_JeX78XwCXPVYrt9BAukKJKmqYA8c8wOwG1I",{"id":74037,"title":74038,"author":74039,"body":74040,"category":74140,"date":74141,"description":74142,"extension":1034,"link":74143,"meta":74144,"navigation":916,"path":74145,"seo":74146,"slug":74044,"stem":74147,"tags":74148,"teaser":74154,"__hash__":74155},"blog/blog/number-formats-and-jdbc-voodoo.md","Number formats and JDBC voodoo",[133],{"type":432,"value":74041,"toc":74138},[74042,74045,74048,74051,74060,74063,74066,74075,74086,74104,74111,74120,74127,74136],[435,74043,74038],{"id":74044},"number-formats-and-jdbc-voodoo",[439,74046,74047],{},"Ever had to insert some numeric values into an Oracle database? From your application through JDBC? You think “this is\nan everyday task – what should go wrong?” – well just read on…",[439,74049,74050],{},"Imagine you have got a simple table that has some numeric values that you want to update. Normally the easiest way to do\nthis is to perform a simple update statement:",[464,74052,74054],{"className":73113,"code":74053,"language":73115,"meta":469,"style":469},"update distances set distance = 12.250 where id = 1;\n",[471,74055,74056],{"__ignoreMap":469},[474,74057,74058],{"class":476,"line":477},[474,74059,74053],{},[439,74061,74062],{},"And guess what happens? The row with the ID 1 is updated to the value 12.250.",[439,74064,74065],{},"Now we have got an application that has build its own OR-mapper. As it acts in a very generic way, it does not care\nwhich data is set on a certain field. So it treats numbers the same way as strings, which results in the following\nstatement:",[464,74067,74069],{"className":73113,"code":74068,"language":73115,"meta":469,"style":469},"update distances set distance = '12.250' where id = 1;\n",[471,74070,74071],{"__ignoreMap":469},[474,74072,74073],{"class":476,"line":477},[474,74074,74068],{},[439,74076,74077,74078,74081,74082,74085],{},"No major problem one may think – oracle will just implicitly call the ",[471,74079,74080],{},"TO_NUMBER()"," function and everything should work\nlike a charm. Should – but does not. At least not always. Sometimes it fails with ",[471,74083,74084],{},"ORA-01722: invalid number",". But why?",[439,74087,74088,74089,74091,74092,74095,74096,74099,74100,74103],{},"Let’s take a close look at the ",[471,74090,74080],{}," function. The function can be configured through the (optional)\n",[471,74093,74094],{},"nlsparams",", where one is ",[471,74097,74098],{},"NLS_NUMERIC_CHARACTERS=''dg''",". ‘d’ tells the function which character is used as the decimal\nseparator, ‘g’ the group separator. If the conversion is done implicit it is not possible to set those parameters\ndirectly, instead those that are set through the environment variable ",[471,74101,74102],{},"NLS_NUMERIC_CHARACTERS"," is used. If this variable\nis not set either, the oracle default value is used, which is ‘.,’ (UK format).",[439,74105,74106,74107,74110],{},"On our setup no value was set, so one might expect that the above statement should work. It did, as long as it was\nexecuted on the database server. If failed with ",[471,74108,74109],{},"ORA-01722"," in case it was run from a remote database tool or on an\napplication server. More strangely it succeeded if it was run in the following way (but only from remote, it failed if\nrun from the server):",[464,74112,74114],{"className":73113,"code":74113,"language":73115,"meta":469,"style":469},"update distances set distance = '12,250' where id = 1;\n",[471,74115,74116],{"__ignoreMap":469},[474,74117,74118],{"class":476,"line":477},[474,74119,74113],{},[439,74121,74122,74123,74126],{},"So the only difference was the JDBC driver that is in-between. And there we have got the problem: The JDBC driver tries\nto do some “intelligent” conversion of your SQL statements. For that purpose it uses the system property\n",[471,74124,74125],{},"user.language",". If this property is not set explicitly, is is set through the locale of the system it is running on.\nThis is no problem as long as the application runs on a system that has a locale set to en_*. But as soon as it is run\non a system that provides a locale with a different number format (like de_DE), the JDBC driver tries to convert the\nnumeric values and the statement fails on the database.",[439,74128,74129,74130,74132,74133,74135],{},"So if your application connects your database through JDBC and you rely on the implicit ",[471,74131,74080],{}," conversion of\nOracle, make sure the system property ",[471,74134,74125],{}," is set to the correct value!",[1024,74137,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":74139},[],[1030],"2011-09-13T12:41:11","Ever had to insert some numeric values into an Oracle database? From your application through JDBC? You think “this is\\nan everyday task – what should go wrong?” – well just read on…","https://synyx.de/blog/number-formats-and-jdbc-voodoo/",{},"/blog/number-formats-and-jdbc-voodoo",{"title":74038,"description":74047},"blog/number-formats-and-jdbc-voodoo",[74149,74150,74151,74152,74153],"jdbc","number","ora-01722","oracle","to_number","Ever had to insert some numeric values into an Oracle database? From your application through JDBC? You think “this is an everyday task – what should go wrong?” – well…","JDixzOeBinZa4_Xzi6M_R8-dHrKNSkZkOQhmKWX2sp8",{"id":74157,"title":74158,"author":74159,"body":74160,"category":74367,"date":74368,"description":74369,"extension":1034,"link":74370,"meta":74371,"navigation":916,"path":74372,"seo":74373,"slug":74164,"stem":74375,"tags":74376,"teaser":74378,"__hash__":74379},"blog/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline.md","Continuous Delivery or: How I Learned to Stop Worrying and Love the Pipeline",[365],{"type":432,"value":74161,"toc":74365},[74162,74165,74205,74214,74217,74226,74229,74232,74254,74257,74263,74266,74294,74308,74317,74320,74336,74341,74347,74358],[435,74163,74158],{"id":74164},"continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",[439,74166,74167,74168,74171,74172,74176,74177,74182,74183,74188,74189,74194,74195,74188,74200,11288],{},"Following our principle of ",[990,74169,74170],{},"Continuous Skill Enhancement"," here at ",[1002,74173,74175],{"href":8256,"rel":74174},[1006],"Synyx"," I\nrecently read the\nbook ",[1002,74178,74181],{"href":74179,"rel":74180},"http://www.informit.com/store/product.aspx?isbn=0321601912",[1006],"Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation","\nby ",[1002,74184,74187],{"href":74185,"rel":74186},"http://jezhumble.net/",[1006],"Jez Humble"," (from ",[1002,74190,74193],{"href":74191,"rel":74192},"http://www.thoughtworks.com/",[1006],"ThoughtWorks",")\nand ",[1002,74196,74199],{"href":74197,"rel":74198},"http://www.davefarley.net/",[1006],"David Farley",[1002,74201,74204],{"href":74202,"rel":74203},"http://www.lmaxtrader.co.uk/",[1006],"LMAX",[439,74206,74207],{},[1002,74208,74211],{"href":74209,"rel":74210},"https://media.synyx.de/uploads//2011/08/continuous_delivery_cover.jpeg",[1006],[2205,74212],{"alt":469,"src":74213,"title":71691},"https://media.synyx.de/uploads//2011/08/continuous_delivery_cover-227x300.jpg",[439,74215,74216],{},"The book consists of three distinct parts.",[439,74218,74219,74220,74225],{},"Part one provides a high-level overview about the basics of software delivery. The authors are touching topics such as\nconfiguration management, continuous integration and software testing, describing what they are good for and what the\nchallenges are when implementing them. While the chapters help to understand the terminology used throughout the book\nthey don’t (and cannot) describe each of the topics in great detail – there\nare ",[1002,74221,74224],{"href":74222,"rel":74223},"http://www.informit.com/store/product.aspx?isbn=0321336380",[1006],"other books"," for that. But of course you’re already\nfamiliar with these topics so it’s just a little refresher.",[439,74227,74228],{},"Part two is dedicated to the central concept described in Continuous Delivery: the deployment pipeline. The idea is to\nreceive immediate feedback on errors and regressions as early in the development lifecycle of a project as possible and\nto provide a working application to the users as early and often as possible.",[439,74230,74231],{},"This means that every commit by a developer triggers a run of the deployment pipeline. It starts with building the\nartifact (obviously), proceeds to the first test stage running unit tests, from which it continues to the integration\ntest phase. If all tests ran successfully the artifact will continue through the stages of the deployment pipeline, e.\ng. a smoke test or non-functional test stage (think security and performance tests) and to a UAT (user acceptance\ntesting) stage. Finally the artifact will end up in the staging environment and from there it should require only the\nclick of a button to deploy it to production. Of course the authors describe each step in great detail and have some\nanecdotes from their projects to lighten up the text.",[439,74233,74234,74235,74239,74240,74243,74244,18175,74249,74253],{},"The central theme of part three is managing different parts of the delivery ecosystem. The authors discuss pros and cons\nof physical servers, virtualized servers and cloud computing, introduce the reader to the concepts of automatic machine\nprovisioning and configuration management with ",[1002,74236,51207],{"href":74237,"rel":74238},"http://www.puppetlabs.com/",[1006],", monitoring your systems and\ncollecting logs and performance data. They talk about managing test data, how to version it and how to get a basic stock\nof data for running integration tests in the first place. One chapter is dedicated to the challenges of managing\ncomponents and dependencies in which the authors discuss different strategies of versioning the components of your\napplication. It even comprises a short introduction to ",[1002,74241,72823],{"href":47203,"rel":74242},[1006],". In the following\nchapter the authors give an introduction to different revision control systems\nlike ",[1002,74245,74248],{"href":74246,"rel":74247},"http://subversion.apache.org/",[1006],"Subversion",[1002,74250,19054],{"href":74251,"rel":74252},"http://gitscm.com/",[1006],", as well as commercial alternatives like\nBitKeeper and ClearCase, and their respective advantages and disadvantages over the free alternatives. They continue to\ndescribe several advanced branching and integration strategies, each with its very own advantages and disadvantages in\ndifferent situations.",[439,74255,74256],{},"The last chapter swiftly copes with rather non-technical questions like the project lifecycle risk management and how\ncompliance and auditing are handled in a project using continuous delivery.",[439,74258,74259,74260,74262],{},"The concepts detailed in ",[990,74261,71691],{}," are not new per se but it’s the first book I read that really brought\nthese together in one coherent narrative. In fact, most of the concepts will seem to be obvious once you’ve read and\ngrokked them, but somehow nobody ever thought about them in depth.",[439,74264,74265],{},"Some of the distilled concepts are:",[994,74267,74268,74275,74278,74281,74288],{},[997,74269,74270,74271,74274],{},"Build binaries exactly ",[448,74272,74273],{},"once",", store them in your artifact repository and promote them through the complete\ndeployment pipeline.",[997,74276,74277],{},"Only promote builds into staging or production that pass all unit and acceptance tests.",[997,74279,74280],{},"The development, testing, UAT and staging environments should be as similar as possible to the production environment.",[997,74282,74283,74284,74287],{},"Automate ",[448,74285,74286],{},"everything",": builds, configuration, tests. Human interaction is prone to error, try to avoid it wherever\npossible.",[997,74289,74290,74291],{},"Use version control for ",[448,74292,74293],{},"everything, including the configuration of underlying operating systems and infrastructure\nsuch as networking equipment.",[439,74295,74296,74298,74299,74304,74305,1402],{},[990,74297,71691],{}," has rightfully received much praise around the Internet and especially in the recently popularized\nDevOps movement. In 2011, the authors also won a ",[1002,74300,74303],{"href":74301,"rel":74302},"http://drdobbs.com/joltawards/231500080?pgno=7",[1006],"Jolt Excellence Award","\nin the category ",[990,74306,74307],{},"The Best Books",[439,74309,74310,74311,74316],{},"One thing I didn’t like about the book is the way online sources have been referenced in the text. Whenever the authors\nreference a website they provide an alphanumeric shortcode you know from URL shorteners like TinyURL. In fact that’s\nexactly what they are. These shortcodes can be used with Bit.ly or, as a fallback, directly from\nthe ",[1002,74312,74315],{"href":74313,"rel":74314},"http://continuousdelivery.com/",[1006],"supporting website"," of the book.",[439,74318,74319],{},"Example:",[994,74321,74322,74329],{},[997,74323,74324],{},[1002,74325,74328],{"href":74326,"rel":74327},"http://bit.ly/bibNp0",[1006],"bit.ly/bibNp0",[997,74330,74331],{},[1002,74332,74335],{"href":74333,"rel":74334},"http://continuousdelivery.com/go/bibNp0",[1006],"continuousdelivery.com/go/bibNp0",[439,74337,74338,74339,1402],{},"This often interrupts the flow of reading. Instead a more traditional style, e. g. placing the shortcodes in footnotes,\nwould have been preferable in the printed version of the book. I also missed a list of all referenced online sources,\neither at the end of each chapter or in a separate appendix. Fortunately this is really the only criticism I have for\n",[990,74340,71691],{},[439,74342,74343,74344,74346],{},"As a conclusion, I can really recommend reading ",[990,74345,71691],{}," to anyone involved in developing and delivering\nsoftware. It will provide some new points of view on your work and give you some new ideas about how to improve your\ncurrent processes. I, for one, am looking forward to applying the principles outlined in this book to some of our\nprojects.",[439,74348,74349,74350,4709,74353,1402],{},"If you’re hooked now you might want to read the sample chapter from ",[990,74351,74352],{},"Continuous\nDelivery",[1002,74354,74357],{"href":74355,"rel":74356},"http://www.informit.com/content/images/9780321601919/samplepages/0321601912.pdf",[1006],"Chapter 5 – Anatomy of the Deployment Pipeline",[439,74359,74360,74361],{},"Oh, and by the way: ",[1002,74362,74364],{"href":3568,"rel":74363},[1006],"We’re hiring!",{"title":469,"searchDepth":507,"depth":507,"links":74366},[],[1030],"2011-08-23T12:00:36","Following our principle of Continuous Skill Enhancement here at Synyx I\\nrecently read the\\nbook Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation\\nby Jez Humble (from ThoughtWorks)\\nand David Farley (from LMAX).","https://synyx.de/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline/",{},"/blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",{"title":74158,"description":74374},"Following our principle of Continuous Skill Enhancement here at Synyx I\nrecently read the\nbook Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation\nby Jez Humble (from ThoughtWorks)\nand David Farley (from LMAX).","blog/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",[74377,56692,56693,44011,37776,14723],"book-review","Following our principle of Continuous Skill Enhancement here at Synyx I recently read the book Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation by Jez Humble (from…","jrVlzFTz3ngZjPQljDiLT8CFkWqKVIVudFr7Im47nTA",{"id":74381,"title":74382,"author":74383,"body":74384,"category":74434,"date":74435,"description":74436,"extension":1034,"link":74437,"meta":74438,"navigation":916,"path":74439,"seo":74440,"slug":74441,"stem":74442,"tags":74443,"teaser":74444,"__hash__":74445},"blog/blog/sommerfeier-bei-synyx-eine-nasse-angelegenheit-2.md","Sommerfeier bei Synyx – eine nasse Angelegenheit",[30],{"type":432,"value":74385,"toc":74432},[74386,74389,74401,74404,74409,74412,74415,74418,74423,74426,74429],[435,74387,74382],{"id":74388},"sommerfeier-bei-synyx-eine-nasse-angelegenheit",[439,74390,74391,74398],{},[1002,74392,74395],{"href":74393,"rel":74394},"https://media.synyx.de/uploads//2011/08/kanu.jpg",[1006],[2205,74396],{"alt":74397,"src":74393,"title":74397},"Kanu fahren",[990,74399,74400],{},"Nicht nur der diesjährige Sommer hat uns oft nasse Bescherung von oben geboten, auch die Sommerfeierei bei Synyx stand\ndieses Jahr ganz im Zeichen des Wassers. Ausflugsziel war nämlich der Altrhein – und zwar nicht zum gemächlichen\nEntenfüttern, sondern zum rasanten Kanufahren in den Stromschnellen des Altrheins.",[439,74402,74403],{},"Für die Meisten war der Startpunkt 13 Uhr im Büro, um sich mit sprudelndem Proviant einzudecken; so eine Kanufahrt macht\nschließlich durstig. Mit der Bahn ging es dann ans Ende der Welt, nach Rappenwörth, wo der Rest der Paddelwilligen zu\nuns stieß. Eine Prise Verwirrung und Ratlosigkeit später („Wo ist denn dieser Kanuverleih überhaupt?“) war unsere Suche\ndoch noch erfolgreich. Wir teilten uns in Zweier- und Dreiergruppen auf und wurden mit Kanus ausgestattet, die wir ans\nUfer hievten, um in See zu stechen. Eines der Kanus bildete sich wohl ein, ein U-Boot zu sein, was zur Folge hatte,\ndass Markus als Erster bis zum Bauch im Wasser stand. Rebecca, ihr Mann und ich sollten das allerdings noch toppen. Denn\nwir fielen beim Einsteigen gleich kopfüber ins Wasser, als unser Kanu kenterte. Zunächst einmal gab es keine weiteren\nOpfer zu beklagen und es hieß für alle: „Volle Fahrt voraus!“.",[439,74405,74406],{},[448,74407,74408],{},"Natürlich wäre gesittetes Benehmen auf dem Kanu viel zu langweilig, weshalb der Altrhein spontan zu einem\nWasserschlachtfeld erklärt wurde.",[439,74410,74411],{},"Feuer lässt sich vielleicht nicht mit Feuer bekämpfen, aber Wasser mit Wasser. Wir lernten schnell, dass sich ein Paddel\nnicht nur zum Paddeln eignet, sondern auch ein sehr effektives Werkzeug zur Bewässerung anderer Kanus und der\nzugehörigen Crew darstellt. Man munkelt, dass das Admin-Kanu noch viel schärfere Geschütze aufgefahren hatte, wie z.B.\nWasserpistolen und Wasserbomben; und ein Megafon hatte wohl sonst auch kein anderes Kanu mit an Bord.",[439,74413,74414],{},"Während der ereignisreichen Seeschlachten hieß es für manche Besatzung leider auch mal: „Mann/Frau über Bord!“ Es steht\nimmer noch Aussage gegen Aussage, wer denn wirklich schuld war am Kentern des Kanus von Thomas, Florian und Lianna; es\ngibt da verschiedene Versionen der Geschichte. Was allerdings sicher feststeht, ist, dass die erwähnten Wasserratten nur\ndurch die Großherzigkeit anderer Besatzungen (anzuführen wären hier das Junggesellen-Kanu, das Admin-Kanu und\nunsereins) gerettet werden konnten – und das gleich zwei Mal innerhalb weniger Minuten.",[439,74416,74417],{},"Während der Fahrt ereilte uns auch tatsächlich noch der angekündigte Regenschauer. Allerdings störte dieser nicht\nweiter, da man ja durch all den Schabernack sowieso schon nass war.",[439,74419,74420],{},[448,74421,74422],{},"Nach circa zweieinhalb Stunden Seeschlacht auf dem Altrhein hatten die vor Brackwasser triefenden Synyx-Piraten\nendlich wieder festen Boden unter den Füßen.",[439,74424,74425],{},"Ein wenig erschöpft und hungrig kehrte man bei den Rheinbrüdern ein. Dort standen schon die Kaltgetränke bereit, während\nder Grill gerade angeschmissen wurde. Neben dem Grillgut hatte das Buffet auch eine Vegetarier-freundliche Auswahl an\nSalaten und Antipasti zu bieten. Als kleines i-Tüpfelchen gab es zum Nachtisch dann noch Kuchen und Eis.",[439,74427,74428],{},"Es wurde reichlich gespeist und getrunken, Seemannsgarn ausgetauscht und miteinander gelacht bis in den Morgen hinein. (\ndie Letzten räumten das Feld gegen drei Uhr früh)",[439,74430,74431],{},"Alles in allem war die Sommerfeier (die gleichzeitig meine erste Feier bei Synyx war) genauso nass wie gelungen, was\nschon viel Vorfreude auf die nächsten Feiern macht.",{"title":469,"searchDepth":507,"depth":507,"links":74433},[],[1031],"2011-08-16T13:16:01","Nicht nur der diesjährige Sommer hat uns oft nasse Bescherung von oben geboten, auch die Sommerfeierei bei Synyx stand\\ndieses Jahr ganz im Zeichen des Wassers. Ausflugsziel war nämlich der Altrhein – und zwar nicht zum gemächlichen\\nEntenfüttern, sondern zum rasanten Kanufahren in den Stromschnellen des Altrheins.","https://synyx.de/blog/sommerfeier-bei-synyx-eine-nasse-angelegenheit-2/",{},"/blog/sommerfeier-bei-synyx-eine-nasse-angelegenheit-2",{"title":74382,"description":74400},"sommerfeier-bei-synyx-eine-nasse-angelegenheit-2","blog/sommerfeier-bei-synyx-eine-nasse-angelegenheit-2",[62431,389],"Nicht nur der diesjährige Sommer hat uns oft nasse Bescherung von oben geboten, auch die Sommerfeierei bei Synyx stand dieses Jahr ganz im Zeichen des Wassers. Ausflugsziel war nämlich der…","yVGa0sRACTuGZGS54jD3cekjhzxqpm31JcogDSBIPlE",{"id":74447,"title":74448,"author":74449,"body":74450,"category":74511,"date":74512,"description":74513,"extension":1034,"link":74514,"meta":74515,"navigation":916,"path":74516,"seo":74517,"slug":74454,"stem":74518,"tags":74519,"teaser":74520,"__hash__":74521},"blog/blog/nun-werde-ich-zum-fachinformatikersystemintegration-gemacht.md","Nun werde ich zum Fachinformatiker/Systemintegration gemacht",[196],{"type":432,"value":74451,"toc":74509},[74452,74455,74458,74461,74464,74469,74480,74483,74486,74489,74500,74503,74506],[435,74453,74448],{"id":74454},"nun-werde-ich-zum-fachinformatikersystemintegration-gemacht",[439,74456,74457],{},"Mein Name ist Matthias Knorre und ich konnte mir nach einem 4-tägigen Praktikum bei der Systemadministration von Synyx,\nwährend dem ich meine Programmier-Erfahrung zeigen durfte, eine Stelle als Azubi der Fachrichtung\n“Fachinformatiker/Systemintegration” ergattern.",[439,74459,74460],{},"Die Ausbildung begann vor zwei Wochen, in denen ich bereits den größten Teil der Firmen-Angehörigen besser kennenlernen\nkonnte, da bereits am Ende der ersten Woche die alljährliche Firmen-Sommerfeierei stattfand. Eine grandiose Möglichkeit\nalso, sich als “Neuer” vorzustellen und mit den Kollegen in’s Gespräch zu kommen.",[439,74462,74463],{},"Nach ersten Einführungen in die Systeme und deren Architektur konnte ich mich ab der zweiten Woche in mein erstes\nProjekt einarbeiten:",[439,74465,74466],{},[448,74467,74468],{},"Temperaturüberwachung unserer Server per 1-Wire-Bus.",[439,74470,74471],{},[1002,74472,74475],{"href":74473,"rel":74474},"https://media.synyx.de/uploads//2011/08/testaufbau_1.png",[1006],[2205,74476],{"alt":74477,"src":74478,"title":74479},"Der erste Versuch auf dem Steckbrett","https://media.synyx.de/uploads//2011/08/testaufbau_1-150x150.png","alpha-Prototyp",[439,74481,74482],{},"Erster Versuchsaufbau",[439,74484,74485],{},"Dazu begann ich zunächst unter fachkundiger Anleitung meines Ausbilders Max Ferstl nach Bauteilen und\nAnwendungs-Beispielen im Netz zu suchen. So verglich ich verschiedene Versuchsaufbauten anderer Projekte, Preise der\nBauteile bei diversen Anbietern sowie mögliche Leitungswege und Steckverbindungen hier im Haus.",[439,74487,74488],{},"Schließlich entschieden wir uns für einen Versuchsaufbau mittels BreadBoard (Steckbrett) und Anbindung durch einen\nvorkonfektionierten USB\u003C->1-Wire Adapter (1-Wire Bus Master), um Fehlerquellen auszuschließen.",[439,74490,74491],{},[1002,74492,74495],{"href":74493,"rel":74494},"https://media.synyx.de/uploads//2011/08/USB-1Wire.jpg",[1006],[2205,74496],{"alt":74497,"src":74498,"title":74499},"USB to RJ11 1-Wire","https://media.synyx.de/uploads//2011/08/USB-1Wire-300x180.jpg","USB to 1Wire",[439,74501,74502],{},"1-Wire Bus Master",[439,74504,74505],{},"Nach erfolgreichem Test des alpha-Prototyps und der Einbindung des OneWireFileSystems (OWFS) in mein System konnte ich\nnach kurzer Zeit bereits die erste Temperaturabfrage eines Sensors starten. Auch der Versuch zwei weitere Sensoren\nmittels HotPlug (Anschluss im laufenden Betrieb) einzubinden war erfolgreich.",[439,74507,74508],{},"Somit steht dem Übergang in die Beta-Phase nichts mehr im Wege. Für den Anfang sollten 10 Sensoren an einem Verteiler\ngenügen, um die Funktion in einem der Serverschränke in unserer Nähe zu realisieren. Für die endgültige Version der\nTemperaturüberwachung ist eine Einbindung in die Nagios-Systemüberwachung unserer Firma vorgesehen.",{"title":469,"searchDepth":507,"depth":507,"links":74510},[],[13208],"2011-08-15T10:03:09","Mein Name ist Matthias Knorre und ich konnte mir nach einem 4-tägigen Praktikum bei der Systemadministration von Synyx,\\nwährend dem ich meine Programmier-Erfahrung zeigen durfte, eine Stelle als Azubi der Fachrichtung\\n“Fachinformatiker/Systemintegration” ergattern.","https://synyx.de/blog/nun-werde-ich-zum-fachinformatikersystemintegration-gemacht/",{},"/blog/nun-werde-ich-zum-fachinformatikersystemintegration-gemacht",{"title":74448,"description":74457},"blog/nun-werde-ich-zum-fachinformatikersystemintegration-gemacht",[13219],"Mein Name ist Matthias Knorre und ich konnte mir nach einem 4-tägigen Praktikum bei der Systemadministration von Synyx, während dem ich meine Programmier-Erfahrung zeigen durfte, eine Stelle als Azubi der…","Lf0wp4p264_qO7iKhGKvmRLH0CvWTJBV-R6Sl5xyPFA",{"id":74523,"title":74524,"author":74525,"body":74526,"category":74557,"date":74558,"description":74543,"extension":1034,"link":74559,"meta":74560,"navigation":916,"path":74561,"seo":74562,"slug":74530,"stem":74563,"tags":74564,"teaser":74566,"__hash__":74567},"blog/blog/endlich-mal-mit-profis-arbeiten.md","Endlich mal mit Profis arbeiten?",[172],{"type":432,"value":74527,"toc":74555},[74528,74531,74544,74547],[435,74529,74524],{"id":74530},"endlich-mal-mit-profis-arbeiten",[439,74532,74533,74541],{},[1002,74534,74537],{"href":74535,"rel":74536},"https://media.synyx.de/uploads//2011/07/java_entwickler_wanted.jpg",[1006],[2205,74538],{"alt":74539,"src":74535,"title":74540},"Java Entwickler wanted"," Java Entwickler wanted",[448,74542,74543],{},"Wir suchen ab sofort Verstärkung für unser Individualsoftware-Team!",[439,74545,74546],{},"Interessante Projekte, nette Arbeitsatmosphäre und alles, was man sonst so braucht.",[439,74548,74549,74550],{},"Schau mal rein, egal ob Du zum reinen Entwickler, zum Softwarearchitekten oder zum Kommunikationsgenie\ntendierst. ",[1002,74551,74554],{"href":3568,"rel":74552,"title":74553},[1006],"Java Entwickler wanted!","Mehr Infos",{"title":469,"searchDepth":507,"depth":507,"links":74556},[],[1030],"2011-07-06T10:15:59","https://synyx.de/blog/endlich-mal-mit-profis-arbeiten/",{},"/blog/endlich-mal-mit-profis-arbeiten",{"title":74524,"description":74543},"blog/endlich-mal-mit-profis-arbeiten",[17867,711,74565,389],"job","Wir suchen ab sofort Verstärkung für unser Individualsoftware-Team! Interessante Projekte, nette Arbeitsatmosphäre und alles, was man sonst so braucht. Schau mal rein, egal ob Du zum reinen Entwickler, zum Softwarearchitekten…","fso_GB2lp56bh3XCQrLc9Zl_yWIIEl3mP_m_CWDbV4I",{"id":74569,"title":74570,"author":74571,"body":74572,"category":76402,"date":76403,"description":76404,"extension":1034,"link":76405,"meta":76406,"navigation":916,"path":76407,"seo":76408,"slug":74576,"stem":76410,"tags":76411,"teaser":76416,"__hash__":76417},"blog/blog/the-tale-of-jboss-and-the-7-little-logging-frameworks.md","The Tale of JBoss and the 7 Little Logging Frameworks",[365],{"type":432,"value":74573,"toc":76398},[74574,74577,74584,74594,74597,74611,74629,74648,74657,74666,74675,74684,74687,74696,74762,74771,74829,74844,76176,76179,76218,76233,76251,76254,76304,76308,76365,76372,76376,76395],[435,74575,74570],{"id":74576},"the-tale-of-jboss-and-the-7-little-logging-frameworks",[439,74578,74579,74580,74583],{},"At Synyx we’re currently taking care of a rather large legacy project for one of our customers in the course of\nour ",[1002,74581,18566],{"href":66510,"rel":74582},[1006]," services. The project comprises several components\nsuch as a fat client implemented with a custom UI framework on top of Swing, a bulky web application using a mixture of\ncustom and obsolete frameworks, and a lot of asynchronously running jobs to process input from other systems involving\ncustom XSL transformations and a heap of stored procedures in a Oracle 9i database. You get the picture, it’s the\nprototype of a legacy system.",[439,74585,74586],{},[1002,74587,74590],{"href":74588,"rel":74589},"https://media.synyx.de/uploads//2011/06/7dwarves.jpg",[1006],[2205,74591],{"alt":469,"src":74592,"title":74593},"https://media.synyx.de/uploads//2011/06/7dwarves-300x225.jpg","7 Little Logging Frameworks",[439,74595,74596],{},"7 Little Logging Frameworks on their way into your code base",[439,74598,74599,74600,74605,74606,74610],{},"The original developers of the system suffered a serious case of the\nwell-known ",[1002,74601,74604],{"href":74602,"rel":74603},"http://en.wikipedia.org/wiki/Not_Invented_Here",[1006],"NIH syndrome"," and thus a lot\nof ",[1002,74607,40135],{"href":74608,"rel":74609},"http://www.martinfowler.com/bliki/TechnicalDebt.html",[1006]," has been piled up over the course of its\ndevelopment.",[439,74612,74613,74614,520,74619,7267,74624,1402],{},"One peculiar case was the use of about 7 different logging abstractions scattered over the whole code base. While some (\nwell, just one) of the implementations provide a certain added value, the other ones were just plain wrappers around the\neventually used logging frameworks. They literally just added a bad API on top of the other. There were also at least\nthree different logging frameworks in use,\nnamely ",[1002,74615,74618],{"href":74616,"rel":74617},"http://commons.apache.org/logging/",[1006],"Apache Commons Logging",[1002,74620,74623],{"href":74621,"rel":74622},"http://www.slf4j.org/",[1006],"SLF4J",[1002,74625,74628],{"href":74626,"rel":74627},"http://logging.apache.org/log4j/",[1006],"Log4j",[439,74630,74631,74632,7267,74637,74642,74643,23112],{},"In order to consolidate the code base, to reduce the dependencies on external frameworks and to prevent conflicts\ninduced by the use of generic class names like Log or Logger we decided to clean up this mess and use SLF4J with its\nLog4j back end as our authoritative logging framework in this project. We chose SLF4J for several (hopefully good)\nreasons, e. g. the low number of dependencies, the ability to plug in the logging framework of choice at deployment\ntime, the clean API and the good support for legacy logging frameworks. Also read the\narticles ",[1002,74633,74636],{"href":74634,"rel":74635},"http://bsnyderblog.blogspot.com/2007/08/my-soapbox-for-slf4j.html",[1006],"My Soapbox for SLF4J",[1002,74638,74641],{"href":74639,"rel":74640},"http://blog.frankel.ch/thoughts-on-java-logging-and-slf4j",[1006],"Thoughts on Java logging and SLF4J"," for a more detailed\ndiscussion of SLF4J’s features. Did I mention SLF4J also ",[1002,74644,74647],{"href":74645,"rel":74646},"http://www.slf4j.org/android/",[1006],"works on Android",[439,74649,74650,74651,74656],{},"SLF4J thankfully makes it easy to ",[1002,74652,74655],{"href":74653,"rel":74654},"http://www.slf4j.org/legacy.html",[1006],"bridge legacy logging frameworks"," so that you don’t\nhave to refactor all your code at once but could incrementally migrate your code base while still using SLF4J under the\nhood.",[439,74658,74659,74660,74665],{},"As you might have already guessed from the title of this blog post, the project uses the\nestablished ",[1002,74661,74664],{"href":74662,"rel":74663},"http://www.jboss.org/",[1006],"JBoss"," Application Server. Unfortunately it’s currently stuck at version\n4.0.3.SP1 but that’s another topic.",[439,74667,74668,74669,74674],{},"After deciding on which framework to use we started refactoring our code base. At first this wasn’t a problem. Deleting\nthe legacy logging classes and then replacing their calls with our authoritative\nSLF4J ",[1002,74670,74673],{"href":74671,"rel":74672},"http://www.slf4j.org/apidocs/org/slf4j/Logger.html",[1006],"Logger"," was straight forward. Since the logging classes were\nscattered across the whole code base we sometimes missed a dependency in another component of the project, but that’s\nwhat a Continuous Integration system is for after all.",[439,74676,74677],{},[1002,74678,74680],{"href":74621,"rel":74679},[1006],[2205,74681],{"alt":74682,"src":74683,"title":74682},"SLF4J Logo","https://media.synyx.de/uploads//2011/06/slf4j-logo.jpg",[439,74685,74686],{},"Simple Logging Facade for Java",[439,74688,74689,74690,74695],{},"We also adapted the dependencies in the components’ POM files to use SLF4J’s commons-logging bridge instead of\ncommons-logging itself. Pro tip from the trenches: Don’t confuse jcl-over-slf4j.jar with slf4j-jcl.jar! The former\nis the API bridge we wanted to use, the latter is a SLF4J backend implementation using commons-logging. In order to get\nthe dependencies straight we used\nMaven’s ",[1002,74691,74694],{"href":74692,"rel":74693},"http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management",[1006],"dependency management","\nfeature and added the following declarations in the parent POM for the project’s components:",[464,74697,74699],{"className":6253,"code":74698,"language":6255,"meta":469,"style":469},"\u003CdependencyManagement>\n \u003Cdependency>\n \u003CgroupId>commons-logging\u003C/groupId>\n \u003CartifactId>commons-logging\u003C/artifactId>\n \u003Cversion>1.1.1\u003C/version>\n \u003Cscope>provided\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.slf4j\u003C/groupId>\n \u003CartifactId>jcl-over-slf4j\u003C/artifactId>\n \u003Cversion>1.6.1\u003C/version>\n \u003C/dependency>\n\u003C/dependencyManagement>\n",[471,74700,74701,74706,74710,74715,74720,74725,74730,74734,74738,74743,74748,74753,74757],{"__ignoreMap":469},[474,74702,74703],{"class":476,"line":477},[474,74704,74705],{},"\u003CdependencyManagement>\n",[474,74707,74708],{"class":476,"line":507},[474,74709,63244],{},[474,74711,74712],{"class":476,"line":547},[474,74713,74714],{}," \u003CgroupId>commons-logging\u003C/groupId>\n",[474,74716,74717],{"class":476,"line":584},[474,74718,74719],{}," \u003CartifactId>commons-logging\u003C/artifactId>\n",[474,74721,74722],{"class":476,"line":607},[474,74723,74724],{}," \u003Cversion>1.1.1\u003C/version>\n",[474,74726,74727],{"class":476,"line":642},[474,74728,74729],{}," \u003Cscope>provided\u003C/scope>\n",[474,74731,74732],{"class":476,"line":663},[474,74733,63269],{},[474,74735,74736],{"class":476,"line":694},[474,74737,63244],{},[474,74739,74740],{"class":476,"line":700},[474,74741,74742],{}," \u003CgroupId>org.slf4j\u003C/groupId>\n",[474,74744,74745],{"class":476,"line":913},[474,74746,74747],{}," \u003CartifactId>jcl-over-slf4j\u003C/artifactId>\n",[474,74749,74750],{"class":476,"line":920},[474,74751,74752],{}," \u003Cversion>1.6.1\u003C/version>\n",[474,74754,74755],{"class":476,"line":926},[474,74756,63269],{},[474,74758,74759],{"class":476,"line":932},[474,74760,74761],{},"\u003C/dependencyManagement>\n",[439,74763,74764,74765,74770],{},"By setting\nthe ",[1002,74766,74769],{"href":74767,"rel":74768},"http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope",[1006],"scope"," of\ncommons-logging to “provided” we made sure that it would not be pulled in accidentally. Unfortunately this scope is not\ntransitive and we had to explicitly exclude commons-logging (and other artifacts) from some of our dependencies:",[464,74772,74774],{"className":6253,"code":74773,"language":6255,"meta":469,"style":469},"\u003Cdependency>\n \u003CgroupId>org.apache.ws.commons.axiom\u003C/groupId>\n \u003CartifactId>axiom-api\u003C/artifactId>\n \u003Cversion>1.2\u003C/version>\n \u003Cexclusions>\n \u003Cexclusion>\n \u003CartifactId>commons-logging\u003C/artifactId>\n \u003CgroupId>commons-logging\u003C/groupId>\n \u003C/exclusion>\n \u003C/exclusions>\n\u003C/dependency>\n",[471,74775,74776,74780,74785,74790,74795,74800,74805,74810,74815,74820,74825],{"__ignoreMap":469},[474,74777,74778],{"class":476,"line":477},[474,74779,65654],{},[474,74781,74782],{"class":476,"line":507},[474,74783,74784],{}," \u003CgroupId>org.apache.ws.commons.axiom\u003C/groupId>\n",[474,74786,74787],{"class":476,"line":547},[474,74788,74789],{}," \u003CartifactId>axiom-api\u003C/artifactId>\n",[474,74791,74792],{"class":476,"line":584},[474,74793,74794],{}," \u003Cversion>1.2\u003C/version>\n",[474,74796,74797],{"class":476,"line":607},[474,74798,74799],{}," \u003Cexclusions>\n",[474,74801,74802],{"class":476,"line":642},[474,74803,74804],{}," \u003Cexclusion>\n",[474,74806,74807],{"class":476,"line":663},[474,74808,74809],{}," \u003CartifactId>commons-logging\u003C/artifactId>\n",[474,74811,74812],{"class":476,"line":694},[474,74813,74814],{}," \u003CgroupId>commons-logging\u003C/groupId>\n",[474,74816,74817],{"class":476,"line":700},[474,74818,74819],{}," \u003C/exclusion>\n",[474,74821,74822],{"class":476,"line":913},[474,74823,74824],{}," \u003C/exclusions>\n",[474,74826,74827],{"class":476,"line":920},[474,74828,65674],{},[439,74830,74831,74832,74837,74838,74843],{},"In order to analyze the dependency trees of our components\nthe ",[1002,74833,74836],{"href":74834,"rel":74835},"http://maven.apache.org/plugins/maven-dependency-plugin/",[1006],"Maven dependency plugin"," turned out to be very useful,\nespecially the ",[1002,74839,74842],{"href":74840,"rel":74841},"http://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html",[1006],"dependency:tree"," mojo which will\nproduce a view of the project’s dependency tree including transitive dependencies. Of course a good IDE will also\nsupport you with graphical views of the dependency tree. Here’s the output of mvn dependency:tree for the aforementioned\nSwing application:",[464,74845,74847],{"className":54685,"code":74846,"language":54687,"meta":469,"style":469},"[INFO] [dependency:tree {execution: default-cli}]\n[INFO] com.example:swing-client:jar:1.1.0-SNAPSHOT\n[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.1:compile\n[INFO] | \\- log4j:log4j:jar:1.2.16:compile\n[INFO] +- jgoodies:plastic:jar:1.2.0:compile\n[INFO] +- com.toedter:jcalendar:jar:1.3.2:compile\n[INFO] +- com.example:jms:jar:1.0.1-SNAPSHOT:compile\n[INFO] | +- jboss:jboss-system:jar:4.0.2:compile\n[INFO] | +- jms:jms:jar:1.0.2:compile\n[INFO] | +- org.quartz-scheduler:quartz:jar:1.2.3:compile\n[INFO] | +- jboss:jbossmq-client:jar:4.0.2:compile\n[INFO] | +- jboss:jboss-jmx:jar:4.0.2:compile\n[INFO] | \\- javax.ejb:ejb:jar:2.1:compile\n[INFO] +- flex:flex-messaging-common:jar:1.0.0:compile\n[INFO] +- flex:flex-messaging-core:jar:1.0.0:compile\n[INFO] +- flex:flex-messaging-opt:jar:1.0.0:compile\n[INFO] +- flex:flex-messaging-proxy:jar:1.0.0:compile\n[INFO] +- flex:flex-messaging-remoting:jar:1.0.0:compile\n[INFO] +- poi:poi-2.5.1-final:jar:20040804:compile\n[INFO] +- com.example:docengine:jar:1.0.0-SNAPSHOT:compile\n[INFO] | +- com.example:webfw:jar:1.0.2-SNAPSHOT:compile\n[INFO] | | +- com.example:dms_client:jar:1.0.2:compile\n[INFO] | | | \\- org.apache.axis2:axis2:jar:1.1.1:compile\n[INFO] | | | +- org.apache.ws.commons.axiom:axiom-dom:jar:1.2:compile\n[INFO] | | | +- org.apache.ws.commons.axiom:axiom-impl:jar:1.2:compile\n[INFO] | | | +- ant:ant:jar:1.6.5:compile\n[INFO] | | | +- woodstox:wstx-asl:jar:3.0.1:compile\n[INFO] | | | +- org.apache.ws.commons.schema:XmlSchema:jar:1.2:compile\n[INFO] | | | +- annogen:annogen:jar:0.1.0:compile\n[INFO] | | | +- commons-httpclient:commons-httpclient:jar:3.0.1:compile\n[INFO] | | | | \\- commons-codec:commons-codec:jar:1.2:compile\n[INFO] | | | +- org.apache.httpcomponents:jakarta-httpcore:jar:4.0-alpha2:compile\n[INFO] | | | +- wsdl4j:wsdl4j:jar:1.6.1:compile\n[INFO] | | | +- backport-util-concurrent:backport-util-concurrent:jar:2.2:compile\n[INFO] | | | +- org.apache.ws.commons.neethi:neethi:jar:2.0:compile\n[INFO] | | | \\- org.apache.woden:woden-impl-om:jar:1.0M8:compile\n[INFO] | | | +- org.apache.woden:woden-api:jar:1.0M8:compile\n[INFO] | | | +- org.apache.ant:ant:jar:1.7.0:compile\n[INFO] | | | | \\- org.apache.ant:ant-launcher:jar:1.7.0:compile\n[INFO] | | | +- xerces:xmlParserAPIs:jar:2.6.0:compile\n[INFO] | | | \\- org.codehaus.woodstox:wstx-asl:jar:3.2.4:runtime\n[INFO] | | +- org.apache.ws.commons.axiom:axiom-api:jar:1.2:compile\n[INFO] | | | +- jaxen:jaxen:jar:1.1-beta-9:compile\n[INFO] | | | +- xml-apis:xml-apis:jar:1.3.03:compile\n[INFO] | | | \\- stax:stax-api:jar:1.0.1:compile\n[INFO] | | +- org.slf4j:jcl-over-slf4j:jar:1.6.1:compile\n[INFO] | | +- org.apache.struts:struts-core:jar:1.3.8:compile\n[INFO] | | | \\- commons-chain:commons-chain:jar:1.1:compile\n[INFO] | | +- com.sun.xml:xml:jar:0.8.0:compile\n[INFO] | | +- xmlc:xmlc-all-runtime:jar:2.2.8.1:compile\n[INFO] | | +- xalan:xalan:jar:2.7.1:compile\n[INFO] | | | \\- xalan:serializer:jar:2.7.1:compile\n[INFO] | | \\- com.lowagie:itext:jar:2.0.7:compile\n[INFO] | | +- bouncycastle:bcmail-jdk14:jar:138:compile\n[INFO] | | \\- bouncycastle:bcprov-jdk14:jar:138:compile\n[INFO] | +- org.apache.xmlgraphics:fop:jar:0.95-1:compile\n[INFO] | | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:1.3.1:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-svg-dom:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-anim:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-css:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-dom:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-parser:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-util:jar:1.7:compile\n[INFO] | | | \\- xml-apis:xml-apis-ext:jar:1.3.04:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-bridge:jar:1.7:compile\n[INFO] | | | +- org.apache.xmlgraphics:batik-script:jar:1.7:compile\n[INFO] | | | \\- org.apache.xmlgraphics:batik-xml:jar:1.7:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-awt-util:jar:1.7:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-gvt:jar:1.7:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-transcoder:jar:1.7:compile\n[INFO] | | | \\- org.apache.xmlgraphics:batik-svggen:jar:1.7:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-extension:jar:1.7:compile\n[INFO] | | +- org.apache.xmlgraphics:batik-ext:jar:1.7:compile\n[INFO] | | +- commons-io:commons-io:jar:1.3.1:compile\n[INFO] | | \\- org.apache.avalon.framework:avalon-framework-impl:jar:4.3.1:compile\n[INFO] | +- org.apache.avalon.framework:avalon-framework-api:jar:4.3.1:compile\n[INFO] | +- poi:poi:jar:2.5.1-final-20040804:compile\n[INFO] | +- javax.servlet:servlet-api:jar:2.5:compile\n[INFO] | +- javax.mail:mail:jar:1.4.1:compile\n[INFO] | | \\- javax.activation:activation:jar:1.1.1:compile\n[INFO] | +- org.openoffice:jurt:jar:3.2.1:compile\n[INFO] | | \\- org.openoffice:ridl:jar:3.2.1:compile\n[INFO] | +- org.openoffice:unoil:jar:3.1.0:compile\n[INFO] | +- org.openoffice:juh:jar:3.1.0:compile\n[INFO] | \\- ru.novosoft.dc:rtf2fo:jar:eval:compile\n[INFO] +- com.jgoodies:forms:jar:1.0.7:compile\n[INFO] +- xerces:xercesImpl:jar:2.4.0:compile\n[INFO] +- com.oracle:ojdbc5:jar:11.1.0.6.0:compile\n[INFO] +- javax.help:javahelp:jar:2.0.02:compile\n[INFO] +- com.example:custom-swing-framework:jar:1.1.2-SNAPSHOT:compile\n[INFO] | +- com.whirlycott:whirlycache:jar:0.7.1:compile\n[INFO] | | +- commons-collections:commons-collections:jar:3.1:compile\n[INFO] | | +- jdom:jdom:jar:1.0:compile\n[INFO] | | \\- concurrent:concurrent:jar:1.3.4:compile\n[INFO] | +- ehcache:ehcache:jar:0.9:compile\n[INFO] | +- org.enhydra.xmlc:xmlc:jar:2.2.7.1:compile\n[INFO] | +- com.jgoodies:looks:jar:2.2.2:compile\n[INFO] | +- org.swinglabs:swingx:jar:1.6.1:compile\n[INFO] | | +- com.jhlabs:filters:jar:2.0.235:compile\n[INFO] | | \\- org.swinglabs:swing-worker:jar:1.1:compile\n[INFO] | \\- struts:struts:jar:1.2.9:compile\n[INFO] | +- commons-beanutils:commons-beanutils:jar:1.7.0:compile\n[INFO] | +- commons-digester:commons-digester:jar:1.6:compile\n[INFO] | +- commons-fileupload:commons-fileupload:jar:1.0:compile\n[INFO] | +- commons-validator:commons-validator:jar:1.1.4:compile\n[INFO] | +- oro:oro:jar:2.0.7:compile\n[INFO] | \\- antlr:antlr:jar:2.7.2:compile\n[INFO] +- junit:junit:jar:4.8.2:test\n[INFO] \\- org.slf4j:slf4j-api:jar:1.6.1:compile\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESSFUL\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time: 2 seconds\n[INFO] Finished at: Tue Jun 28 14:10:29 CEST 2011\n[INFO] Final Memory: 22M/257M\n[INFO] ------------------------------------------------------------------------\n",[471,74848,74849,74854,74859,74864,74877,74882,74887,74892,74904,74915,74926,74937,74948,74959,74964,74969,74974,74979,74984,74989,74994,75005,75019,75034,75050,75065,75080,75095,75110,75125,75140,75158,75173,75188,75203,75218,75234,75250,75265,75283,75298,75314,75327,75342,75357,75372,75385,75398,75413,75426,75439,75452,75467,75480,75493,75506,75517,75530,75543,75558,75573,75588,75603,75618,75633,75646,75661,75676,75689,75702,75715,75730,75743,75756,75769,75782,75793,75804,75815,75826,75839,75850,75863,75874,75885,75896,75901,75906,75911,75916,75921,75932,75945,75958,75971,75982,75993,76004,76015,76028,76041,76052,76063,76074,76085,76096,76107,76119,76125,76136,76142,76148,76153,76159,76165,76171],{"__ignoreMap":469},[474,74850,74851],{"class":476,"line":477},[474,74852,74853],{"class":503},"[INFO] [dependency:tree {execution: default-cli}]\n",[474,74855,74856],{"class":476,"line":507},[474,74857,74858],{"class":503},"[INFO] com.example:swing-client:jar:1.1.0-SNAPSHOT\n",[474,74860,74861],{"class":476,"line":547},[474,74862,74863],{"class":503},"[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.1:compile\n",[474,74865,74866,74869,74871,74874],{"class":476,"line":584},[474,74867,74868],{"class":503},"[INFO] ",[474,74870,6911],{"class":810},[474,74872,74873],{"class":480}," \\-",[474,74875,74876],{"class":484}," log4j:log4j:jar:1.2.16:compile\n",[474,74878,74879],{"class":476,"line":607},[474,74880,74881],{"class":503},"[INFO] +- jgoodies:plastic:jar:1.2.0:compile\n",[474,74883,74884],{"class":476,"line":642},[474,74885,74886],{"class":503},"[INFO] +- com.toedter:jcalendar:jar:1.3.2:compile\n",[474,74888,74889],{"class":476,"line":663},[474,74890,74891],{"class":503},"[INFO] +- com.example:jms:jar:1.0.1-SNAPSHOT:compile\n",[474,74893,74894,74896,74898,74901],{"class":476,"line":694},[474,74895,74868],{"class":503},[474,74897,6911],{"class":810},[474,74899,74900],{"class":480}," +-",[474,74902,74903],{"class":484}," jboss:jboss-system:jar:4.0.2:compile\n",[474,74905,74906,74908,74910,74912],{"class":476,"line":700},[474,74907,74868],{"class":503},[474,74909,6911],{"class":810},[474,74911,74900],{"class":480},[474,74913,74914],{"class":484}," jms:jms:jar:1.0.2:compile\n",[474,74916,74917,74919,74921,74923],{"class":476,"line":913},[474,74918,74868],{"class":503},[474,74920,6911],{"class":810},[474,74922,74900],{"class":480},[474,74924,74925],{"class":484}," org.quartz-scheduler:quartz:jar:1.2.3:compile\n",[474,74927,74928,74930,74932,74934],{"class":476,"line":920},[474,74929,74868],{"class":503},[474,74931,6911],{"class":810},[474,74933,74900],{"class":480},[474,74935,74936],{"class":484}," jboss:jbossmq-client:jar:4.0.2:compile\n",[474,74938,74939,74941,74943,74945],{"class":476,"line":926},[474,74940,74868],{"class":503},[474,74942,6911],{"class":810},[474,74944,74900],{"class":480},[474,74946,74947],{"class":484}," jboss:jboss-jmx:jar:4.0.2:compile\n",[474,74949,74950,74952,74954,74956],{"class":476,"line":932},[474,74951,74868],{"class":503},[474,74953,6911],{"class":810},[474,74955,74873],{"class":480},[474,74957,74958],{"class":484}," javax.ejb:ejb:jar:2.1:compile\n",[474,74960,74961],{"class":476,"line":938},[474,74962,74963],{"class":503},"[INFO] +- flex:flex-messaging-common:jar:1.0.0:compile\n",[474,74965,74966],{"class":476,"line":944},[474,74967,74968],{"class":503},"[INFO] +- flex:flex-messaging-core:jar:1.0.0:compile\n",[474,74970,74971],{"class":476,"line":950},[474,74972,74973],{"class":503},"[INFO] +- flex:flex-messaging-opt:jar:1.0.0:compile\n",[474,74975,74976],{"class":476,"line":956},[474,74977,74978],{"class":503},"[INFO] +- flex:flex-messaging-proxy:jar:1.0.0:compile\n",[474,74980,74981],{"class":476,"line":962},[474,74982,74983],{"class":503},"[INFO] +- flex:flex-messaging-remoting:jar:1.0.0:compile\n",[474,74985,74986],{"class":476,"line":4876},[474,74987,74988],{"class":503},"[INFO] +- poi:poi-2.5.1-final:jar:20040804:compile\n",[474,74990,74991],{"class":476,"line":4888},[474,74992,74993],{"class":503},"[INFO] +- com.example:docengine:jar:1.0.0-SNAPSHOT:compile\n",[474,74995,74996,74998,75000,75002],{"class":476,"line":4900},[474,74997,74868],{"class":503},[474,74999,6911],{"class":810},[474,75001,74900],{"class":480},[474,75003,75004],{"class":484}," com.example:webfw:jar:1.0.2-SNAPSHOT:compile\n",[474,75006,75007,75009,75011,75014,75016],{"class":476,"line":4913},[474,75008,74868],{"class":503},[474,75010,6911],{"class":810},[474,75012,75013],{"class":810}," |",[474,75015,74900],{"class":480},[474,75017,75018],{"class":484}," com.example:dms_client:jar:1.0.2:compile\n",[474,75020,75021,75023,75025,75027,75029,75031],{"class":476,"line":4921},[474,75022,74868],{"class":503},[474,75024,6911],{"class":810},[474,75026,75013],{"class":810},[474,75028,75013],{"class":810},[474,75030,74873],{"class":480},[474,75032,75033],{"class":484}," org.apache.axis2:axis2:jar:1.1.1:compile\n",[474,75035,75036,75038,75040,75042,75044,75047],{"class":476,"line":4932},[474,75037,74868],{"class":503},[474,75039,6911],{"class":810},[474,75041,75013],{"class":810},[474,75043,75013],{"class":810},[474,75045,75046],{"class":480}," +-",[474,75048,75049],{"class":484}," org.apache.ws.commons.axiom:axiom-dom:jar:1.2:compile\n",[474,75051,75052,75054,75056,75058,75060,75062],{"class":476,"line":4938},[474,75053,74868],{"class":503},[474,75055,6911],{"class":810},[474,75057,75013],{"class":810},[474,75059,75013],{"class":810},[474,75061,75046],{"class":480},[474,75063,75064],{"class":484}," org.apache.ws.commons.axiom:axiom-impl:jar:1.2:compile\n",[474,75066,75067,75069,75071,75073,75075,75077],{"class":476,"line":4946},[474,75068,74868],{"class":503},[474,75070,6911],{"class":810},[474,75072,75013],{"class":810},[474,75074,75013],{"class":810},[474,75076,75046],{"class":480},[474,75078,75079],{"class":484}," ant:ant:jar:1.6.5:compile\n",[474,75081,75082,75084,75086,75088,75090,75092],{"class":476,"line":4952},[474,75083,74868],{"class":503},[474,75085,6911],{"class":810},[474,75087,75013],{"class":810},[474,75089,75013],{"class":810},[474,75091,75046],{"class":480},[474,75093,75094],{"class":484}," woodstox:wstx-asl:jar:3.0.1:compile\n",[474,75096,75097,75099,75101,75103,75105,75107],{"class":476,"line":4957},[474,75098,74868],{"class":503},[474,75100,6911],{"class":810},[474,75102,75013],{"class":810},[474,75104,75013],{"class":810},[474,75106,75046],{"class":480},[474,75108,75109],{"class":484}," org.apache.ws.commons.schema:XmlSchema:jar:1.2:compile\n",[474,75111,75112,75114,75116,75118,75120,75122],{"class":476,"line":4969},[474,75113,74868],{"class":503},[474,75115,6911],{"class":810},[474,75117,75013],{"class":810},[474,75119,75013],{"class":810},[474,75121,75046],{"class":480},[474,75123,75124],{"class":484}," annogen:annogen:jar:0.1.0:compile\n",[474,75126,75127,75129,75131,75133,75135,75137],{"class":476,"line":4990},[474,75128,74868],{"class":503},[474,75130,6911],{"class":810},[474,75132,75013],{"class":810},[474,75134,75013],{"class":810},[474,75136,75046],{"class":480},[474,75138,75139],{"class":484}," commons-httpclient:commons-httpclient:jar:3.0.1:compile\n",[474,75141,75142,75144,75146,75148,75150,75153,75155],{"class":476,"line":5001},[474,75143,74868],{"class":503},[474,75145,6911],{"class":810},[474,75147,75013],{"class":810},[474,75149,75013],{"class":810},[474,75151,75152],{"class":810}," |",[474,75154,74873],{"class":480},[474,75156,75157],{"class":484}," commons-codec:commons-codec:jar:1.2:compile\n",[474,75159,75160,75162,75164,75166,75168,75170],{"class":476,"line":5013},[474,75161,74868],{"class":503},[474,75163,6911],{"class":810},[474,75165,75013],{"class":810},[474,75167,75013],{"class":810},[474,75169,75046],{"class":480},[474,75171,75172],{"class":484}," org.apache.httpcomponents:jakarta-httpcore:jar:4.0-alpha2:compile\n",[474,75174,75175,75177,75179,75181,75183,75185],{"class":476,"line":5024},[474,75176,74868],{"class":503},[474,75178,6911],{"class":810},[474,75180,75013],{"class":810},[474,75182,75013],{"class":810},[474,75184,75046],{"class":480},[474,75186,75187],{"class":484}," wsdl4j:wsdl4j:jar:1.6.1:compile\n",[474,75189,75190,75192,75194,75196,75198,75200],{"class":476,"line":5035},[474,75191,74868],{"class":503},[474,75193,6911],{"class":810},[474,75195,75013],{"class":810},[474,75197,75013],{"class":810},[474,75199,75046],{"class":480},[474,75201,75202],{"class":484}," backport-util-concurrent:backport-util-concurrent:jar:2.2:compile\n",[474,75204,75205,75207,75209,75211,75213,75215],{"class":476,"line":5047},[474,75206,74868],{"class":503},[474,75208,6911],{"class":810},[474,75210,75013],{"class":810},[474,75212,75013],{"class":810},[474,75214,75046],{"class":480},[474,75216,75217],{"class":484}," org.apache.ws.commons.neethi:neethi:jar:2.0:compile\n",[474,75219,75220,75222,75224,75226,75228,75231],{"class":476,"line":5055},[474,75221,74868],{"class":503},[474,75223,6911],{"class":810},[474,75225,75013],{"class":810},[474,75227,75013],{"class":810},[474,75229,75230],{"class":480}," \\-",[474,75232,75233],{"class":484}," org.apache.woden:woden-impl-om:jar:1.0M8:compile\n",[474,75235,75236,75238,75240,75242,75244,75247],{"class":476,"line":5062},[474,75237,74868],{"class":503},[474,75239,6911],{"class":810},[474,75241,75013],{"class":810},[474,75243,75013],{"class":810},[474,75245,75246],{"class":480}," +-",[474,75248,75249],{"class":484}," org.apache.woden:woden-api:jar:1.0M8:compile\n",[474,75251,75252,75254,75256,75258,75260,75262],{"class":476,"line":5067},[474,75253,74868],{"class":503},[474,75255,6911],{"class":810},[474,75257,75013],{"class":810},[474,75259,75013],{"class":810},[474,75261,75246],{"class":480},[474,75263,75264],{"class":484}," org.apache.ant:ant:jar:1.7.0:compile\n",[474,75266,75267,75269,75271,75273,75275,75278,75280],{"class":476,"line":5072},[474,75268,74868],{"class":503},[474,75270,6911],{"class":810},[474,75272,75013],{"class":810},[474,75274,75013],{"class":810},[474,75276,75277],{"class":810}," |",[474,75279,74873],{"class":480},[474,75281,75282],{"class":484}," org.apache.ant:ant-launcher:jar:1.7.0:compile\n",[474,75284,75285,75287,75289,75291,75293,75295],{"class":476,"line":5084},[474,75286,74868],{"class":503},[474,75288,6911],{"class":810},[474,75290,75013],{"class":810},[474,75292,75013],{"class":810},[474,75294,75246],{"class":480},[474,75296,75297],{"class":484}," xerces:xmlParserAPIs:jar:2.6.0:compile\n",[474,75299,75300,75302,75304,75306,75308,75311],{"class":476,"line":5100},[474,75301,74868],{"class":503},[474,75303,6911],{"class":810},[474,75305,75013],{"class":810},[474,75307,75013],{"class":810},[474,75309,75310],{"class":480}," \\-",[474,75312,75313],{"class":484}," org.codehaus.woodstox:wstx-asl:jar:3.2.4:runtime\n",[474,75315,75316,75318,75320,75322,75324],{"class":476,"line":5111},[474,75317,74868],{"class":503},[474,75319,6911],{"class":810},[474,75321,75013],{"class":810},[474,75323,74900],{"class":480},[474,75325,75326],{"class":484}," org.apache.ws.commons.axiom:axiom-api:jar:1.2:compile\n",[474,75328,75329,75331,75333,75335,75337,75339],{"class":476,"line":5122},[474,75330,74868],{"class":503},[474,75332,6911],{"class":810},[474,75334,75013],{"class":810},[474,75336,75013],{"class":810},[474,75338,74900],{"class":480},[474,75340,75341],{"class":484}," jaxen:jaxen:jar:1.1-beta-9:compile\n",[474,75343,75344,75346,75348,75350,75352,75354],{"class":476,"line":5134},[474,75345,74868],{"class":503},[474,75347,6911],{"class":810},[474,75349,75013],{"class":810},[474,75351,75013],{"class":810},[474,75353,74900],{"class":480},[474,75355,75356],{"class":484}," xml-apis:xml-apis:jar:1.3.03:compile\n",[474,75358,75359,75361,75363,75365,75367,75369],{"class":476,"line":5145},[474,75360,74868],{"class":503},[474,75362,6911],{"class":810},[474,75364,75013],{"class":810},[474,75366,75013],{"class":810},[474,75368,74873],{"class":480},[474,75370,75371],{"class":484}," stax:stax-api:jar:1.0.1:compile\n",[474,75373,75374,75376,75378,75380,75382],{"class":476,"line":5156},[474,75375,74868],{"class":503},[474,75377,6911],{"class":810},[474,75379,75013],{"class":810},[474,75381,74900],{"class":480},[474,75383,75384],{"class":484}," org.slf4j:jcl-over-slf4j:jar:1.6.1:compile\n",[474,75386,75387,75389,75391,75393,75395],{"class":476,"line":5163},[474,75388,74868],{"class":503},[474,75390,6911],{"class":810},[474,75392,75013],{"class":810},[474,75394,74900],{"class":480},[474,75396,75397],{"class":484}," org.apache.struts:struts-core:jar:1.3.8:compile\n",[474,75399,75400,75402,75404,75406,75408,75410],{"class":476,"line":5170},[474,75401,74868],{"class":503},[474,75403,6911],{"class":810},[474,75405,75013],{"class":810},[474,75407,75013],{"class":810},[474,75409,74873],{"class":480},[474,75411,75412],{"class":484}," commons-chain:commons-chain:jar:1.1:compile\n",[474,75414,75415,75417,75419,75421,75423],{"class":476,"line":5175},[474,75416,74868],{"class":503},[474,75418,6911],{"class":810},[474,75420,75013],{"class":810},[474,75422,74900],{"class":480},[474,75424,75425],{"class":484}," com.sun.xml:xml:jar:0.8.0:compile\n",[474,75427,75428,75430,75432,75434,75436],{"class":476,"line":5180},[474,75429,74868],{"class":503},[474,75431,6911],{"class":810},[474,75433,75013],{"class":810},[474,75435,74900],{"class":480},[474,75437,75438],{"class":484}," xmlc:xmlc-all-runtime:jar:2.2.8.1:compile\n",[474,75440,75441,75443,75445,75447,75449],{"class":476,"line":5192},[474,75442,74868],{"class":503},[474,75444,6911],{"class":810},[474,75446,75013],{"class":810},[474,75448,74900],{"class":480},[474,75450,75451],{"class":484}," xalan:xalan:jar:2.7.1:compile\n",[474,75453,75454,75456,75458,75460,75462,75464],{"class":476,"line":5217},[474,75455,74868],{"class":503},[474,75457,6911],{"class":810},[474,75459,75013],{"class":810},[474,75461,75013],{"class":810},[474,75463,74873],{"class":480},[474,75465,75466],{"class":484}," xalan:serializer:jar:2.7.1:compile\n",[474,75468,75469,75471,75473,75475,75477],{"class":476,"line":5229},[474,75470,74868],{"class":503},[474,75472,6911],{"class":810},[474,75474,75013],{"class":810},[474,75476,74873],{"class":480},[474,75478,75479],{"class":484}," com.lowagie:itext:jar:2.0.7:compile\n",[474,75481,75482,75484,75486,75488,75490],{"class":476,"line":5240},[474,75483,74868],{"class":503},[474,75485,6911],{"class":810},[474,75487,75013],{"class":810},[474,75489,75046],{"class":480},[474,75491,75492],{"class":484}," bouncycastle:bcmail-jdk14:jar:138:compile\n",[474,75494,75495,75497,75499,75501,75503],{"class":476,"line":5251},[474,75496,74868],{"class":503},[474,75498,6911],{"class":810},[474,75500,75013],{"class":810},[474,75502,75230],{"class":480},[474,75504,75505],{"class":484}," bouncycastle:bcprov-jdk14:jar:138:compile\n",[474,75507,75508,75510,75512,75514],{"class":476,"line":5262},[474,75509,74868],{"class":503},[474,75511,6911],{"class":810},[474,75513,74900],{"class":480},[474,75515,75516],{"class":484}," org.apache.xmlgraphics:fop:jar:0.95-1:compile\n",[474,75518,75519,75521,75523,75525,75527],{"class":476,"line":5274},[474,75520,74868],{"class":503},[474,75522,6911],{"class":810},[474,75524,75013],{"class":810},[474,75526,74900],{"class":480},[474,75528,75529],{"class":484}," org.apache.xmlgraphics:xmlgraphics-commons:jar:1.3.1:compile\n",[474,75531,75532,75534,75536,75538,75540],{"class":476,"line":5281},[474,75533,74868],{"class":503},[474,75535,6911],{"class":810},[474,75537,75013],{"class":810},[474,75539,74900],{"class":480},[474,75541,75542],{"class":484}," org.apache.xmlgraphics:batik-svg-dom:jar:1.7:compile\n",[474,75544,75545,75547,75549,75551,75553,75555],{"class":476,"line":5294},[474,75546,74868],{"class":503},[474,75548,6911],{"class":810},[474,75550,75013],{"class":810},[474,75552,75013],{"class":810},[474,75554,74900],{"class":480},[474,75556,75557],{"class":484}," org.apache.xmlgraphics:batik-anim:jar:1.7:compile\n",[474,75559,75560,75562,75564,75566,75568,75570],{"class":476,"line":5307},[474,75561,74868],{"class":503},[474,75563,6911],{"class":810},[474,75565,75013],{"class":810},[474,75567,75013],{"class":810},[474,75569,74900],{"class":480},[474,75571,75572],{"class":484}," org.apache.xmlgraphics:batik-css:jar:1.7:compile\n",[474,75574,75575,75577,75579,75581,75583,75585],{"class":476,"line":5318},[474,75576,74868],{"class":503},[474,75578,6911],{"class":810},[474,75580,75013],{"class":810},[474,75582,75013],{"class":810},[474,75584,74900],{"class":480},[474,75586,75587],{"class":484}," org.apache.xmlgraphics:batik-dom:jar:1.7:compile\n",[474,75589,75590,75592,75594,75596,75598,75600],{"class":476,"line":5323},[474,75591,74868],{"class":503},[474,75593,6911],{"class":810},[474,75595,75013],{"class":810},[474,75597,75013],{"class":810},[474,75599,74900],{"class":480},[474,75601,75602],{"class":484}," org.apache.xmlgraphics:batik-parser:jar:1.7:compile\n",[474,75604,75605,75607,75609,75611,75613,75615],{"class":476,"line":5330},[474,75606,74868],{"class":503},[474,75608,6911],{"class":810},[474,75610,75013],{"class":810},[474,75612,75013],{"class":810},[474,75614,74900],{"class":480},[474,75616,75617],{"class":484}," org.apache.xmlgraphics:batik-util:jar:1.7:compile\n",[474,75619,75620,75622,75624,75626,75628,75630],{"class":476,"line":5335},[474,75621,74868],{"class":503},[474,75623,6911],{"class":810},[474,75625,75013],{"class":810},[474,75627,75013],{"class":810},[474,75629,74873],{"class":480},[474,75631,75632],{"class":484}," xml-apis:xml-apis-ext:jar:1.3.04:compile\n",[474,75634,75635,75637,75639,75641,75643],{"class":476,"line":5340},[474,75636,74868],{"class":503},[474,75638,6911],{"class":810},[474,75640,75013],{"class":810},[474,75642,74900],{"class":480},[474,75644,75645],{"class":484}," org.apache.xmlgraphics:batik-bridge:jar:1.7:compile\n",[474,75647,75648,75650,75652,75654,75656,75658],{"class":476,"line":5352},[474,75649,74868],{"class":503},[474,75651,6911],{"class":810},[474,75653,75013],{"class":810},[474,75655,75013],{"class":810},[474,75657,74900],{"class":480},[474,75659,75660],{"class":484}," org.apache.xmlgraphics:batik-script:jar:1.7:compile\n",[474,75662,75663,75665,75667,75669,75671,75673],{"class":476,"line":5376},[474,75664,74868],{"class":503},[474,75666,6911],{"class":810},[474,75668,75013],{"class":810},[474,75670,75013],{"class":810},[474,75672,74873],{"class":480},[474,75674,75675],{"class":484}," org.apache.xmlgraphics:batik-xml:jar:1.7:compile\n",[474,75677,75678,75680,75682,75684,75686],{"class":476,"line":5387},[474,75679,74868],{"class":503},[474,75681,6911],{"class":810},[474,75683,75013],{"class":810},[474,75685,74900],{"class":480},[474,75687,75688],{"class":484}," org.apache.xmlgraphics:batik-awt-util:jar:1.7:compile\n",[474,75690,75691,75693,75695,75697,75699],{"class":476,"line":5398},[474,75692,74868],{"class":503},[474,75694,6911],{"class":810},[474,75696,75013],{"class":810},[474,75698,74900],{"class":480},[474,75700,75701],{"class":484}," org.apache.xmlgraphics:batik-gvt:jar:1.7:compile\n",[474,75703,75704,75706,75708,75710,75712],{"class":476,"line":5409},[474,75705,74868],{"class":503},[474,75707,6911],{"class":810},[474,75709,75013],{"class":810},[474,75711,74900],{"class":480},[474,75713,75714],{"class":484}," org.apache.xmlgraphics:batik-transcoder:jar:1.7:compile\n",[474,75716,75717,75719,75721,75723,75725,75727],{"class":476,"line":5420},[474,75718,74868],{"class":503},[474,75720,6911],{"class":810},[474,75722,75013],{"class":810},[474,75724,75013],{"class":810},[474,75726,74873],{"class":480},[474,75728,75729],{"class":484}," org.apache.xmlgraphics:batik-svggen:jar:1.7:compile\n",[474,75731,75732,75734,75736,75738,75740],{"class":476,"line":5432},[474,75733,74868],{"class":503},[474,75735,6911],{"class":810},[474,75737,75013],{"class":810},[474,75739,74900],{"class":480},[474,75741,75742],{"class":484}," org.apache.xmlgraphics:batik-extension:jar:1.7:compile\n",[474,75744,75745,75747,75749,75751,75753],{"class":476,"line":5439},[474,75746,74868],{"class":503},[474,75748,6911],{"class":810},[474,75750,75013],{"class":810},[474,75752,74900],{"class":480},[474,75754,75755],{"class":484}," org.apache.xmlgraphics:batik-ext:jar:1.7:compile\n",[474,75757,75758,75760,75762,75764,75766],{"class":476,"line":5450},[474,75759,74868],{"class":503},[474,75761,6911],{"class":810},[474,75763,75013],{"class":810},[474,75765,74900],{"class":480},[474,75767,75768],{"class":484}," commons-io:commons-io:jar:1.3.1:compile\n",[474,75770,75771,75773,75775,75777,75779],{"class":476,"line":5461},[474,75772,74868],{"class":503},[474,75774,6911],{"class":810},[474,75776,75013],{"class":810},[474,75778,74873],{"class":480},[474,75780,75781],{"class":484}," org.apache.avalon.framework:avalon-framework-impl:jar:4.3.1:compile\n",[474,75783,75784,75786,75788,75790],{"class":476,"line":5470},[474,75785,74868],{"class":503},[474,75787,6911],{"class":810},[474,75789,74900],{"class":480},[474,75791,75792],{"class":484}," org.apache.avalon.framework:avalon-framework-api:jar:4.3.1:compile\n",[474,75794,75795,75797,75799,75801],{"class":476,"line":5475},[474,75796,74868],{"class":503},[474,75798,6911],{"class":810},[474,75800,74900],{"class":480},[474,75802,75803],{"class":484}," poi:poi:jar:2.5.1-final-20040804:compile\n",[474,75805,75806,75808,75810,75812],{"class":476,"line":5482},[474,75807,74868],{"class":503},[474,75809,6911],{"class":810},[474,75811,74900],{"class":480},[474,75813,75814],{"class":484}," javax.servlet:servlet-api:jar:2.5:compile\n",[474,75816,75817,75819,75821,75823],{"class":476,"line":5487},[474,75818,74868],{"class":503},[474,75820,6911],{"class":810},[474,75822,74900],{"class":480},[474,75824,75825],{"class":484}," javax.mail:mail:jar:1.4.1:compile\n",[474,75827,75828,75830,75832,75834,75836],{"class":476,"line":5492},[474,75829,74868],{"class":503},[474,75831,6911],{"class":810},[474,75833,75013],{"class":810},[474,75835,74873],{"class":480},[474,75837,75838],{"class":484}," javax.activation:activation:jar:1.1.1:compile\n",[474,75840,75841,75843,75845,75847],{"class":476,"line":5504},[474,75842,74868],{"class":503},[474,75844,6911],{"class":810},[474,75846,74900],{"class":480},[474,75848,75849],{"class":484}," org.openoffice:jurt:jar:3.2.1:compile\n",[474,75851,75852,75854,75856,75858,75860],{"class":476,"line":5521},[474,75853,74868],{"class":503},[474,75855,6911],{"class":810},[474,75857,75013],{"class":810},[474,75859,74873],{"class":480},[474,75861,75862],{"class":484}," org.openoffice:ridl:jar:3.2.1:compile\n",[474,75864,75865,75867,75869,75871],{"class":476,"line":5532},[474,75866,74868],{"class":503},[474,75868,6911],{"class":810},[474,75870,74900],{"class":480},[474,75872,75873],{"class":484}," org.openoffice:unoil:jar:3.1.0:compile\n",[474,75875,75876,75878,75880,75882],{"class":476,"line":5543},[474,75877,74868],{"class":503},[474,75879,6911],{"class":810},[474,75881,74900],{"class":480},[474,75883,75884],{"class":484}," org.openoffice:juh:jar:3.1.0:compile\n",[474,75886,75887,75889,75891,75893],{"class":476,"line":5554},[474,75888,74868],{"class":503},[474,75890,6911],{"class":810},[474,75892,74873],{"class":480},[474,75894,75895],{"class":484}," ru.novosoft.dc:rtf2fo:jar:eval:compile\n",[474,75897,75898],{"class":476,"line":5565},[474,75899,75900],{"class":503},"[INFO] +- com.jgoodies:forms:jar:1.0.7:compile\n",[474,75902,75903],{"class":476,"line":5576},[474,75904,75905],{"class":503},"[INFO] +- xerces:xercesImpl:jar:2.4.0:compile\n",[474,75907,75908],{"class":476,"line":5583},[474,75909,75910],{"class":503},"[INFO] +- com.oracle:ojdbc5:jar:11.1.0.6.0:compile\n",[474,75912,75913],{"class":476,"line":5590},[474,75914,75915],{"class":503},"[INFO] +- javax.help:javahelp:jar:2.0.02:compile\n",[474,75917,75918],{"class":476,"line":5595},[474,75919,75920],{"class":503},"[INFO] +- com.example:custom-swing-framework:jar:1.1.2-SNAPSHOT:compile\n",[474,75922,75923,75925,75927,75929],{"class":476,"line":5600},[474,75924,74868],{"class":503},[474,75926,6911],{"class":810},[474,75928,74900],{"class":480},[474,75930,75931],{"class":484}," com.whirlycott:whirlycache:jar:0.7.1:compile\n",[474,75933,75934,75936,75938,75940,75942],{"class":476,"line":5612},[474,75935,74868],{"class":503},[474,75937,6911],{"class":810},[474,75939,75013],{"class":810},[474,75941,74900],{"class":480},[474,75943,75944],{"class":484}," commons-collections:commons-collections:jar:3.1:compile\n",[474,75946,75947,75949,75951,75953,75955],{"class":476,"line":5632},[474,75948,74868],{"class":503},[474,75950,6911],{"class":810},[474,75952,75013],{"class":810},[474,75954,74900],{"class":480},[474,75956,75957],{"class":484}," jdom:jdom:jar:1.0:compile\n",[474,75959,75960,75962,75964,75966,75968],{"class":476,"line":5643},[474,75961,74868],{"class":503},[474,75963,6911],{"class":810},[474,75965,75013],{"class":810},[474,75967,74873],{"class":480},[474,75969,75970],{"class":484}," concurrent:concurrent:jar:1.3.4:compile\n",[474,75972,75973,75975,75977,75979],{"class":476,"line":5654},[474,75974,74868],{"class":503},[474,75976,6911],{"class":810},[474,75978,74900],{"class":480},[474,75980,75981],{"class":484}," ehcache:ehcache:jar:0.9:compile\n",[474,75983,75984,75986,75988,75990],{"class":476,"line":5665},[474,75985,74868],{"class":503},[474,75987,6911],{"class":810},[474,75989,74900],{"class":480},[474,75991,75992],{"class":484}," org.enhydra.xmlc:xmlc:jar:2.2.7.1:compile\n",[474,75994,75995,75997,75999,76001],{"class":476,"line":5676},[474,75996,74868],{"class":503},[474,75998,6911],{"class":810},[474,76000,74900],{"class":480},[474,76002,76003],{"class":484}," com.jgoodies:looks:jar:2.2.2:compile\n",[474,76005,76006,76008,76010,76012],{"class":476,"line":5687},[474,76007,74868],{"class":503},[474,76009,6911],{"class":810},[474,76011,74900],{"class":480},[474,76013,76014],{"class":484}," org.swinglabs:swingx:jar:1.6.1:compile\n",[474,76016,76017,76019,76021,76023,76025],{"class":476,"line":5694},[474,76018,74868],{"class":503},[474,76020,6911],{"class":810},[474,76022,75013],{"class":810},[474,76024,74900],{"class":480},[474,76026,76027],{"class":484}," com.jhlabs:filters:jar:2.0.235:compile\n",[474,76029,76030,76032,76034,76036,76038],{"class":476,"line":5701},[474,76031,74868],{"class":503},[474,76033,6911],{"class":810},[474,76035,75013],{"class":810},[474,76037,74873],{"class":480},[474,76039,76040],{"class":484}," org.swinglabs:swing-worker:jar:1.1:compile\n",[474,76042,76043,76045,76047,76049],{"class":476,"line":5707},[474,76044,74868],{"class":503},[474,76046,6911],{"class":810},[474,76048,74873],{"class":480},[474,76050,76051],{"class":484}," struts:struts:jar:1.2.9:compile\n",[474,76053,76054,76056,76058,76060],{"class":476,"line":5713},[474,76055,74868],{"class":503},[474,76057,6911],{"class":810},[474,76059,75046],{"class":480},[474,76061,76062],{"class":484}," commons-beanutils:commons-beanutils:jar:1.7.0:compile\n",[474,76064,76065,76067,76069,76071],{"class":476,"line":5718},[474,76066,74868],{"class":503},[474,76068,6911],{"class":810},[474,76070,75046],{"class":480},[474,76072,76073],{"class":484}," commons-digester:commons-digester:jar:1.6:compile\n",[474,76075,76076,76078,76080,76082],{"class":476,"line":5724},[474,76077,74868],{"class":503},[474,76079,6911],{"class":810},[474,76081,75046],{"class":480},[474,76083,76084],{"class":484}," commons-fileupload:commons-fileupload:jar:1.0:compile\n",[474,76086,76087,76089,76091,76093],{"class":476,"line":5732},[474,76088,74868],{"class":503},[474,76090,6911],{"class":810},[474,76092,75046],{"class":480},[474,76094,76095],{"class":484}," commons-validator:commons-validator:jar:1.1.4:compile\n",[474,76097,76098,76100,76102,76104],{"class":476,"line":5740},[474,76099,74868],{"class":503},[474,76101,6911],{"class":810},[474,76103,75046],{"class":480},[474,76105,76106],{"class":484}," oro:oro:jar:2.0.7:compile\n",[474,76108,76110,76112,76114,76116],{"class":476,"line":76109},107,[474,76111,74868],{"class":503},[474,76113,6911],{"class":810},[474,76115,75230],{"class":480},[474,76117,76118],{"class":484}," antlr:antlr:jar:2.7.2:compile\n",[474,76120,76122],{"class":476,"line":76121},108,[474,76123,76124],{"class":503},"[INFO] +- junit:junit:jar:4.8.2:test\n",[474,76126,76128,76130,76133],{"class":476,"line":76127},109,[474,76129,74868],{"class":503},[474,76131,76132],{"class":510},"\\-",[474,76134,76135],{"class":503}," org.slf4j:slf4j-api:jar:1.6.1:compile\n",[474,76137,76139],{"class":476,"line":76138},110,[474,76140,76141],{"class":503},"[INFO] ------------------------------------------------------------------------\n",[474,76143,76145],{"class":476,"line":76144},111,[474,76146,76147],{"class":503},"[INFO] BUILD SUCCESSFUL\n",[474,76149,76151],{"class":476,"line":76150},112,[474,76152,76141],{"class":503},[474,76154,76156],{"class":476,"line":76155},113,[474,76157,76158],{"class":503},"[INFO] Total time: 2 seconds\n",[474,76160,76162],{"class":476,"line":76161},114,[474,76163,76164],{"class":503},"[INFO] Finished at: Tue Jun 28 14:10:29 CEST 2011\n",[474,76166,76168],{"class":476,"line":76167},115,[474,76169,76170],{"class":503},"[INFO] Final Memory: 22M/257M\n",[474,76172,76174],{"class":476,"line":76173},116,[474,76175,76141],{"class":503},[439,76177,76178],{},"As described before the elimination of the “rogue” logging class wasn’t a big problem. But we hadn’t reached the finish\nline yet.",[439,76180,76181,76182,76187,76188,76193,76194,76199,76200,76205,76206,76211,76212,76217],{},"The final application is being assembled into one big EAR file which contains a lot\nof ",[1002,76183,76186],{"href":76184,"rel":76185},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch5.chapter.html",[1006],"EJBs",", SAR\nfiles (",[1002,76189,76192],{"href":76190,"rel":76191},"http://community.jboss.org/wiki/ServiceArchive",[1006],"JBoss Service Archives","), and\nthree ",[1002,76195,76198],{"href":76196,"rel":76197},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch9.chapt.html",[1006],"WARs"," (web applications). By default JBoss uses\nits unified class loader (well described in the JBoss Admin Guide in\nsection ",[1002,76201,76204],{"href":76202,"rel":76203},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch2.chapter.html#d0e2490",[1006],"2.2.2.4. Inside the JBoss Class Loading Architecture",")\nwhich is for several reasons not suitable for the architecture of this legacy application (using incompatible versions\nof the same library in different components for example). Thus we wanted to utilize Apache Tomcat’s class loading\nmechanism, described in\nthe ",[1002,76207,76210],{"href":76208,"rel":76209},"http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html",[1006],"Apache Tomcat 5.5 Class Loader HOW-TO",", which\nbasically isolates the deployed web applications from each other. The wiki article\non ",[1002,76213,76216],{"href":76214,"rel":76215},"http://community.jboss.org/wiki/JBossClassLoadingUseCases",[1006],"Advanced JBoss Class Loading"," is also very informative in\nthis context.",[439,76219,76220,76221,76226,76227,76232],{},"Since JBoss 4.0.3.SP1 is using Apache Tomcat 5.5 as servlet container it\nprovides ",[1002,76222,76225],{"href":76223,"rel":76224},"http://community.jboss.org/wiki/UseJBossWebLoader",[1006],"a simple configuration setting"," to achieve exactly the\nabove described behaviour. Unfortunately we encountered a lot of class loading problems when we first deployed the\napplication after the log framework refactoring. We checked the dependencies for each component but everything seemed to\nbe correct. So we started what we do best:\nset ",[1002,76228,76231],{"href":76229,"rel":76230},"http://community.jboss.org/wiki/EnableClassloaderLogging",[1006],"logging to DEBUG"," and engage!",[439,76234,76235,76236,76241,76242,76247,76248,1402],{},"In the end some knee-deep class loader debugging hinted us in the right direction: It’s not advisable to have more than\none instance of the SLF4J JARs in your class path. Basically the class loader was complaining that the classes it was\ntrying to load (e.\ng. ",[1002,76237,76240],{"href":76238,"rel":76239},"http://www.slf4j.org/apidocs/org/slf4j/spi/LoggerFactoryBinder.html",[1006],"org.slf4j.spi.LoggerFactoryBinder",") had already\nbeen loaded from another repository. If you recall the details of Apache\nTomcat’s ",[1002,76243,76246],{"href":76244,"rel":76245},"http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html#Overview",[1006],"class loader"," this makes perfect\nsense but we thought that it was intelligent enough to actually share these classes. As it turns out there’s a (somewhat\nundocumented) configuration option which will exactly do this for us: ",[448,76249,76250],{},"FilteredPackages",[439,76252,76253],{},"After adding the SLF4J package prefix to our jbossweb-tomcat.sar/META-INF/jboss-service.xml file as shown below, the\napplication magically started working again.",[464,76255,76257],{"className":6253,"code":76256,"language":6255,"meta":469,"style":469}," \u003C!-- The list of package prefixes that should not be loaded without\n delegating to the parent class loader before trying the web app\n class loader. The packages listed here are those tha are used by\n the web container implementation and cannot be overriden. The format\n is a comma separated list of the package names. There cannot be any\n whitespace between the package prefixes.\n This setting only applies when UseJBossWebLoader=false.\n -->\n \u003Cattribute name=\"FilteredPackages\">javax.servlet,org.slf4j\u003C/attribute>\n",[471,76258,76259,76264,76269,76274,76279,76284,76289,76294,76299],{"__ignoreMap":469},[474,76260,76261],{"class":476,"line":477},[474,76262,76263],{}," \u003C!-- The list of package prefixes that should not be loaded without\n",[474,76265,76266],{"class":476,"line":507},[474,76267,76268],{}," delegating to the parent class loader before trying the web app\n",[474,76270,76271],{"class":476,"line":547},[474,76272,76273],{}," class loader. The packages listed here are those tha are used by\n",[474,76275,76276],{"class":476,"line":584},[474,76277,76278],{}," the web container implementation and cannot be overriden. The format\n",[474,76280,76281],{"class":476,"line":607},[474,76282,76283],{}," is a comma separated list of the package names. There cannot be any\n",[474,76285,76286],{"class":476,"line":642},[474,76287,76288],{}," whitespace between the package prefixes.\n",[474,76290,76291],{"class":476,"line":663},[474,76292,76293],{}," This setting only applies when UseJBossWebLoader=false.\n",[474,76295,76296],{"class":476,"line":694},[474,76297,76298],{}," -->\n",[474,76300,76301],{"class":476,"line":700},[474,76302,76303],{}," \u003Cattribute name=\"FilteredPackages\">javax.servlet,org.slf4j\u003C/attribute>\n",[3938,76305,76307],{"id":76306},"lessons-learned","Lessons learned",[994,76309,76310,76323,76326,76329,76332,76335,76362],{},[997,76311,76312,76313,76316,76317,76322],{},"If you’re starting a new project choose exactly one logging framework and stick with it. I recommend\nusing ",[1002,76314,74623],{"href":74621,"rel":76315},[1006]," for its stable API and ",[1002,76318,76321],{"href":76319,"rel":76320},"http://logback.qos.ch/",[1006],"Logback"," as backend at the\nmoment.",[997,76324,76325],{},"Don’t create log wrappers if they provide no additional value and if you really, really need to implement your own,\nuse the logging framework’s API instead of thinking up your own.",[997,76327,76328],{},"Dependencies over a lot of distinct components which should be assembled into a single EAR can be quite hairy. Think\nvery well about how to partition your application and which components have common dependencies which can be separated\ninto a parent POM.",[997,76330,76331],{},"The one configuration option that might solve all of your problems is not documented very well. Scan through your (\ncommented) configuration files once in a while.",[997,76333,76334],{},"Use the tools your IDE provides. It makes refactoring so much easier if you know what your IDE is capable of.",[997,76336,76337,76338,520,76343,8351,76348,8351,76353,8351,76358,18773],{},"Never underestimate the grief that class loading issues can cause\nyou. ",[1002,76339,76342],{"href":76340,"rel":76341},"http://onjava.com/lpt/a/5586",[1006],"Really",[1002,76344,76347],{"href":76345,"rel":76346},"http://www.devx.com/Java/Article/31614/1954?pf=true",[1006],"read",[1002,76349,76352],{"href":76350,"rel":76351},"http://onjava.com/lpt/a/4337",[1006],"up",[1002,76354,76357],{"href":76355,"rel":76356},"http://onjava.com/lpt/a/5795",[1006],"on",[1002,76359,8469],{"href":76360,"rel":76361},"http://www.theserverside.com/news/1364680/Understanding-J2EE-Application-Server-ClassLoading-Architectures",[1006],[997,76363,76364],{},"Continuous integration the way we do it is nice but it doesn’t help you with class loading issues appearing in your\nJEE application server.",[439,76366,76367,76368,76371],{},"How about you? Did you experience similar stories with large legacy applications? What did you take from it? We’re\nthrilled to hear ",[448,76369,76370],{},"your"," “Development War Stories” in the comments!",[3938,76373,76375],{"id":76374},"attributions","Attributions",[994,76377,76378],{},[997,76379,76380,74182,76385,520,76390],{},[1002,76381,76384],{"href":76382,"rel":76383},"http://www.flickr.com/photos/lorenjavier/3537572809/",[1006],"Seven Dwarves “Homeward Bound” statue by Jim Shore as soon from the China Closet on Main Street",[1002,76386,76389],{"href":76387,"rel":76388},"http://www.flickr.com/photos/lorenjavier/",[1006],"Loren Javier",[1002,76391,76394],{"href":76392,"rel":76393},"http://creativecommons.org/licenses/by-nd/2.0/",[1006],"CC BY-ND 2.0",[1024,76396,76397],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":469,"searchDepth":507,"depth":507,"links":76399},[76400,76401],{"id":76306,"depth":507,"text":76307},{"id":76374,"depth":507,"text":76375},[1030],"2011-06-30T09:37:07","At Synyx we’re currently taking care of a rather large legacy project for one of our customers in the course of\\nour Code Clinic services. The project comprises several components\\nsuch as a fat client implemented with a custom UI framework on top of Swing, a bulky web application using a mixture of\\ncustom and obsolete frameworks, and a lot of asynchronously running jobs to process input from other systems involving\\ncustom XSL transformations and a heap of stored procedures in a Oracle 9i database. You get the picture, it’s the\\nprototype of a legacy system.","https://synyx.de/blog/the-tale-of-jboss-and-the-7-little-logging-frameworks/",{},"/blog/the-tale-of-jboss-and-the-7-little-logging-frameworks",{"title":74570,"description":76409},"At Synyx we’re currently taking care of a rather large legacy project for one of our customers in the course of\nour Code Clinic services. The project comprises several components\nsuch as a fat client implemented with a custom UI framework on top of Swing, a bulky web application using a mixture of\ncustom and obsolete frameworks, and a lot of asynchronously running jobs to process input from other systems involving\ncustom XSL transformations and a heap of stored procedures in a Oracle 9i database. You get the picture, it’s the\nprototype of a legacy system.","blog/the-tale-of-jboss-and-the-7-little-logging-frameworks",[73058,76412,76413,711,59864,76414,76415,51154],"class-loader","commons-logging","log4j","slf4j","At Synyx we’re currently taking care of a rather large legacy project for one of our customers in the course of our Code Clinic services. The project comprises several components…","QpBEh-GEWijvznNq1tZHIzmrsKlQVYhmSxYzpwcwkYI",{"id":76419,"title":76420,"author":76421,"body":76422,"category":76708,"date":76709,"description":76710,"extension":1034,"link":76711,"meta":76712,"navigation":916,"path":76713,"seo":76714,"slug":76426,"stem":76715,"tags":76716,"teaser":76718,"__hash__":76719},"blog/blog/evaluating-mobile-multiplatform-frameworks.md","Evaluating Mobile Multiplatform Frameworks",[190],{"type":432,"value":76423,"toc":76706},[76424,76427,76430,76457,76460,76475,76478,76481,76484,76541,76550,76688,76691,76694,76697,76700,76703],[435,76425,76420],{"id":76426},"evaluating-mobile-multiplatform-frameworks",[439,76428,76429],{},"For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform\nframeworks that cover at least Android and iOS and provide access to some native API’s like the camera. So I looked at\nseveral of the available frameworks, but only two of them fulfilled all requirements while also providing advantages\ntowards other ones.",[439,76431,76432,76439,76440,76445,76446,76451,76452],{},[1002,76433,76436],{"href":76434,"rel":76435},"https://media.synyx.de/uploads//2011/05/phonegap_symbian.jpg",[1006],[2205,76437],{"alt":76438,"src":76434,"title":76438},"phonegap_logo","\nFirst there is PhoneGap (",[1002,76441,76444],{"href":76442,"rel":76443},"https://web.archive.org/web/20210302121558/https://phonegap.com/",[1006],"http://www.phonegap.com/",")\nwhich additionally provides you a buildservice in their\ncloud (",[1002,76447,76450],{"href":76448,"rel":76449},"https://web.archive.org/web/20210502104931/https://build.phonegap.com/",[1006],"https://build.phonegap.com/"," – currently\nin beta), to which you can upload your project and it builds your Apps for Android, iOS, BlackBerry OS, Palm OS and\nSymbian. PhoneGap furthermore supports Windows Mobile and will be supporting Bada and MeeGo in the future. For an\noverview of the supported features on the different platforms, check this\nsite: ",[1002,76453,76456],{"href":76454,"rel":76455},"https://web.archive.org/web/20120512021720/http://phonegap.com/about/features/",[1006],"http://www.phonegap.com/features",[439,76458,76459],{},"With PhoneGap you write your App in HTML, CSS and Javascript and can also access platform dependent API’s through the\nframework (You can write your own native classes and access them as well, but you have to provide them for every\nplatform). The App runs inside the browser and so it doesn’t feel like an app, but more like a website. Moreover, you\nstyle it like a website, which makes it easier to design it for all the different platforms and display resolutions, and\nPhoneGap lets you even use your own JS-Framworks to do so.",[439,76461,76462,76463,76467,76468],{},"Then there’s Titanium (",[1002,76464,76465],{"href":76465,"rel":76466},"http://www.appcelerator.com/",[1006],"), with which you also write your app in HTML, CSS and JS, but in\nterms of JS you can only use the Titanium provided JS-Framework and no others. It only supports Android and iOS, but\nprovides you access to the native UI elements of both platforms and so doesn’t need to run in the browser, which is a\nbig benefit to the Apps look&feel as well as to the performance. Unfortunately there’s also a downside to that: based\non a few feedbacks from other projects, you have to differ between Android and iOS code on many occasions, because the\nUI elements are different or some of them only work on one of the\nplatforms.",[1002,76469,76472],{"href":76470,"rel":76471},"https://media.synyx.de/uploads//2011/05/titanium_logo.png",[1006],[2205,76473],{"alt":76474,"src":76470,"title":76474},"titanium_logo",[439,76476,76477],{},"And what’s more, I didn’t even get the Titanium showcase App (KitchenSink) running in the first place. At the beginning\nthere were several errors with the provided IDE that is needed to build the Apps. As the errors were cleared (took some\ntime to find the solutions in the forum and elsewhere), I could build the App, but it only got to the splashscreen – and\nthen it did nothing. I then decided to download it from someone who built it some time ago and it ran very smoothly and\nprovided access to all kind of features. But nevertheless, I couldn’t try it out myself, because it didn’t work for me.",[439,76479,76480],{},"So only PhoneGap was left and I decided to compare it effort- and performance wise with a Java coded Android App. I\nimplemented some basic elements in both apps, like Textfields, Lists (with much data, because the upcoming project will\nmost likely need that), dynamic SelectFields, Images, and of course multiple Activities/Pages.",[439,76482,76483],{},"What I noticed immediately is the difference in responsiveness of the elements. The ones in the browser need remarkably\nlonger to react on the users touch. Other than that, the App by itselfs needs much longer to load with PhoneGap. This\ncan be traced to the way transition animations are done in PhoneGap, according to the PhoneGap tutorials:",[464,76485,76487],{"className":15039,"code":76486,"language":15041,"meta":469,"style":469},"\u003Cdiv class=\"page\" id=\"page1\">...\u003C/div>\n\u003Cdiv class=\"page\" id=\"page2\">...\u003C/div>\n",[471,76488,76489,76516],{"__ignoreMap":469},[474,76490,76491,76493,76495,76497,76499,76502,76504,76506,76509,76512,76514],{"class":476,"line":477},[474,76492,15048],{"class":503},[474,76494,15027],{"class":15051},[474,76496,61709],{"class":480},[474,76498,811],{"class":503},[474,76500,76501],{"class":484},"\"page\"",[474,76503,15185],{"class":480},[474,76505,811],{"class":503},[474,76507,76508],{"class":484},"\"page1\"",[474,76510,76511],{"class":503},">...\u003C/",[474,76513,15027],{"class":15051},[474,76515,15070],{"class":503},[474,76517,76518,76520,76522,76524,76526,76528,76530,76532,76535,76537,76539],{"class":476,"line":507},[474,76519,15048],{"class":503},[474,76521,15027],{"class":15051},[474,76523,61709],{"class":480},[474,76525,811],{"class":503},[474,76527,76501],{"class":484},[474,76529,15185],{"class":480},[474,76531,811],{"class":503},[474,76533,76534],{"class":484},"\"page2\"",[474,76536,76511],{"class":503},[474,76538,15027],{"class":15051},[474,76540,15070],{"class":503},[439,76542,76543,76544,76549],{},"You have to declare the different pages in the same HTML file, with all but one styled as “display: none”. Then you\nswitch between them via javascript (",[1002,76545,76548],{"href":76546,"rel":76547},"https://jqueryui.com/",[1006],"jQueryUI","‘s hide- and show-methods for example).",[464,76551,76553],{"className":15199,"code":76552,"language":15201,"meta":469,"style":469},"function switchView(hideView, showView, hideDirection, showDirection) {\n $(\"#\" + hideView).hide(\n \"slide\",\n {\n direction: hideDirection,\n },\n 250,\n );\n $(\"#\" + showView).show(\n \"slide\",\n {\n direction: showDirection,\n },\n 250,\n );\n}\n",[471,76554,76555,76584,76604,76611,76615,76620,76625,76632,76637,76655,76661,76665,76670,76674,76680,76684],{"__ignoreMap":469},[474,76556,76557,76559,76562,76564,76567,76569,76572,76574,76577,76579,76582],{"class":476,"line":477},[474,76558,14021],{"class":810},[474,76560,76561],{"class":480}," switchView",[474,76563,1483],{"class":503},[474,76565,76566],{"class":14037},"hideView",[474,76568,520],{"class":503},[474,76570,76571],{"class":14037},"showView",[474,76573,520],{"class":503},[474,76575,76576],{"class":14037},"hideDirection",[474,76578,520],{"class":503},[474,76580,76581],{"class":14037},"showDirection",[474,76583,23418],{"class":503},[474,76585,76586,76589,76591,76594,76596,76599,76602],{"class":476,"line":507},[474,76587,76588],{"class":480}," $",[474,76590,1483],{"class":503},[474,76592,76593],{"class":484},"\"#\"",[474,76595,46303],{"class":810},[474,76597,76598],{"class":503}," hideView).",[474,76600,76601],{"class":480},"hide",[474,76603,1555],{"class":503},[474,76605,76606,76609],{"class":476,"line":547},[474,76607,76608],{"class":484}," \"slide\"",[474,76610,4715],{"class":503},[474,76612,76613],{"class":476,"line":584},[474,76614,4796],{"class":503},[474,76616,76617],{"class":476,"line":607},[474,76618,76619],{"class":503}," direction: hideDirection,\n",[474,76621,76622],{"class":476,"line":642},[474,76623,76624],{"class":503}," },\n",[474,76626,76627,76630],{"class":476,"line":663},[474,76628,76629],{"class":510}," 250",[474,76631,4715],{"class":503},[474,76633,76634],{"class":476,"line":694},[474,76635,76636],{"class":503}," );\n",[474,76638,76639,76641,76643,76645,76647,76650,76653],{"class":476,"line":700},[474,76640,76588],{"class":480},[474,76642,1483],{"class":503},[474,76644,76593],{"class":484},[474,76646,46303],{"class":810},[474,76648,76649],{"class":503}," showView).",[474,76651,76652],{"class":480},"show",[474,76654,1555],{"class":503},[474,76656,76657,76659],{"class":476,"line":913},[474,76658,76608],{"class":484},[474,76660,4715],{"class":503},[474,76662,76663],{"class":476,"line":920},[474,76664,4796],{"class":503},[474,76666,76667],{"class":476,"line":926},[474,76668,76669],{"class":503}," direction: showDirection,\n",[474,76671,76672],{"class":476,"line":932},[474,76673,76624],{"class":503},[474,76675,76676,76678],{"class":476,"line":938},[474,76677,76629],{"class":510},[474,76679,4715],{"class":503},[474,76681,76682],{"class":476,"line":944},[474,76683,76636],{"class":503},[474,76685,76686],{"class":476,"line":950},[474,76687,703],{"class":503},[439,76689,76690],{},"The problem in this case is, that multiple “pages” are being loaded right at the beginning, increasing the load time.\nAlso the transition animation with JavaScript isn’t that smooth – even with high-end smartphones.",[439,76692,76693],{},"Comparing the speed of development of my sample App, I was a little faster with PhoneGap than with the Java code. In\nterms of speed, PhoneGap is especially useful, if you’re developing your App for mulitple platforms – you will most\nlikely be a few times faster than developing them natively on each platform and you also don’t have to set up the\ndifferent IDE’s if you are using PhoneGap Build on top of that.",[439,76695,76696],{},"Like I already mentioned, the performance isn’t that great, though. I assume that you probably won’t use PhoneGap – or\nhtml and js in general – for larger Apps which you only need for one or two platforms. This is because the\nmaintainability and testability are (from my point of view as a java developer) much better with java and also in other\nlanguages than they are with javascript and html+css (Well, you DO have only one codebase here, but therefor have a set\nof different mobile browsers that you need to check it with).",[439,76698,76699],{},"In my opinion, PhoneGap only comes in handy if you want to distribute a small App for as many platforms as possible\nwhile keeping the needed effort as low as possible.",[439,76701,76702],{},"In our case, we decided not to use PhoneGap or another framework for the project, because it needs to perform well and\nwill be a bigger project where the maintainability is very important. Besides, it will only have to run on Android and (\nmaybe) iOS, which doesn’t make the benefit you gain from PhoneGap that great.",[1024,76704,76705],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":469,"searchDepth":507,"depth":507,"links":76707},[],[11122],"2011-06-27T08:52:15","For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform\\nframeworks that cover at least Android and iOS and provide access to some native API’s like the camera. So I looked at\\nseveral of the available frameworks, but only two of them fulfilled all requirements while also providing advantages\\ntowards other ones.","https://synyx.de/blog/evaluating-mobile-multiplatform-frameworks/",{},"/blog/evaluating-mobile-multiplatform-frameworks",{"title":76420,"description":76429},"blog/evaluating-mobile-multiplatform-frameworks",[11132,63947,18496,76717],"phonegap","For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform frameworks that cover at least Android and iOS and provide access…","Jw_4N2uUHE03f4LmsIlQhHj05IVjssClu6IMLKTKM5w",{"id":76721,"title":76722,"author":76723,"body":76724,"category":76757,"date":76758,"description":76759,"extension":1034,"link":76760,"meta":76761,"navigation":916,"path":76762,"seo":76763,"slug":76764,"stem":76765,"tags":76766,"teaser":76769,"__hash__":76770},"blog/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter.md","Eine Kochmuddi für hungrige Synyx-Mitarbeiter",[172],{"type":432,"value":76725,"toc":76755},[76726,76729,76739,76742,76745,76748],[435,76727,76722],{"id":76728},"eine-kochmuddi-für-hungrige-synyx-mitarbeiter",[439,76730,76731,76738],{},[1002,76732,76735],{"href":76733,"rel":76734},"https://media.synyx.de/uploads//2011/06/mellie.jpg",[1006],[2205,76736],{"alt":76737,"src":76733,"title":76737},"Kochmellie beim ersten Synyx-Einsatz","\nWer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer\ngrößer. Der Magen grummelt. Die Konzentration lässt nach. Und dann stellt sich wie immer die Frage: „Was sollen wir denn\nheute Mittag essen?“",[439,76740,76741],{},"Ständig belegte Brötchen – das ist auf Dauer langweilig. Essen gehen – schön und gut, aber recht kostenintensiv und\nzeitaufwendig. Daher kochen bei Synyx ab und an Mitarbeiter für Mitarbeiter zum Selbstkostenpreis. Das macht zwar Spaß,\naber auch das kostet viel Zeit.",[439,76743,76744],{},"Aus diesem Grund haben die Synyx Chefs uns Mitarbeitern mal wieder was echt Gutes gegönnt: eine Kochmuddi! Zwei bis drei\nMal die Woche kommt Mellie, die Kochmuddi oder besser gesagt die Kochmellie ;-), bei uns vorbei und verwöhnt uns mit\nihren Kochkünsten.",[439,76746,76747],{},"Es gibt eine vegetarische und eine fleischhaltige Variante zum Selbstkostenpreis. Heute stand auf dem „Menüplan“ Nudeln\nmit Bolognese oder Nudeln mit Tomate-Zucchini-Soße. Dazu gab es Salat. Das Essen war ausgesprochen lecker. Weiter so!",[439,76749,76750],{},[1002,76751,76754],{"href":76752,"rel":76753},"http://www.hypersmash.com/dreamhost/",[1006],"Dreamhost promotion code",{"title":469,"searchDepth":507,"depth":507,"links":76756},[],[1031],"2011-06-14T13:35:06","\\nWer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer\\ngrößer. Der Magen grummelt. Die Konzentration lässt nach. Und dann stellt sich wie immer die Frage: „Was sollen wir denn\\nheute Mittag essen?“","https://synyx.de/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter/",{},"/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter",{"title":76722,"description":76738},"eine-kochmuddi-fur-hungrige-synyx-mitarbeiter","blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter",[76767,76768,389],"kochen","mitarbeiter","Wer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer größer. Der Magen grummelt. Die Konzentration lässt nach. Und…","Hpkv_-LnfJxqAKwJSz7xTNpCP4aTUZhpkI4TwbccEq8",{"id":76772,"title":76773,"author":76774,"body":76775,"category":76810,"date":76811,"description":76812,"extension":1034,"link":76813,"meta":76814,"navigation":916,"path":76815,"seo":76816,"slug":76779,"stem":76818,"tags":76819,"teaser":76820,"__hash__":76821},"blog/blog/opencms-modul-integriert-externe-blogs.md","OpenCms Modul integriert externe Blogs",[172],{"type":432,"value":76776,"toc":76808},[76777,76780,76795],[435,76778,76773],{"id":76779},"opencms-modul-integriert-externe-blogs",[439,76781,76782,76789,76790,76794],{},[1002,76783,76786],{"href":76784,"rel":76785},"https://media.synyx.de/uploads//2011/05/opencms-modul1.jpg",[1006],[2205,76787],{"alt":76788,"src":76784,"title":76788},"opencms-modul","\nDie Synyx Hompage wird mittels dem Content Management System ",[1002,76791,71236],{"href":76792,"rel":76793},"http://www.opencms.org/de/",[1006]," umgesetzt; unser Blog\nmit WordPress. Um neue Blogposts auch auf der Homepage anzuzeigen, musste bisher beides parallel bearbeitet und gepflegt\nwerden. Das ist umständlich und kostet viel Zeit.",[439,76796,8663,76797,76801,76802,76807],{},[1002,76798,76800],{"href":13089,"rel":76799},[1006],"Synyx OpenCms Team"," hat nun Abhilfe geschaffen. Sie entwickelten ein Modul\nfür OpenCms, welches die Aggregation, bzw. Integration von externen Blogs ins System ermöglicht. So werden jetzt auf\nder ",[1002,76803,76806],{"href":76804,"rel":76805},"https://synyx.de/de/",[1006],"Startseite unserer Homepage"," automatisch alle Blogs angezeigt. Der Synyx Blog hat drei\nunterschiedliche Blogkategorien. Damit dies auf der Homepage übersichtlich dargestellt wird, gibt es auch hier drei\nGruppierungen.",{"title":469,"searchDepth":507,"depth":507,"links":76809},[],[1031],"2011-05-30T12:41:00","\\nDie Synyx Hompage wird mittels dem Content Management System OpenCms umgesetzt; unser Blog\\nmit WordPress. Um neue Blogposts auch auf der Homepage anzuzeigen, musste bisher beides parallel bearbeitet und gepflegt\\nwerden. Das ist umständlich und kostet viel Zeit.","https://synyx.de/blog/opencms-modul-integriert-externe-blogs/",{},"/blog/opencms-modul-integriert-externe-blogs",{"title":76773,"description":76817},"\nDie Synyx Hompage wird mittels dem Content Management System OpenCms umgesetzt; unser Blog\nmit WordPress. Um neue Blogposts auch auf der Homepage anzuzeigen, musste bisher beides parallel bearbeitet und gepflegt\nwerden. Das ist umständlich und kostet viel Zeit.","blog/opencms-modul-integriert-externe-blogs",[64517,389],"Die Synyx Hompage wird mittels dem Content Management System OpenCms umgesetzt; unser Blog mit WordPress. Um neue Blogposts auch auf der Homepage anzuzeigen, musste bisher beides parallel bearbeitet und gepflegt…","VbWfP0zXmrGke9jEYih906Rq8uKjj-pX_4PXXrDeYco",{"id":76823,"title":76824,"author":76825,"body":76826,"category":76886,"date":76887,"description":76888,"extension":1034,"link":76889,"meta":76890,"navigation":916,"path":76891,"seo":76892,"slug":76830,"stem":76894,"tags":76895,"teaser":76896,"__hash__":76897},"blog/blog/solr-as-search-engine-for-opencms.md","Solr as search engine for OpenCms",[148],{"type":432,"value":76827,"toc":76884},[76828,76831,76846,76860,76863,76872,76881],[435,76829,76824],{"id":76830},"solr-as-search-engine-for-opencms",[439,76832,76833,76834,76839,76840,76845],{},"Matching the time of my talk at this years ",[1002,76835,76838],{"href":76836,"rel":76837},"http://www.opencms-days.org/en/index.html",[1006],"OpenCms Days"," we released our\nmodule for integrating ",[1002,76841,76844],{"href":76842,"rel":76843},"https://github.com/synyx/opencms-solr-module",[1006],"Solr with OpenCms",". A few days have passed now and\nwe had the time to polish the documentation and some aspects of the module.",[439,76847,76848,76853,76854,76859],{},[1002,76849,76852],{"href":76850,"rel":76851},"http://lucene.apache.org/solr/",[1006],"Solr"," is a search server that is based on the de facto standard for indexing in\nJava, ",[1002,76855,76858],{"href":76856,"rel":76857},"http://lucene.apache.org/",[1006],"Apache Lucene",". It provides an abstraction layer above the low level details of\nindexing and adds some useful features like facetting and synonyms.",[439,76861,76862],{},"Solr is integrated transparently as an OpenCms index and can be used and mixed with common Lucene indexes. Communication\nis done via HTTP, Solr is accessed using a REST based interface.",[439,76864,76865,76866,76871],{},"We provide two ways to try the module: You can\neither ",[1002,76867,76870],{"href":76868,"rel":76869},"https://github.com/Synyx/opencms-solr-module",[1006],"checkout an example application"," that you can start up immediately\nusing the OpenCms demo application TemplateTwo. This is the best place if you want to play with some configuration\noptions and just see how all of it works.",[439,76873,76874,76875,76880],{},"Another way is\nthe ",[1002,76876,76879],{"href":76877,"rel":76878},"https://github.com/synyx/opencms-solr-module/wiki/Integrating-Solr-into-an-existing-application",[1006],"integration in an existing application which is also described in detail",".\nThis is what you would do if you really want to use it in production.",[439,76882,76883],{},"Please feel free to post any questions or feature requests to the issue tracker.",{"title":469,"searchDepth":507,"depth":507,"links":76885},[],[1030,1412],"2011-05-26T15:01:54","Matching the time of my talk at this years OpenCms Days we released our\\nmodule for integrating Solr with OpenCms. A few days have passed now and\\nwe had the time to polish the documentation and some aspects of the module.","https://synyx.de/blog/solr-as-search-engine-for-opencms/",{},"/blog/solr-as-search-engine-for-opencms",{"title":76824,"description":76893},"Matching the time of my talk at this years OpenCms Days we released our\nmodule for integrating Solr with OpenCms. A few days have passed now and\nwe had the time to polish the documentation and some aspects of the module.","blog/solr-as-search-engine-for-opencms",[26636,64517,64519],"Matching the time of my talk at this years OpenCms Days we released our module for integrating Solr with OpenCms. A few days have passed now and we had the…","B1O9VoFEtSOcrvIZm-OgWICsh3-nJM9l4WPhJ44dmW8",{"id":76899,"title":76900,"author":76901,"body":76902,"category":76921,"date":76922,"description":76923,"extension":1034,"link":76924,"meta":76925,"navigation":916,"path":76926,"seo":76927,"slug":76928,"stem":76929,"tags":76930,"teaser":76935,"__hash__":76936},"blog/blog/opensource-is-not-just-about-the-license.md","Being Open Source instead of just Open-Sourcing or Open Source is not just about the license",[27],{"type":432,"value":76903,"toc":76919},[76904,76907,76910,76913],[435,76905,76900],{"id":76906},"being-open-source-instead-of-just-open-sourcing-or-open-source-is-not-just-about-the-license",[439,76908,76909],{},"Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community\nthat matters, not just users, strong leaders that listen and still communicate their vision and goal, growing base of\ncontributors who don’t want to be ignored, even if there’s a benevolent dictator as project lead.",[439,76911,76912],{},"There are some popular Open Source projects which recently failed in some of these areas and got forked. Synyx decided\nto go with the forks even though we are mainly users and in these cases not main contributors. What’s the reason behind\nthese decisions? Our company vision tells us to live Open Source which includes much more than what it looks on its\nsurface. It’s not a goal in itself. We believe openness, communication and trancparency are key to quality software,\nespecially long term. That leads to more deliverable value and improved competitiveness as well as more transparency to\nthe community behind.",[439,76914,76915],{},[2205,76916],{"alt":469,"src":76917,"title":76918},"https://media.synyx.de/uploads//2011/05/chili-300x199.jpg","chili",{"title":469,"searchDepth":507,"depth":507,"links":76920},[],[1030,1412],"2011-05-20T17:01:44","Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community\\nthat matters, not just users, strong leaders that listen and still communicate their vision and goal, growing base of\\ncontributors who don’t want to be ignored, even if there’s a benevolent dictator as project lead.","https://synyx.de/blog/opensource-is-not-just-about-the-license/",{},"/blog/opensource-is-not-just-about-the-license",{"title":76900,"description":76909},"opensource-is-not-just-about-the-license","blog/opensource-is-not-just-about-the-license",[76931,7288,76932,44013,76933,26636,69101,76934],"chiliproject","hudson","libreoffice","transparency","Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community that matters, not just users, strong leaders that listen and still communicate…","CCudIs0-B2Q3HB1MJjr6jXo1c__MAyAy4luOsfGU9SQ",{"id":76938,"title":76939,"author":76940,"body":76941,"category":77072,"date":77073,"description":77074,"extension":1034,"link":77075,"meta":77076,"navigation":916,"path":77077,"seo":77078,"slug":76945,"stem":77080,"tags":77081,"teaser":77082,"__hash__":77083},"blog/blog/opencms-days-2011-in-retrospect.md","OpenCms Days 2011 in retrospect",[148],{"type":432,"value":76942,"toc":77070},[76943,76946,76954,76969,76972,76975,76990,76999,77020,77046,77064,77067],[435,76944,76939],{"id":76945},"opencms-days-2011-in-retrospect",[439,76947,76948,76949,76953],{},"From May 9 2011 to May 10 the ",[1002,76950,76952],{"href":76836,"rel":76951},[1006],"third OpenCms Days"," took place in Cologne. The\ntopic of the conference was “The OpenCms 8 User Experience”, targeting the release of OpenCms 8 with its advanced direct\nedit (ADE) functionality. We at Synyx, being a sponsor for the third conference in a row, have been looking forward to\nboth of these events, the conference and the release of OpenCms 8 for quite some time. In this blogpost I want to\nmention some of the highlights of the conference, in another blogpost I will describe some of the new features of\nOpenCms 8.",[439,76955,76956,76957,76962,76963,76968],{},"The conference again took place at ",[1002,76958,76961],{"href":76959,"rel":76960},"http://www.komed.de/",[1006],"KOMED"," which is suited quite well for the amount of people\nthat are attending. There were two tracks, one showcase track where different companies from the community presented\nsolutions they build around OpenCms and a workshop track that was exclusively held\nby ",[1002,76964,76967],{"href":76965,"rel":76966},"http://www.alkacon.com/en/",[1006],"Alkacon"," employees, mainly targeting new features of OpenCms 8.",[439,76970,76971],{},"The conference started off with the first public presentation of OpenCms 8 in Alexander Kandziors keynote. A lot of\npeople have been looking forward to this moment as the development of the new version was conducted in a private version\ncontrol system so this was the first time to see the new features. The new interface looks really promising and seems to\nbe received well by the audience.",[439,76973,76974],{},"I will not go into detail on the workshop track sessions, the new features of OpenCms will be described in another post.\nIn retrospect I think it was not ideal that on the first day I went to workshop tracks nearly exclusively. All the\nAlkacon team members prepared their slides in a way that makes it easy to take those as a kind of reference manual,\nwhich is good, as you can come back to the slides later and have a look at those to learn the new features.",[439,76976,76977,76978,76983,76984,76989],{},"I heard a lot of good things about the talk on optimizing OpenCms performance using Varnish but that’s one I didn’t\nattend. The single showcase track I went to on the first day was on ",[1002,76979,76982],{"href":76980,"rel":76981},"http://www.softwareag.com/",[1006],"Software AG"," and the\nmodules they build together with ",[1002,76985,76988],{"href":76986,"rel":76987},"http://www.componio.net/",[1006],"Componio",". Quite impressive how much work they seem to have\ninvested into implementing a lot of modules and extensions.",[439,76991,76992,76993,76998],{},"The first day ended with the Get-Together at Kandinsky, a restaurant just next to the conference location. The food was\nreally good, a nice location and nice old and new contacts to chat with. Also a big thanks goes out to Alkacon who in\ncelebration of the release of OpenCms 8 supplied us with free (",[1002,76994,76997],{"href":76995,"rel":76996},"http://www.gnu.org/philosophy/free-sw.html",[1006],"as in beer",")\nbeer :).",[439,77000,77001,77002,77007,77008,77013,77014,77019],{},"On the second day I went only to talks in the showcase track. Selver Softic of ",[1002,77003,77006],{"href":77004,"rel":77005},"http://www.infonova.com/",[1006],"Infonova","\npresented an Open Source module that they are using for implementing\nthe ",[1002,77009,77012],{"href":77010,"rel":77011},"http://telekom.at/",[1006],"website of Austrian telekom",". The module makes it possible to\nuse ",[1002,77015,77018],{"href":77016,"rel":77017},"http://velocity.apache.org/",[1006],"Velocity"," as the templating language for OpenCms. Velocity provides an easier syntax\nthan JSPs and is better suited for a lot of web developers. I am not sure if we will use it in a project in the near\nfuture but it’s often handy to know that something like this is there when you need it.",[439,77021,77022,77023,77028,77029,77034,77035,77040,77041,77045],{},"Another interesting Open Source module was presented by Rich Cooley of ",[1002,77024,77027],{"href":77025,"rel":77026},"http://www.northps.com/",[1006],"Northpoint Solutions",".\nThe idea behind the module is quite simple but it can be very useful when dealing with large assets. You can declare\nsome assets to reside outside of the Virtual File System which stores its content in the database. The assets are stored\nin the normal filesystem but parts of its content are extracted via the library ",[1002,77030,77033],{"href":77031,"rel":77032},"http://tika.apache.org/",[1006],"Apache Tika","\nand stored in OpenCms. This is definitively something I will try out in the near future. Also quite interesting that\nthey are also using the search engine Lucene for content aggregation for\nexample ",[1002,77036,77039],{"href":77037,"rel":77038},"http://www.chop.edu/",[1006],"for the glossary on the website they implemented the module for",". This is something where\nour ",[1002,77042,77044],{"href":76842,"rel":77043},[1006],"Solr integration"," would probably also be a benefit.",[439,77047,77048,77049,77052,77053,77057,77058,77063],{},"Speaking of Solr, did I miss something? Oh yes, there was also my talk on our integration\nof ",[1002,77050,76852],{"href":76850,"rel":77051},[1006]," with OpenCms which is\navailable ",[1002,77054,77056],{"href":76842,"rel":77055},[1006],"as an Open Source module",". Though there have been some\ntechnical difficulties (probably I can refer to those as “the slide incident”) the talk seems to be received really\nwell. A lot of people confirmed that this is an interesting topic that will become more and more important in the\nfuture. I will try to enhance the ",[1002,77059,77062],{"href":77060,"rel":77061},"https://github.com/synyx/opencms-solr-module/wiki",[1006],"documentation of the module"," in\nthe next days and probably write another blog post going more into some of the details.",[439,77065,77066],{},"Finally, looking at the layout of the conference, I think the separation into workshop and showcase tracks is not as\ngood as in the years before, where the split was between business and technical tracks. A lot of the showcase sessions\nhad a rather technical content so there was no place to go for the business people. Also probably more people would have\nattended the technical showcase talks if they would have been aware that those might also be of interest to them.",[439,77068,77069],{},"All in all the conference was really worthwile as it has been the years before. Alkacon is doing a great job in\norganizing the conference and I hope it will become an annual event.",{"title":469,"searchDepth":507,"depth":507,"links":77071},[],[1412],"2011-05-17T14:56:01","From May 9 2011 to May 10 the third OpenCms Days took place in Cologne. The\\ntopic of the conference was “The OpenCms 8 User Experience”, targeting the release of OpenCms 8 with its advanced direct\\nedit (ADE) functionality. We at Synyx, being a sponsor for the third conference in a row, have been looking forward to\\nboth of these events, the conference and the release of OpenCms 8 for quite some time. In this blogpost I want to\\nmention some of the highlights of the conference, in another blogpost I will describe some of the new features of\\nOpenCms 8.","https://synyx.de/blog/opencms-days-2011-in-retrospect/",{},"/blog/opencms-days-2011-in-retrospect",{"title":76939,"description":77079},"From May 9 2011 to May 10 the third OpenCms Days took place in Cologne. The\ntopic of the conference was “The OpenCms 8 User Experience”, targeting the release of OpenCms 8 with its advanced direct\nedit (ADE) functionality. We at Synyx, being a sponsor for the third conference in a row, have been looking forward to\nboth of these events, the conference and the release of OpenCms 8 for quite some time. In this blogpost I want to\nmention some of the highlights of the conference, in another blogpost I will describe some of the new features of\nOpenCms 8.","blog/opencms-days-2011-in-retrospect",[8276,64517,64519],"From May 9 2011 to May 10 the third OpenCms Days took place in Cologne. The topic of the conference was “The OpenCms 8 User Experience”, targeting the release of…","b-YpbhnXuLLNNPkBPQOw0y-8PfoBWh3v0TXYjMdCmWY",{"id":77085,"title":77086,"author":77087,"body":77088,"category":77111,"date":77112,"description":77113,"extension":1034,"link":77114,"meta":77115,"navigation":916,"path":77116,"seo":77117,"slug":77092,"stem":77118,"tags":77119,"teaser":77121,"__hash__":77122},"blog/blog/aus-zwei-mach-eins.md","Aus Zwei mach Eins",[172],{"type":432,"value":77089,"toc":77109},[77090,77093,77103,77106],[435,77091,77086],{"id":77092},"aus-zwei-mach-eins",[439,77094,77095,77102],{},[1002,77096,77099],{"href":77097,"rel":77098},"https://media.synyx.de/uploads//2011/04/zusammenlegung2.jpg",[1006],[2205,77100],{"alt":77101,"src":77097,"title":77101},"zusammenlegung","\nDen Synyx Corporate Blog und den Mobile Blog gibt es nun über ein Jahr. Bisher liefen beide Blogs eigenständig. Wir\nhaben nun den Mobile Blog in den Corporate Blog integriert. So haben die Leser alle Informationen auf einen Blick und\nman muss nicht mehr zwischen zwei Blogs hin- und herklicken.",[439,77104,77105],{},"Der Mobile Blog war bisher nur in englischer Sprache verfasst. Der Corporate Blog ist zweisprachig. Unternehmensnews\nwerden auf Deutsch verfasst, Blogs aus dem Developer Bereich auf Englisch. Um euch hier auch mehr Komfort zu bieten,\nwird es in Zukunft eine Sprachumschaltung geben, da manche Posts in Englisch und Deutsch verfügbar sein werden.",[439,77107,77108],{},"Ich bin gespannt, wie euch die Idee gefällt und wie ihr damit zurechtkommt.",{"title":469,"searchDepth":507,"depth":507,"links":77110},[],[1031],"2011-05-17T10:19:56","\\nDen Synyx Corporate Blog und den Mobile Blog gibt es nun über ein Jahr. Bisher liefen beide Blogs eigenständig. Wir\\nhaben nun den Mobile Blog in den Corporate Blog integriert. So haben die Leser alle Informationen auf einen Blick und\\nman muss nicht mehr zwischen zwei Blogs hin- und herklicken.","https://synyx.de/blog/aus-zwei-mach-eins/",{},"/blog/aus-zwei-mach-eins",{"title":77086,"description":77102},"blog/aus-zwei-mach-eins",[77120,389],"blog","Den Synyx Corporate Blog und den Mobile Blog gibt es nun über ein Jahr. Bisher liefen beide Blogs eigenständig. Wir haben nun den Mobile Blog in den Corporate Blog integriert.…","brNQYKyNVTCS-dRDALs25IkjrD8x2HYY1I57bKO5-0U",{"id":77124,"title":77125,"author":77126,"body":77127,"category":77167,"date":77168,"description":469,"extension":1034,"link":77169,"meta":77170,"navigation":916,"path":77171,"seo":77172,"slug":77131,"stem":77173,"tags":77174,"teaser":77175,"__hash__":77176},"blog/blog/vortrag-von-synyx-entwickler-bei-den-opencms-days.md","Vortrag von Synyx Entwickler bei den OpenCms Days",[172],{"type":432,"value":77128,"toc":77165},[77129,77132,77141,77162],[435,77130,77125],{"id":77131},"vortrag-von-synyx-entwickler-bei-den-opencms-days",[439,77133,77134],{},[1002,77135,77138],{"href":77136,"rel":77137},"https://media.synyx.de/uploads//2011/05/logo_opencmsdays_2011.jpg",[1006],[2205,77139],{"alt":77140,"src":77136,"title":77140},"Logo OpenCms Days 2011",[439,77142,77143,77144,77147,77148,77152,77153,77157,77158,77161],{},"Synyx wird wieder die ",[1002,77145,76838],{"href":76836,"rel":77146},[1006]," besuchen. Dieses Mal hält Florian Hopf\neinen ",[1002,77149,33065],{"href":77150,"rel":77151},"http://www.opencms-days.org/en/program/sessions/session_s5.html",[1006]," über ein Open Source Modul für OpenCms.\nDieses Modul integriert Solr in OpenCms. Der Such Server ",[1002,77154,77156],{"href":76850,"rel":77155},[1006],"Apache Solr"," setzt auf der\nbewährten IR-Bibliothek ",[1002,77159,76858],{"href":76856,"rel":77160},[1006]," auf.",[439,77163,77164],{},"Der Vortrag ist am 10.Mai von 10:30-11:30 Uhr. Wir sind schon gespannt. Florian Hopf wird nach den OpenCms Days noch\neinen Blogpost dazu verfassen.",{"title":469,"searchDepth":507,"depth":507,"links":77166},[],[1031],"2011-05-08T08:23:26","https://synyx.de/blog/vortrag-von-synyx-entwickler-bei-den-opencms-days/",{},"/blog/vortrag-von-synyx-entwickler-bei-den-opencms-days",{"title":77125,"description":469},"blog/vortrag-von-synyx-entwickler-bei-den-opencms-days",[64517],"Synyx wird wieder die OpenCms Days besuchen. Dieses Mal hält Florian Hopf einen Vortrag über ein Open Source Modul für OpenCms. Dieses Modul integriert Solr in OpenCms. Der Such Server…","C8inKWUzDhhglHU_JsYFxCeEBaEy1JeuDzLc8c9Id7w",{"id":77178,"title":77179,"author":77180,"body":77181,"category":77212,"date":77213,"description":469,"extension":1034,"link":77214,"meta":77215,"navigation":916,"path":77216,"seo":77217,"slug":77218,"stem":77219,"tags":77220,"teaser":77223,"__hash__":77224},"blog/blog/open-source-anwendungen-fur-die-community.md","Open Source Anwendungen für die Community",[172],{"type":432,"value":77182,"toc":77210},[77183,77186,77195,77198,77201,77204],[435,77184,77179],{"id":77185},"open-source-anwendungen-für-die-community",[439,77187,77188],{},[1002,77189,77192],{"href":77190,"rel":77191},"https://media.synyx.de/uploads//2011/05/opensourceprojekte.jpg",[1006],[2205,77193],{"alt":77194,"src":77190,"title":77194},"Open Source Projekte",[439,77196,77197],{},"Synyx entwickelt bereits seit Jahren Anwendungen und Programme auf Basis von Open Source. Rund 20 Mitarbeiter\nentwickeln, optimieren und leben Open Source Anwendungen. Denn wer Open Source Solutions programmiert, lebt eine\nPhilosophie.",[439,77199,77200],{},"Einige im Laufe der Zeit selbst entwickelten Tools stellt Synyx der Communitiy wieder zur Verfügung. Darunter sind\nsowohl selbstfinanzierte Entwicklungen als auch Tools die ursprünglich im Kundenauftrag entwickelt wurden und welche von\ndiesen als Dank für die Community frei zugänglich gemacht wurden.",[439,77202,77203],{},"Wir haben auf unserer Homepage alle zur Zeit verfügbaren Anwendungen zusammengestellt. Ihr findet dort unter anderem\nInformationen zu den einzelnen Tools und Links zum Quellcode.",[439,77205,77206],{},[1002,77207,77209],{"href":26561,"rel":77208},[1006],"Link zur Homepage und den Tools",{"title":469,"searchDepth":507,"depth":507,"links":77211},[],[1412],"2011-05-04T11:14:29","https://synyx.de/blog/open-source-anwendungen-fur-die-community/",{},"/blog/open-source-anwendungen-fur-die-community",{"title":77179,"description":469},"open-source-anwendungen-fur-die-community","blog/open-source-anwendungen-fur-die-community",[77221,26636,77222],"anwendungen","quellcode","Synyx entwickelt bereits seit Jahren Anwendungen und Programme auf Basis von Open Source. Rund 20 Mitarbeiter entwickeln, optimieren und leben Open Source Anwendungen. Denn wer Open Source Solutions programmiert, lebt…","_Po1q2cWDRnPtQTsw93ftp-rj4dcNaTb7ox65mnmfIw",{"id":77226,"title":77227,"author":77228,"body":77229,"category":77287,"date":77288,"description":77289,"extension":1034,"link":77290,"meta":77291,"navigation":916,"path":77292,"seo":77293,"slug":77233,"stem":77294,"tags":77295,"teaser":77296,"__hash__":77297},"blog/blog/happy-birthday-synyx-corporate-blog.md","Happy Birthday Synyx Corporate Blog",[172],{"type":432,"value":77230,"toc":77285},[77231,77234,77244,77247,77250,77253,77279,77282],[435,77232,77227],{"id":77233},"happy-birthday-synyx-corporate-blog",[439,77235,77236,77243],{},[1002,77237,77240],{"href":77238,"rel":77239},"https://media.synyx.de/uploads//2011/04/Fotolia_17342363_XS.jpg",[1006],[2205,77241],{"alt":77242,"src":77238,"title":77242},"Geburtstagskuchen","\nKaum zu glauben – ist es tatsächlich schon ein Jahr her, als Synyx den Corporate Blog startete? Die Zeit ist wirklich\nschnell vergangen. Und nun feiert der Synyx Blog seinen ersten Geburtstag.",[439,77245,77246],{},"Die rund 60 Artikel sind noch nicht die Welt – aber wir werden uns weiterhin Mühe geben, euch informative Artikel\nbereitzustellen.",[439,77248,77249],{},"Wir bedanken uns an dieser Stelle bei allen Blog-Lesern, Feed-Abonnenten und Twitter-Followern. Aber auch bei den\nMitarbeitern von Synyx. Durch ihr Mitwirken sind viele interessante Artikel entstanden.",[439,77251,77252],{},"Hier die 3 erfolgreichsten Blogposts:",[8310,77254,77255,77263,77271],{},[997,77256,77257,77262],{},[1002,77258,77261],{"href":77259,"rel":77260,"title":77259},"http://blog.synyx.de/tag/document-management/",[1006],"Template based document generation using ODFDOM","\nvon Florian Hopf",[997,77264,77265,77270],{},[1002,77266,77269],{"href":77267,"rel":77268,"title":77269},"http://blog.synyx.de/2010/07/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app/",[1006],"Dependency Hell or Including JSR303 into a hibernated JavaEE App","\nvon Joachim Arrasz",[997,77272,77273,77278],{},[1002,77274,77277],{"href":77275,"rel":77276,"title":77277},"http://blog.synyx.de/2011/01/spring-ide-into-eclipse/",[1006],"Spring IDE into eclipse"," von Sascha\nRüssel",[439,77280,77281],{},"Uns interessiert natürlich eure Meinung: Was gefällt euch an dem Blog, was fehlt euch noch? Welche Themen wären für euch\nnoch interessant? Was können wir besser machen? Wir sind schon gespannt auf euer Feedback.",[439,77283,77284],{},"Auf ein weiteres erfolgreiches Blog-Jahr!",{"title":469,"searchDepth":507,"depth":507,"links":77286},[],[1031],"2011-04-21T12:31:10","\\nKaum zu glauben – ist es tatsächlich schon ein Jahr her, als Synyx den Corporate Blog startete? Die Zeit ist wirklich\\nschnell vergangen. Und nun feiert der Synyx Blog seinen ersten Geburtstag.","https://synyx.de/blog/happy-birthday-synyx-corporate-blog/",{},"/blog/happy-birthday-synyx-corporate-blog",{"title":77227,"description":77243},"blog/happy-birthday-synyx-corporate-blog",[],"Kaum zu glauben – ist es tatsächlich schon ein Jahr her, als Synyx den Corporate Blog startete? Die Zeit ist wirklich schnell vergangen. Und nun feiert der Synyx Blog seinen…","r60v81JRGQehON_U__dgEiVLhAJc9t-bpgnxtMWSvB0",{"id":77299,"title":77300,"author":77301,"body":77302,"category":77366,"date":77367,"description":77368,"extension":1034,"link":77369,"meta":77370,"navigation":916,"path":77371,"seo":77372,"slug":77306,"stem":77374,"tags":77375,"teaser":77376,"__hash__":77377},"blog/blog/maven-and-opencms.md","Maven and OpenCms",[148],{"type":432,"value":77303,"toc":77364},[77304,77307,77342,77345],[435,77305,77300],{"id":77306},"maven-and-opencms",[439,77308,77309,77310,77315,77316,77320,77321,77326,77327,77332,77333,77336,77337,1402],{},"I ",[1002,77311,77314],{"href":77312,"rel":77313},"http://blog.synyx.de/2010/11/netbeans-and-opencms/",[1006],"previously mentioned"," that setting up a development environment\nfor ",[1002,77317,71236],{"href":77318,"rel":77319},"http://opencms.org/",[1006]," can be quite hard. Besides\nour ",[1002,77322,77325],{"href":77323,"rel":77324},"https://github.com/synyx/opencms-netbeans-module",[1006],"Netbeans module"," we are using a custom maven plugin for some time\nnow. As we gain a lot of benefit by building our modules from the file system it’s time to release it and see if other\npeople also want to use it. It’s based on an Ant task that has originally been released\nby ",[1002,77328,77331],{"href":77329,"rel":77330},"http://www.eurelis.com",[1006],"Eurelis",". Today we ",[448,77334,77335],{},"released version 1.0"," which is now available under the terms of\nthe ",[1002,77338,77341],{"href":77339,"rel":77340},"http://www.fsf.org/licensing/licenses/lgpl.txt",[1006],"GNU Lesser General Public License",[439,77343,77344],{},"The plugin builds OpenCms module zip files from a Maven directory structure and can import these automatically in a\nrunning instance. You don’t have to edit any files in the OpenCms workplace anymore, the local filesystem is the basis\nfor your module. Use the archetype to set up a complete installation of OpenCms 7.5.2 or 7.5.4 in no time.",[439,77346,77347,77348,77352,77353,77358,77359,1402],{},"You can find the projects homepage including documentation how to get started and how everything\nworks ",[1002,77349,22916],{"href":77350,"rel":77351},"https://github.com/synyx/maven-opencms/",[1006],". You should not get problems to get up and running after reading\nthe information from the projects ",[1002,77354,77357],{"href":77355,"rel":77356},"https://github.com/synyx/maven-opencms/wiki",[1006],"Wiki",". If you’re having any trouble or\nfeature request feel free to contact us or create a ticket in the\nprojects ",[1002,77360,77363],{"href":77361,"rel":77362},"https://github.com/synyx/maven-opencms/issues",[1006],"issue tracker",{"title":469,"searchDepth":507,"depth":507,"links":77365},[],[1030,1412],"2011-04-08T17:39:59","I previously mentioned that setting up a development environment\\nfor OpenCms can be quite hard. Besides\\nour Netbeans module we are using a custom maven plugin for some time\\nnow. As we gain a lot of benefit by building our modules from the file system it’s time to release it and see if other\\npeople also want to use it. It’s based on an Ant task that has originally been released\\nby Eurelis. Today we released version 1.0 which is now available under the terms of\\nthe GNU Lesser General Public License.","https://synyx.de/blog/maven-and-opencms/",{},"/blog/maven-and-opencms",{"title":77300,"description":77373},"I previously mentioned that setting up a development environment\nfor OpenCms can be quite hard. Besides\nour Netbeans module we are using a custom maven plugin for some time\nnow. As we gain a lot of benefit by building our modules from the file system it’s time to release it and see if other\npeople also want to use it. It’s based on an Ant task that has originally been released\nby Eurelis. Today we released version 1.0 which is now available under the terms of\nthe GNU Lesser General Public License.","blog/maven-and-opencms",[22988,64517],"I previously mentioned that setting up a development environment for OpenCms can be quite hard. Besides our Netbeans module we are using a custom maven plugin for some time now.…","xHLhHj4ODShymebsyF1zyo6xejQxvqb3ILdPFOcloUA",{"id":77379,"title":77380,"author":77381,"body":77382,"category":77424,"date":77425,"description":77426,"extension":1034,"link":77427,"meta":77428,"navigation":916,"path":77429,"seo":77430,"slug":77386,"stem":77432,"tags":77433,"teaser":77434,"__hash__":77435},"blog/blog/synyx-at-the-droidcon-2011.md","Synyx at the Droidcon 2011",[205],{"type":432,"value":77383,"toc":77422},[77384,77387,77394,77407,77410,77413],[435,77385,77380],{"id":77386},"synyx-at-the-droidcon-2011",[439,77388,77389,77390,77393],{},"It has been a while since these pages saw some content. The daily software engineering business calls for our full\nattention, leaving us writing more code than content. ",[990,77391,77392],{},"Which is absolutely a good thing!"," Nevertheless we want to show\nyou, that we are still pretty much alive.",[439,77395,77396,77397,77402,77403,77406],{},"While still working on some new articles, we are at this very moment in Berlin to visit\nthe ",[1002,77398,77401],{"href":77399,"rel":77400},"https://www.droidcon.com/",[1006],"Droidcon"," conference. From ",[990,77404,77405],{},"March 23rd to 24th",", we will be attending the barcamp\nsessions & talks. It will be our pleasure to meet familiar faces as also new people there and we are looking forward to\ninteresting discussions.",[439,77408,77409],{},"We wish everyone attending a good time there!",[439,77411,77412],{},"— Florian Krupicka & Tobias Knell",[439,77414,77415,77416,77421],{},"PS: If you are more interested in database storage beyond SQL, you will also be able to meet even more members of the\nSynyx family at the ",[1002,77417,77420],{"href":77418,"rel":77419},"http://www.10gen.com/conferences/mongoberlin2011",[1006],"MongoBerlin"," conference on Friday, the 25th of\nMarch.",{"title":469,"searchDepth":507,"depth":507,"links":77423},[],[11122],"2011-03-23T02:24:43","It has been a while since these pages saw some content. The daily software engineering business calls for our full\\nattention, leaving us writing more code than content. Which is absolutely a good thing! Nevertheless we want to show\\nyou, that we are still pretty much alive.","https://synyx.de/blog/synyx-at-the-droidcon-2011/",{},"/blog/synyx-at-the-droidcon-2011",{"title":77380,"description":77431},"It has been a while since these pages saw some content. The daily software engineering business calls for our full\nattention, leaving us writing more code than content. Which is absolutely a good thing! Nevertheless we want to show\nyou, that we are still pretty much alive.","blog/synyx-at-the-droidcon-2011",[16640,8276,11133],"It has been a while since these pages saw some content. The daily software engineering business calls for our full attention, leaving us writing more code than content. Which is…","rgaqFjx5_-Ru7Rq2afSUOyG-0MdF2mgHboSi2lNAiPw",{"id":77437,"title":77438,"author":77439,"body":77440,"category":77883,"date":77884,"description":77447,"extension":1034,"link":77885,"meta":77886,"navigation":916,"path":77887,"seo":77888,"slug":77889,"stem":77890,"tags":77891,"teaser":77900,"__hash__":77901},"blog/blog/utilizing-git-to-dive-into-huge-code-bases.md","Utilizing Git to dive into huge code bases – Git SVN Tips",[27],{"type":432,"value":77441,"toc":77881},[77442,77445,77448,77451,77454,77468,77471,77474,77477,77480,77483,77486,77489,77492,77495,77498,77501,77504,77507,77510,77513,77516,77519,77533,77563,77566,77569,77572,77586,77588,77602,77605,77608,77634,77637,77680,77683,77686,77689,77708,77736,77739,77742,77745,77748,77772,77775,77789,77816,77819,77822,77825,77828,77851,77859,77873,77876,77879],[435,77443,77438],{"id":77444},"utilizing-git-to-dive-into-huge-code-bases-git-svn-tips",[439,77446,77447],{},"Unfortunately there are still projects not on dvsc like git. That’s especially true",[439,77449,77450],{},"for enterprise customers which are at least stuck on Subversion if not worse.",[439,77452,77453],{},"So the first thing I do on new projects I join:",[464,77455,77457],{"className":16895,"code":77456,"language":16897,"meta":469,"style":469},"\n git svn clone -s svn-url\n\n",[471,77458,77459,77463],{"__ignoreMap":469},[474,77460,77461],{"class":476,"line":477},[474,77462,917],{"emptyLinePlaceholder":916},[474,77464,77465],{"class":476,"line":507},[474,77466,77467],{}," git svn clone -s svn-url\n",[439,77469,77470],{},"Or installing Git if I have to work on customer provided machines. That’s even more",[439,77472,77473],{},"important than the rest of a development environment like an IDE.",[439,77475,77476],{},"From experience I find it especially useful to experiment with new code basis",[439,77478,77479],{},"utilizing Git. Grown and big projects aren’t easy to understand architecturally and",[439,77481,77482],{},"implementation wise without digging deep. With the help of Git you can jump right",[439,77484,77485],{},"in, without fear and without messing everything up or having too much unrevertable",[439,77487,77488],{},"local changes. Just commit early and often! By doing it locally, in experimental",[439,77490,77491],{},"branches you can try and learn. Before you publish something to a wider audience",[439,77493,77494],{},"(svn) you can reorder, cherrypick and change everything or parts of it. Git is my",[439,77496,77497],{},"tool of choice to get my hands dirty with legacy code (new one too of course).",[439,77499,77500],{},"Some useful tips on how I use Git-SVN:",[439,77502,77503],{},"SVN history is linear, so you can’t use branches and merge the usual git-way without",[439,77505,77506],{},"thinking.",[439,77508,77509],{},"What often happens to me is that I implement a new feature, do some refactorings on",[439,77511,77512],{},"my way etc and an urgant bug report comes along. But I commited on master, don’t",[439,77514,77515],{},"want to push it to SVN yet since it’s not finished yet and might not be stable. What",[439,77517,77518],{},"to do? git svn dcommit would push all my local master commits to svn. The solution:",[464,77520,77522],{"className":16895,"code":77521,"language":16897,"meta":469,"style":469},"\n git branch featureA\n\n",[471,77523,77524,77528],{"__ignoreMap":469},[474,77525,77526],{"class":476,"line":477},[474,77527,917],{"emptyLinePlaceholder":916},[474,77529,77530],{"class":476,"line":507},[474,77531,77532],{}," git branch featureA\n",[464,77534,77536],{"className":16895,"code":77535,"language":16897,"meta":469,"style":469},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[471,77537,77538,77543,77548,77553,77558],{"__ignoreMap":469},[474,77539,77540],{"class":476,"line":477},[474,77541,77542],{},"\u003Ctt>\n",[474,77544,77545],{"class":476,"line":507},[474,77546,77547],{}," |svn | master\n",[474,77549,77550],{"class":476,"line":547},[474,77551,77552],{}," ---o---o---o---o---o---o---o---o---o---o\n",[474,77554,77555],{"class":476,"line":584},[474,77556,77557],{}," | featureA\n",[474,77559,77560],{"class":476,"line":607},[474,77561,77562],{},"\u003C/tt>\n",[439,77564,77565],{},"Now both branches featureA and master point to the latest commit. But we want master",[439,77567,77568],{},"to point to an earlier commit. Let’s say the last 10 commits aren’t in SVN yet and",[439,77570,77571],{},"the last 8 are experimental, so 2 could be pushed.",[464,77573,77575],{"className":16895,"code":77574,"language":16897,"meta":469,"style":469},"\n git reset --hard HEAD~8\n\n",[471,77576,77577,77581],{"__ignoreMap":469},[474,77578,77579],{"class":476,"line":477},[474,77580,917],{"emptyLinePlaceholder":916},[474,77582,77583],{"class":476,"line":507},[474,77584,77585],{}," git reset --hard HEAD~8\n",[439,77587,20416],{},[464,77589,77591],{"className":16895,"code":77590,"language":16897,"meta":469,"style":469},"\n git reset --hard sha-hash-of-commit-to-point-to\n\n",[471,77592,77593,77597],{"__ignoreMap":469},[474,77594,77595],{"class":476,"line":477},[474,77596,917],{"emptyLinePlaceholder":916},[474,77598,77599],{"class":476,"line":507},[474,77600,77601],{}," git reset --hard sha-hash-of-commit-to-point-to\n",[439,77603,77604],{},"Now my master is in the state it was in 8 commits ago and my experimental changes",[439,77606,77607],{},"are still in featureA branch.",[464,77609,77611],{"className":16895,"code":77610,"language":16897,"meta":469,"style":469},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[471,77612,77613,77617,77622,77626,77630],{"__ignoreMap":469},[474,77614,77615],{"class":476,"line":477},[474,77616,77542],{},[474,77618,77619],{"class":476,"line":507},[474,77620,77621],{}," |svn | master\n",[474,77623,77624],{"class":476,"line":547},[474,77625,77552],{},[474,77627,77628],{"class":476,"line":584},[474,77629,77557],{},[474,77631,77632],{"class":476,"line":607},[474,77633,77562],{},[439,77635,77636],{},"I can continue with fixing that critical bug, commit and svn dcommit. Have a look on how your history look with gitk\n–all.",[464,77638,77640],{"className":16895,"code":77639,"language":16897,"meta":469,"style":469},"\u003Ctt>\n | svn\n ---o---o---o master\n /\n ---o---o---o\n \\---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[471,77641,77642,77646,77651,77656,77661,77666,77671,77676],{"__ignoreMap":469},[474,77643,77644],{"class":476,"line":477},[474,77645,77542],{},[474,77647,77648],{"class":476,"line":507},[474,77649,77650],{}," | svn\n",[474,77652,77653],{"class":476,"line":547},[474,77654,77655],{}," ---o---o---o master\n",[474,77657,77658],{"class":476,"line":584},[474,77659,77660],{}," /\n",[474,77662,77663],{"class":476,"line":607},[474,77664,77665],{}," ---o---o---o\n",[474,77667,77668],{"class":476,"line":642},[474,77669,77670],{}," \\---o---o---o---o---o---o---o\n",[474,77672,77673],{"class":476,"line":663},[474,77674,77675],{}," | featureA\n",[474,77677,77678],{"class":476,"line":694},[474,77679,77562],{},[439,77681,77682],{},"Dcommit rebased 3 commits and especially if",[439,77684,77685],{},"there were some more upstream svn commits, I want to base my experimental stuff",[439,77687,77688],{},"ontop of this. So I do a",[464,77690,77692],{"className":16895,"code":77691,"language":16897,"meta":469,"style":469},"\n git checkout featureA\n git rebase master\n\n",[471,77693,77694,77698,77703],{"__ignoreMap":469},[474,77695,77696],{"class":476,"line":477},[474,77697,917],{"emptyLinePlaceholder":916},[474,77699,77700],{"class":476,"line":507},[474,77701,77702],{}," git checkout featureA\n",[474,77704,77705],{"class":476,"line":547},[474,77706,77707],{}," git rebase master\n",[464,77709,77711],{"className":16895,"code":77710,"language":16897,"meta":469,"style":469},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn | featureA\n\u003C/tt>\n",[471,77712,77713,77717,77722,77727,77732],{"__ignoreMap":469},[474,77714,77715],{"class":476,"line":477},[474,77716,77542],{},[474,77718,77719],{"class":476,"line":507},[474,77720,77721],{}," | master\n",[474,77723,77724],{"class":476,"line":547},[474,77725,77726],{}," ---o---o---o---o---o---o---o---o---o---o---o\n",[474,77728,77729],{"class":476,"line":584},[474,77730,77731],{}," | svn | featureA\n",[474,77733,77734],{"class":476,"line":607},[474,77735,77562],{},[439,77737,77738],{},"Even if I could live without the upstream changes on my featureA branch for now, I’d",[439,77740,77741],{},"need a rebase later anyway, so I can do it in advance. That’s because the history",[439,77743,77744],{},"wouldn’t be linear anymore by doing a three-way merge of my featureA into master without rebasing.",[439,77746,77747],{},"When I’m satisfied and with featureA and nothing changed in master I can",[464,77749,77751],{"className":16895,"code":77750,"language":16897,"meta":469,"style":469},"\n git checkout master\n git merge featureA\n git branch -d featureA\n\n",[471,77752,77753,77757,77762,77767],{"__ignoreMap":469},[474,77754,77755],{"class":476,"line":477},[474,77756,917],{"emptyLinePlaceholder":916},[474,77758,77759],{"class":476,"line":507},[474,77760,77761],{}," git checkout master\n",[474,77763,77764],{"class":476,"line":547},[474,77765,77766],{}," git merge featureA\n",[474,77768,77769],{"class":476,"line":584},[474,77770,77771],{}," git branch -d featureA\n",[439,77773,77774],{},"And since it’s a fast-forward merge can continue to push it to SVN",[464,77776,77778],{"className":16895,"code":77777,"language":16897,"meta":469,"style":469},"\n git svn dcommit\n\n",[471,77779,77780,77784],{"__ignoreMap":469},[474,77781,77782],{"class":476,"line":477},[474,77783,917],{"emptyLinePlaceholder":916},[474,77785,77786],{"class":476,"line":507},[474,77787,77788],{}," git svn dcommit\n",[464,77790,77792],{"className":16895,"code":77791,"language":16897,"meta":469,"style":469},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn\n\u003C/tt>\n",[471,77793,77794,77798,77803,77807,77812],{"__ignoreMap":469},[474,77795,77796],{"class":476,"line":477},[474,77797,77542],{},[474,77799,77800],{"class":476,"line":507},[474,77801,77802],{}," | master\n",[474,77804,77805],{"class":476,"line":547},[474,77806,77726],{},[474,77808,77809],{"class":476,"line":584},[474,77810,77811],{}," | svn\n",[474,77813,77814],{"class":476,"line":607},[474,77815,77562],{},[439,77817,77818],{},"If something did change in master I just do another rebase before the merge.",[439,77820,77821],{},"If I come to the conclusion that my experimental branch was just for learning",[439,77823,77824],{},"purpose and only one or two useful refactoring or unit-test improving commits I take",[439,77826,77827],{},"only these to master and abandon the branch.",[464,77829,77831],{"className":16895,"code":77830,"language":16897,"meta":469,"style":469},"\n git checkout master\n git cherry-pick sha-of-one-commit\n git cherry-pick sha-of-another\n\n",[471,77832,77833,77837,77841,77846],{"__ignoreMap":469},[474,77834,77835],{"class":476,"line":477},[474,77836,917],{"emptyLinePlaceholder":916},[474,77838,77839],{"class":476,"line":507},[474,77840,77761],{},[474,77842,77843],{"class":476,"line":547},[474,77844,77845],{}," git cherry-pick sha-of-one-commit\n",[474,77847,77848],{"class":476,"line":584},[474,77849,77850],{}," git cherry-pick sha-of-another\n",[439,77852,77853,77854],{},"If I’m overall satisfied with the results of my experimental branch, but not with commit messages, how the commits are\nordered and maybe their scope, I use git\nrebases ",[1002,77855,77858],{"href":77856,"rel":77857},"http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode",[1006],"interactive mode",[464,77860,77862],{"className":16895,"code":77861,"language":16897,"meta":469,"style":469},"\n git rebase -i sha-after-this-commit\n\n",[471,77863,77864,77868],{"__ignoreMap":469},[474,77865,77866],{"class":476,"line":477},[474,77867,917],{"emptyLinePlaceholder":916},[474,77869,77870],{"class":476,"line":507},[474,77871,77872],{}," git rebase -i sha-after-this-commit\n",[439,77874,77875],{},"reordering commits, splitting commits, editing commit messages, squashing multiple commits together.",[439,77877,77878],{},"There are endless more possibilities to get a better grip on your code-base.",[1024,77880,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":77882},[],[1030],"2011-03-21T06:22:53","https://synyx.de/blog/utilizing-git-to-dive-into-huge-code-bases/",{},"/blog/utilizing-git-to-dive-into-huge-code-bases",{"title":77438,"description":77447},"utilizing-git-to-dive-into-huge-code-bases","blog/utilizing-git-to-dive-into-huge-code-bases",[77892,19320,77893,14723,77894,77895,77896,77897,77898,77899],"dvcs","productivity","subversion","svn","tips","vcs","versioncontrol","withoutfear","Unfortunately there are still projects not on dvsc like git. That’s especially true for enterprise customers which are at least stuck on Subversion if not worse. So the first thing…","jY0wlPCOmzUfKbhiV6O6parFyGSU5NhgtbhpJnpQyIQ",{"id":77903,"title":77904,"author":77905,"body":77906,"category":77953,"date":77954,"description":77955,"extension":1034,"link":77956,"meta":77957,"navigation":916,"path":77958,"seo":77959,"slug":77910,"stem":77961,"tags":77962,"teaser":77963,"__hash__":77964},"blog/blog/farewell-not-goodbye.md","Farewell not Goodbye",[223],{"type":432,"value":77907,"toc":77951},[77908,77911,77918,77927,77948],[435,77909,77904],{"id":77910},"farewell-not-goodbye",[439,77912,77913,77914,77917],{},"About one and a half years ago I decided to change companies. I was sick and tired of producing low quality software at\nmy previous employer. I joined ",[1002,77915,74175],{"href":26130,"rel":77916},[1006],", a small team of highly skilled developers with the same\npassion and mindset, that I have.",[439,77919,77920,77921,77926],{},"One thing that got my attention, while looking for a new jobs was Synyx’s vision of Open Source. Having worked at Synyx\nfor quite a while now, I can say it is more than a slogan here. We are not only using open source software, but also\nproducing it. There’s dedicated time to polish and release, whatever you think is helping fellow developers out there.\nAs for me, I was able to plan and organize the ",[1002,77922,77925],{"href":77923,"rel":77924},"http://jug-ka.de",[1006],"local Java User Group here in Karlsruhe"," during\nworking hours.",[439,77928,77929,77930,77935,77936,77941,77942,77947],{},"About a year ago, I got the opportunity to spearhead the ",[1002,77931,77934],{"href":77932,"rel":77933},"http://mobile.synyx.de",[1006],"mobile development efforts"," here at\nSynyx. It was an amazing opportunity, which helped me to realize, in which direction my future career is heading. Next\nto Android, the mobile team was able to build up substantial knowledge in iOS and the mobile market in general. We\nreleased our very first App for Android and iOS,\ncalled “",[1002,77937,77940],{"href":77938,"rel":77939},"http://mobile.synyx.de/2010/09/i-think-i-spider-1-0-released/",[1006],"I think I spider","“. It was a joined effort with\nthe talented designer at “",[1002,77943,77946],{"href":77944,"rel":77945},"http://upstruct.com",[1006],"upstruct","“.",[439,77949,77950],{},"Working with the folks at Synyx was one of the most enjoyable experiences of my career. I’d like to thank each and\neveryone of them for the opportunities and support over the past one and a half years.",{"title":469,"searchDepth":507,"depth":507,"links":77952},[],[1031],"2011-02-25T13:27:37","About one and a half years ago I decided to change companies. I was sick and tired of producing low quality software at\\nmy previous employer. I joined Synyx, a small team of highly skilled developers with the same\\npassion and mindset, that I have.","https://synyx.de/blog/farewell-not-goodbye/",{},"/blog/farewell-not-goodbye",{"title":77904,"description":77960},"About one and a half years ago I decided to change companies. I was sick and tired of producing low quality software at\nmy previous employer. I joined Synyx, a small team of highly skilled developers with the same\npassion and mindset, that I have.","blog/farewell-not-goodbye",[18496,389],"About one and a half years ago I decided to change companies. I was sick and tired of producing low quality software at my previous employer. I joined Synyx, a…","RZB0DpytAB4ZD8wrGoAt8FIlor6bKayjPUP69gCmV3o",{"id":77966,"title":77967,"author":77968,"body":77969,"category":78019,"date":78020,"description":78021,"extension":1034,"link":78022,"meta":78023,"navigation":916,"path":78024,"seo":78025,"slug":77973,"stem":78027,"tags":78028,"teaser":78029,"__hash__":78030},"blog/blog/solr-summit-frankfurt.md","Solr Summit Frankfurt",[148],{"type":432,"value":77970,"toc":78017},[77971,77974,77987,77996,78005,78014],[435,77972,77967],{"id":77973},"solr-summit-frankfurt",[439,77975,77976,77977,77982,77983,77986],{},"I just returned\nfrom ",[1002,77978,77981],{"href":77979,"rel":77980},"http://www.lucidimagination.com/blog/2011/01/31/solr-summit-series-comes-to-germany-munich23-feb-frankfurt24-feb-2011-2/",[1006],"Solr Summit","\nin Frankfurt, a half day mini conference about ",[1002,77984,76852],{"href":76850,"rel":77985},[1006],", the search server based on Apache\nLucene. It has been a really worthwile event with a lot of insight into large scale implementations of Solr.",[439,77988,77989,77990,77995],{},"The first half of the conference Marc Krellenstein, a Co-Founder\nof ",[1002,77991,77994],{"href":77992,"rel":77993},"http://www.lucidimagination.com/",[1006],"Lucid Imagination",", presented trends in enterprise search as well as Lucids\ncommercial Solr environment Lucid Works Enterprise. After an outline of the history of search systems he presented\ndifferent characteristics of a successful search system. Though being held by someone who is obviously biased towards\nSolr and Lucene he also summarized where commercial search systems like Autonomy and Fast have their strengths, good to\nhave an insight into competing systems.",[439,77997,77998,77999,78004],{},"Afterwards Oliver Schönherr and Thomas Kwiatkowski spoke on how Solr is used\nat ",[1002,78000,78003],{"href":78001,"rel":78002},"http://www.immobilienscout24.de",[1006],"Immobilienscout24",", where it powers full text search. Solr had been selected after\nan evaluation period where commercial as well as non-commercial systems were compared. The way Solr is used probably is\nnot a common use case. IS24 uses a custom build search system for doing their structured search, where you basically\nrefine the search results using different form fields. Solr is used to search within this result list by intersecting\nthe Solr search results with the results of the legacy system. They are using a plain Solr 1.4 without any patches and\nonly two additional components, a scheduler for the data import handler that indexes a database and a component that\nprovides fast access to only the ids of documents because that’s all that is needed for the intersection.",[439,78006,78007,78008,78013],{},"The last talk was held by Olaf Zschiedrich of ",[1002,78009,78012],{"href":78010,"rel":78011},"https://www.ebay-kleinanzeigen.de/",[1006],"eBay Kleinanzeigen",", formerly known\nas Kijiji. eBay Kleinanzeigen seems to use nearly all features that Solr has to offer, most notably facetting,\nautocompletion and more-like-this for displaying related articles. The site is being developed by a relatively small\nteam and seems to be blazing fast though there are lots of hits on Solr, on peak times 1500 requests/s. Of course this\nis only possible as Solr is designed to be scalable by means of its replication features, its internal caching and the\nexternal caching support through ETags. At eBay Kleinanzeigen there are 12 Solr instances that are used for searching\nbut according to Olaf, 8 would still be enough to keep the resource consumption under 50%.",[439,78015,78016],{},"All of the talks were really interesting, Olaf Zschiedrichs being the one with the most laughters. I have learned a lot\nand appreciate the time and costs Lucid Imagination and its partners have invested to make this event possible.",{"title":469,"searchDepth":507,"depth":507,"links":78018},[],[1030,1412],"2011-02-24T20:34:30","I just returned\\nfrom Solr Summit\\nin Frankfurt, a half day mini conference about Solr, the search server based on Apache\\nLucene. It has been a really worthwile event with a lot of insight into large scale implementations of Solr.","https://synyx.de/blog/solr-summit-frankfurt/",{},"/blog/solr-summit-frankfurt",{"title":77967,"description":78026},"I just returned\nfrom Solr Summit\nin Frankfurt, a half day mini conference about Solr, the search server based on Apache\nLucene. It has been a really worthwile event with a lot of insight into large scale implementations of Solr.","blog/solr-summit-frankfurt",[8276,64519],"I just returned from Solr Summit in Frankfurt, a half day mini conference about Solr, the search server based on Apache Lucene. It has been a really worthwile event with…","kPs7DFsBho3bz4Mub2Pfnm_Wm3cs2gZQU5AYzAkVY8Y",{"id":78032,"title":78033,"author":78034,"body":78035,"category":78088,"date":78089,"description":78090,"extension":1034,"link":78091,"meta":78092,"navigation":916,"path":78093,"seo":78094,"slug":78039,"stem":78095,"tags":78096,"teaser":78098,"__hash__":78099},"blog/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles.md","Synyx Team bekommt drei neue Gesichter und das ist noch nicht alles …",[172],{"type":432,"value":78036,"toc":78086},[78037,78040,78050,78053,78056,78059,78062,78065,78068,78071,78074,78077,78080],[435,78038,78033],{"id":78039},"synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",[439,78041,78042,78049],{},[1002,78043,78046],{"href":78044,"rel":78045},"https://media.synyx.de/uploads//2011/02/neue_mitarbeiter.jpg",[1006],[2205,78047],{"alt":78048,"src":78044,"title":78048},"Neue Gesichter bei Synyx","\nBereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber\nschnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als erwartet. Es gab zwar\neine große Anzahl von Bewerbern, doch häufig waren für die umfangreichen und verantwortungsvollen Aufgabengebiete nicht\ndie entsprechenden Bewerber dabei.",[439,78051,78052],{},"Nach einer letztendlich getroffenen Vorauswahl und einigen Vorstellungsgesprächen wurde uns dann schnell klar, wer die\npassenden Neulinge im Synyx-Team sind.",[439,78054,78055],{},"Aus diesem Grund wollen wir die neuen Synyxler einmal vorstellen.",[439,78057,78058],{},"Für den Bereich Office-Management haben wir Sabine David ins Boot geholt.",[439,78060,78061],{},"Die gelernte Bürokauffrau war zuvor als kaufmännische Angestellte tätig und sucht die Herausforderung in einem sehr\numfangreichen und verantwortungsvollen Tätigkeitsfeld.",[439,78063,78064],{},"Als neues „altes“ Gesicht begrüßen wir Jochen Schalanda in unserem Team.",[439,78066,78067],{},"Der Diplominformatiker (FH) war bereits während seines Praxissemesters und anschließender Diplomarbeit von 2008-2009\nein wertvolles Mitglied von Synyx. Das Thema seiner Diplomarbeit umfasste den Entwurf und die Umsetzung einer auf REST\naufsetzenden Interoperabilitäts-Schnittstelle und deren Integration in eine JEE-basierte Geschäftsanwendungen. Nach\nseiner Diplomarbeit und einer 13-monatigen Zeit als Software Engineer in Riad (Saudi Arabien) kehrt Jochen Schalanda\njetzt als Senior Developer zu Synyx zurück.",[439,78069,78070],{},"Und dann war da noch…",[439,78072,78073],{},"Andreas Weigel, der während seines Informatikstudiums (9.Semester(KIT)) bei uns arbeitet. Derzeit erweitert er sein\nWissen in OpenCms. Der Java – Erfahrene Student wird zukünftig die einzelnen Projektteams tatkräftig unterstützen.",[439,78075,78076],{},"Alle drei Neuankömmlinge heißen wir nochmals herzlich willkommen!",[439,78078,78079],{},"Zur weiteren Verstärkung unseres Teams suchen wir kreative Java-Entwickler, sowie eine/n Auszubildende/n zum\nFachinformatiker/in Systemintegration und eine/n Auszubildende/n zum Fachinformatiker/in Anwendungsentwicklung.",[439,78081,78082,78083,1402],{},"Nähere Informationen dazu gibt es auf der ",[1002,78084,41993],{"href":3568,"rel":78085},[1006],{"title":469,"searchDepth":507,"depth":507,"links":78087},[],[1031],"2011-02-22T16:20:28","\\nBereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber\\nschnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als erwartet. Es gab zwar\\neine große Anzahl von Bewerbern, doch häufig waren für die umfangreichen und verantwortungsvollen Aufgabengebiete nicht\\ndie entsprechenden Bewerber dabei.","https://synyx.de/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles/",{},"/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",{"title":78033,"description":78049},"blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",[76768,389,78097],"team","Bereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber schnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als…","NCKFBY2CXlYYghnQjXTOr4G01NBWHNql0egWOpKTtxw",{"id":78101,"title":78102,"author":78103,"body":78104,"category":78159,"date":78160,"description":78161,"extension":1034,"link":78162,"meta":78163,"navigation":916,"path":78164,"seo":78165,"slug":78108,"stem":78167,"tags":78168,"teaser":78172,"__hash__":78173},"blog/blog/synyx-messagesource-load-your-i18n-messages-from-database.md","Synyx MessageSource: Load your i18n messages from database",[166],{"type":432,"value":78105,"toc":78157},[78106,78109,78117,78124,78139],[435,78107,78102],{"id":78108},"synyx-messagesource-load-your-i18n-messages-from-database",[439,78110,78111,78112,78116],{},"A while ago we wanted to store internationalisation for a project in database to allow (a subset of) users to create and\nupdate internationalisation using the application itself. When we searched the web for existing projects that allow to\ndo this we did not find a good and ready to use solution. This is why we decided to write the code ourselves and make it\navailable to others, especially since there was some public demand and it will probably not be shipped by SpringSource (\ncheck out ",[1002,78113,58858],{"href":78114,"rel":78115},"http://www.google.de/search?q=spring+i18n+database",[1006]," for details).",[439,78118,78119,78120,78123],{},"So today I’d like to announce our new Open Source project ",[448,78121,78122],{},"Synyx Messagesource for Spring"," business-friendly\npublished using Apache License, Version 2.0.",[439,78125,78126,78127,78130,78131,78134,78135,78138],{},"When you want to store internationalisation of your Spring-backed application in database, the project is the right\nthing to use. It provides an implementation of Springs ",[471,78128,78129],{},"MessageSource"," interface that is able to load and cache a set of\nmessages at once using a ",[471,78132,78133],{},"MessageProvider",". The project brings a configurable one that is able to read (and write) your\ni18n to database using JDBC. There is also support to import and export your messages to the “standard” i18n\n",[471,78136,78137],{},".properties"," files.",[439,78140,77347,78141,78145,78146,78151,78152,1402],{},[1002,78142,22916],{"href":78143,"rel":78144},"http://messagesource.synyx.org",[1006],". You should not get problems to get up and running after reading the\ninformation from the ",[1002,78147,78150],{"href":78148,"rel":78149},"https://github.com/synyx/messagesource/wiki",[1006],"projects Wiki",". If you’re having any trouble or\nfeature request feel free to contact us\nor ",[1002,78153,78156],{"href":78154,"rel":78155},"https://github.com/synyx/messagesource/issues",[1006],"create a ticket in the projects issue tracker",{"title":469,"searchDepth":507,"depth":507,"links":78158},[],[1030,1412],"2011-02-14T18:06:55","A while ago we wanted to store internationalisation for a project in database to allow (a subset of) users to create and\\nupdate internationalisation using the application itself. When we searched the web for existing projects that allow to\\ndo this we did not find a good and ready to use solution. This is why we decided to write the code ourselves and make it\\navailable to others, especially since there was some public demand and it will probably not be shipped by SpringSource (\\ncheck out Google for details).","https://synyx.de/blog/synyx-messagesource-load-your-i18n-messages-from-database/",{},"/blog/synyx-messagesource-load-your-i18n-messages-from-database",{"title":78102,"description":78166},"A while ago we wanted to store internationalisation for a project in database to allow (a subset of) users to create and\nupdate internationalisation using the application itself. When we searched the web for existing projects that allow to\ndo this we did not find a good and ready to use solution. This is why we decided to write the code ourselves and make it\navailable to others, especially since there was some public demand and it will probably not be shipped by SpringSource (\ncheck out Google for details).","blog/synyx-messagesource-load-your-i18n-messages-from-database",[54575,78169,78170,78171,26636,1426],"i18n","internationalisation","internationalization","A while ago we wanted to store internationalisation for a project in database to allow (a subset of) users to create and update internationalisation using the application itself. When we…","nyZcMPq1I7-esYkZoVwAgntWZKJrIJkCdwQvpagdS20",{"id":78175,"title":78176,"author":78177,"body":78178,"category":78227,"date":78228,"description":78229,"extension":1034,"link":78230,"meta":78231,"navigation":916,"path":78232,"seo":78233,"slug":78182,"stem":78235,"tags":78236,"teaser":78238,"__hash__":78239},"blog/blog/synyx-open-source-projekt-hades-schafft-den-sprung-zu-springsource.md","Synyx Open Source Projekt Hades schafft den Sprung zu SpringSource",[172],{"type":432,"value":78179,"toc":78225},[78180,78183,78196,78199,78202,78205,78208],[435,78181,78176],{"id":78182},"synyx-open-source-projekt-hades-schafft-den-sprung-zu-springsource",[439,78184,78185,78186,78190,78191,1402],{},"Am Donnerstag hat SpringSource das erste Milestone-Release von Spring Data\nJPA ",[1002,78187,26468],{"href":78188,"rel":78189},"http://www.springsource.org/node/3022",[1006],". Spring Data JPA ist die Weiterentwicklung der Synyx\nOpenSource ",[1002,78192,78195],{"href":78193,"rel":78194},"http://hades.synyx.org",[1006],"Bibliothek Hades",[439,78197,78198],{},"Hades wurde hauptsächlich von Oliver Gierke entwickelt, welcher bis Anfang 2010 als Software Architekt bei Synyx tätig\nwar. Die Entwicklung des Projekts wurde von Synyx vollumfänglich unterstützt und gefördert, die Bibliothek in vielen\nProjekten erfolgreich eingesetzt.",[439,78200,78201],{},"Oliver hat mittlerweile neue Wege eingeschlagen und arbeitet nun direkt für SpringSource. Die Entwicklung an Hades\nführte er, auch in Zusammenarbeit mit Synyx, fort. In den letzten Monaten hat er sich darum gekümmert, die Zukunft von\nHades bei SpringSource zu sichern. Deshalb freut es uns natürlich sehr, dass die Bibliothek nun in Spring Data\nintegriert wurde, dort das JPA Modul und darüber hinaus die Basis für die Unterstützung weiterer Persistenztechnologien\nbildet.",[439,78203,78204],{},"Wir hoffen, dass Hades so einer noch breiteren Masse an Entwicklern des Implementieren von Persistenzschichten\nerleichtern wird. Natürlich wird auch Synyx das neue Spring Data JPA mit Vergnügen und Erfolg einsetzen.",[439,78206,78207],{},"Nähere Infos:",[994,78209,78210,78217],{},[997,78211,78212],{},[1002,78213,78216],{"href":78214,"rel":78215},"http://synyx.org/",[1006],"Synyx.org – Open Source Projekte",[997,78218,78219,78220],{},"Tutorial zu Spring Data JPA von Oliver\nim ",[1002,78221,78224],{"href":78222,"rel":78223},"http://blog.springsource.com/2011/02/10/getting-started-with-spring-data-jpa/",[1006],"SpringSource Blog",{"title":469,"searchDepth":507,"depth":507,"links":78226},[],[1412],"2011-02-13T10:07:31","Am Donnerstag hat SpringSource das erste Milestone-Release von Spring Data\\nJPA veröffentlicht. Spring Data JPA ist die Weiterentwicklung der Synyx\\nOpenSource Bibliothek Hades.","https://synyx.de/blog/synyx-open-source-projekt-hades-schafft-den-sprung-zu-springsource/",{},"/blog/synyx-open-source-projekt-hades-schafft-den-sprung-zu-springsource",{"title":78176,"description":78234},"Am Donnerstag hat SpringSource das erste Milestone-Release von Spring Data\nJPA veröffentlicht. Spring Data JPA ist die Weiterentwicklung der Synyx\nOpenSource Bibliothek Hades.","blog/synyx-open-source-projekt-hades-schafft-den-sprung-zu-springsource",[78237,1426],"hades","Am Donnerstag hat SpringSource das erste Milestone-Release von Spring Data JPA veröffentlicht. Spring Data JPA ist die Weiterentwicklung der Synyx OpenSource Bibliothek Hades. Hades wurde hauptsächlich von Oliver Gierke entwickelt,…","0l5QYp6kg1FYAQd1i8BEizzQ0V_uvxv2TDCb_vaCBe0",{"id":78241,"title":78242,"author":78243,"body":78244,"category":78259,"date":78260,"description":78261,"extension":1034,"link":78262,"meta":78263,"navigation":916,"path":78264,"seo":78265,"slug":78248,"stem":78267,"tags":78268,"teaser":78269,"__hash__":78270},"blog/blog/synyx-sponsort-die-opencms-days.md","Synyx sponsort die OpenCms-Days",[148],{"type":432,"value":78245,"toc":78257},[78246,78249],[435,78247,78242],{"id":78248},"synyx-sponsort-die-opencms-days",[439,78250,78251,78252,78256],{},"Vom 09. bis zum 10. Mai finden, rechtzeitig zum Release von OpenCms 8, die\n3. ",[1002,78253,78255],{"href":76836,"rel":78254},[1006],"OpenCms-Days"," in Köln statt. Synyx unterstützt die Entwicklerkonferenz\nwie in den vergangenen Jahren gerne, wir sind gespannt auf die Neuerungen in OpenCms 8 und interessante Vorträge.",{"title":469,"searchDepth":507,"depth":507,"links":78258},[],[1031],"2011-02-02T21:31:27","Vom 09. bis zum 10. Mai finden, rechtzeitig zum Release von OpenCms 8, die\\n3. OpenCms-Days in Köln statt. Synyx unterstützt die Entwicklerkonferenz\\nwie in den vergangenen Jahren gerne, wir sind gespannt auf die Neuerungen in OpenCms 8 und interessante Vorträge.","https://synyx.de/blog/synyx-sponsort-die-opencms-days/",{},"/blog/synyx-sponsort-die-opencms-days",{"title":78242,"description":78266},"Vom 09. bis zum 10. Mai finden, rechtzeitig zum Release von OpenCms 8, die\n3. OpenCms-Days in Köln statt. Synyx unterstützt die Entwicklerkonferenz\nwie in den vergangenen Jahren gerne, wir sind gespannt auf die Neuerungen in OpenCms 8 und interessante Vorträge.","blog/synyx-sponsort-die-opencms-days",[64517],"Vom 09. bis zum 10. Mai finden, rechtzeitig zum Release von OpenCms 8, die 3. OpenCms-Days in Köln statt. Synyx unterstützt die Entwicklerkonferenz wie in den vergangenen Jahren gerne, wir…","m7FdDpWg9kZnUbK_gVOiGv0ZxlQukwGv2QnWBGldLh4",{"id":78272,"title":78273,"author":78274,"body":78275,"category":78361,"date":78362,"description":78363,"extension":1034,"link":78364,"meta":78365,"navigation":916,"path":78366,"seo":78367,"slug":78279,"stem":78368,"tags":78369,"teaser":78370,"__hash__":78371},"blog/blog/mit-spannung-in-die-retrowinterfeier.md","Mit Spannung in die RetroWinterfeier",[172],{"type":432,"value":78276,"toc":78359},[78277,78280,78283,78286,78289,78298,78311,78314,78326,78329,78341,78344,78356],[435,78278,78273],{"id":78279},"mit-spannung-in-die-retrowinterfeier",[439,78281,78282],{},"Dieses Jahr war alles ein wenig anders. Meistens organisieren wir Mitarbeiter die Synyx-Feiern selbst, doch dieses Mal\nhaben es sich die 3 Geschäftsführer nicht nehmen lassen, haben diesen Part selbst übernommen und wollten uns\nMitarbeiter, Partner und Freunde von Synyx damit überraschen.",[439,78284,78285],{},"Fest stand nur, wir treffen uns hier im Büro und es geht mit der Bahn weiter. Aber wohin? Ganz klar, dies ließ im\nVorfeld viele wilde Spekulationen zu. Auch ich war sehr gespannt.",[439,78287,78288],{},"Nichts ahnend sind die meisten im Büro gegen 18 Uhr angekommen. Kurz danach hieß es dann ab zur Bahnhaltestelle.\nNatürlich rätselten wir alle, wohin es geht. Die Andeutungen seitens der Cheffen machten es uns nicht leichter. Dafür\nhatten die 3 Herren ihren Spaß an unserer Raterei. Nachdem wir dann noch mal umsteigen mussten, war die Richtung klar\nund die Spekulationen wurden konkreter. Lago-Center oder der Gotec-Club, in dem auch RetroGames e.V. seine\nRäumlichkeiten hat? Thomas hat uns noch kurz versucht zu irritieren und blieb vor dem Lago-Center stehen. Kann das\nsein? Jedoch teilte er uns lediglich mit, dass wir da nicht hingehen 😉",[439,78290,78291,78292,78297],{},"Also ab in den Gotec-Club, besser gesagt in die Hallen von ",[1002,78293,78296],{"href":78294,"rel":78295},"http://www.retrogames.info/",[1006],"RetroGames",". Der 2002\ngegründete Verein RetroGames e.V. möchte die Kultur der elektronischen Videospiele in Deutschland erhalten, fördern und\neiner breiteren Öffentlichkeit zugänglich machen. Jeden Dienstag ab 20 Uhr ist Mitgliedertreffen, jeden Samstag öffnet\nRetroGames ab 21 Uhr für Publikum.",[439,78299,78300,78301,78310],{},"In einem Nebenraum waren Tische und Bänke für uns aufgestellt, Kaltgetränke bereitgestellt. Schnell ein Getränk\nergattern und dann ab an die\nSpielautomaten.",[1002,78302,78305],{"href":78303,"rel":78304},"https://media.synyx.de/uploads//2011/02/flipper.jpg",[1006],[2205,78306],{"alt":78307,"src":78308,"title":78309},"Flipper","https://media.synyx.de/uploads//2011/02/flipper-300x225.jpg","flipper","\nErst mal daddeln. Das war äußerst lustig. Schnell fühlt man sich in seine Jugendzeit zurückversetzt. Da werden\nErinnerungen wach, das kann ich euch sagen!",[439,78312,78313],{},"Unser Host und Betreuer an diesem Abend war Mike, seines Zeichens Gründungsmitglied von RetroGames und auch langjähriger\nFreund von einigen Synyx-lern. Er war Aufpasser, Automatenerklärer und DJ in Personalunion und hat einen nicht\nunerheblichen Anteil zu der familiären Feier-Atmosphäre beigetragen. Ein herzliches Dankeschön daher in seine Richtung,\ndass er es mit der Rasselbande so lange ausgehalten hat.",[439,78315,78316,78325],{},[1002,78317,78320],{"href":78318,"rel":78319},"https://media.synyx.de/uploads//2011/02/cheffe.jpg",[1006],[2205,78321],{"alt":78322,"src":78323,"title":78324},"Unsere 3 Cheffen","https://media.synyx.de/uploads//2011/02/cheffe-300x225.jpg","cheffe","\nNur kurze Zeit später wurde vom Caterer das Essen angeliefert und aufgebaut. Noch ein wenig zocken und dann trommelte\nThomas alle zusammen.",[439,78327,78328],{},"Die Rede! Jaja, auch das gibt es bei uns. Dieses Jahr ließ es sich Thomas nicht nehmen, zu jedem Mitarbeiter gab es\neinen kleinen, geistreichen Spruch. So wurde die Rede etwas länger als üblich, aber dafür gabs auch einige Lacher. Auch\nwenn unser Magen knurrte, mussten wir danach noch kurz ausharren, da es nun das Präsent für unsere Cheffen gab. Wir\nhoffen mal, sie haben sich gefreut.",[439,78330,78331,78340],{},[1002,78332,78335],{"href":78333,"rel":78334},"https://media.synyx.de/uploads//2011/02/buffet.jpg",[1006],[2205,78336],{"alt":78337,"src":78338,"title":78339},"Das Buffet","https://media.synyx.de/uploads//2011/02/buffet-300x224.jpg","buffet","\nAlso los gings, ab zum Buffet. Hey, das war echt riesig und für jeden was dabei. Selbstverständlich auch für die\nVegetarier unter uns. Ich von meiner Seite aus kann sagen, dass es äußert lecker war. Ich konnte mich gar nicht\nentscheiden, was ich zuerst essen sollte. Viele warme Gerichte aber auch Fingerfood. Lecker, lecker!",[439,78342,78343],{},"Und nachdem die Bäuche vollgeschlagen waren, mussten wir das Ganze an den Spielautomaten wieder “abtrainieren”. Mein\nFavorite war natürlich “Tetris” und “Arkanoid”. Und weil wir ja erst gegessen haben, gabs dann Nachtisch. Mousse au\nChocolat und Pana Cotta. Ebenfalls sehr lecker.",[439,78345,78346,78355],{},[1002,78347,78350],{"href":78348,"rel":78349},"https://media.synyx.de/uploads//2011/02/sascha.jpg",[1006],[2205,78351],{"alt":78352,"src":78353,"title":78354},"Synyx kann feiern!","https://media.synyx.de/uploads//2011/02/sascha-300x225.jpg","sascha","\nIch bin dann auch bereits um 1 Uhr gegangen, da meine letzte Bahn fuhr. Ich habe mir aber erzählen lassen, dass der eine\noder andere erst gegen halb neun den Heimweg angetreten ist, nachdem die Tanzfläche vom Gotec noch erobert wurde.",[439,78357,78358],{},"Also ein durchweg gelungenes Fest! Naja, aber wir Synyx-ler können halt auch feiern!",{"title":469,"searchDepth":507,"depth":507,"links":78360},[],[1031],"2011-02-01T11:40:35","Dieses Jahr war alles ein wenig anders. Meistens organisieren wir Mitarbeiter die Synyx-Feiern selbst, doch dieses Mal\\nhaben es sich die 3 Geschäftsführer nicht nehmen lassen, haben diesen Part selbst übernommen und wollten uns\\nMitarbeiter, Partner und Freunde von Synyx damit überraschen.","https://synyx.de/blog/mit-spannung-in-die-retrowinterfeier/",{},"/blog/mit-spannung-in-die-retrowinterfeier",{"title":78273,"description":78282},"blog/mit-spannung-in-die-retrowinterfeier",[62431,389],"Dieses Jahr war alles ein wenig anders. Meistens organisieren wir Mitarbeiter die Synyx-Feiern selbst, doch dieses Mal haben es sich die 3 Geschäftsführer nicht nehmen lassen, haben diesen Part selbst…","9oJJ4LX_DpiWmTAEnZeb3cLhWmCyYrmLZ53NSepkge4",{"id":78373,"title":78374,"author":78375,"body":78376,"category":78389,"date":78390,"description":469,"extension":1034,"link":78391,"meta":78392,"navigation":916,"path":78393,"seo":78394,"slug":78395,"stem":78396,"tags":78397,"teaser":78400,"__hash__":78401},"blog/blog/i-think-i-spider-2-0.md","I think I spider 2.0",[223],{"type":432,"value":78377,"toc":78387},[78378,78381],[435,78379,78374],{"id":78380},"i-think-i-spider-20",[439,78382,78383],{},[2205,78384],{"alt":78385,"src":78386},"I Think I Spider","https://media.synyx.de/uploads//2010/09/app-icon-512px.png",{"title":469,"searchDepth":507,"depth":507,"links":78388},[],[11122,3799],"2011-01-31T12:36:40","https://synyx.de/blog/i-think-i-spider-2-0/",{},"/blog/i-think-i-spider-2-0",{"title":78374,"description":469},"i-think-i-spider-2-0","blog/i-think-i-spider-2-0",[63945,78398,78399],"iphone","ipod","It has been a while since our 1.0 release of I think I spider, but we have been working hard on a new major release with a couple of great…","wQyb1IaF6l-gmasnEesnmZdzAba4HTg7N-UWdSnfvPI",{"id":78403,"title":78404,"author":78405,"body":78406,"category":78419,"date":78420,"description":469,"extension":1034,"link":78421,"meta":78422,"navigation":916,"path":78423,"seo":78424,"slug":78425,"stem":78426,"tags":78427,"teaser":78428,"__hash__":78429},"blog/blog/debian-sqeeze-countdown.md","join the countdown to squeeze",[88],{"type":432,"value":78407,"toc":78417},[78408,78411],[435,78409,78404],{"id":78410},"join-the-countdown-to-squeeze",[439,78412,78413],{},[2205,78414],{"alt":78415,"src":78416},"Debian squeeze countdown","http://news.debian.net/wp-content/uploads/2011/squeeze_countdown.png",{"title":469,"searchDepth":507,"depth":507,"links":78418},[],[1031],"2011-01-27T10:32:04","https://synyx.de/blog/debian-sqeeze-countdown/",{},"/blog/debian-sqeeze-countdown",{"title":78404,"description":469},"debian-sqeeze-countdown","blog/debian-sqeeze-countdown",[],"Since about a week the Debian project invites everyone to join in the countdown to the brand rustynew version of Debian. While waiting for the official squeeze release, one can…","bd6MQetbkCgOiPoM5jVBx7JQdAQNiLrYNOV2LWpxycA",{"id":78431,"title":78432,"author":78433,"body":78434,"category":78506,"date":78507,"description":78508,"extension":1034,"link":78509,"meta":78510,"navigation":916,"path":78511,"seo":78512,"slug":78438,"stem":78513,"tags":78514,"teaser":78517,"__hash__":78518},"blog/blog/one-firefox-for-two-x-servers.md","One firefox for two X servers",[88],{"type":432,"value":78435,"toc":78504},[78436,78439,78442,78451,78458,78461,78472,78475,78478,78491,78501],[435,78437,78432],{"id":78438},"one-firefox-for-two-x-servers",[439,78440,78441],{},"Like a lot of it-staff nowadays, I’ve got two displays connected to my Linux Box. Most of my colleagues like having one\nbig workspace using Xinerama / TwinView, but I prefer 2 independent workspaces, even if I can’t use drag and drop\nbetween them.",[439,78443,78444,78445,78450],{},"Since I’m using that layout for years, I rarely run in problems, but today I faced a problem. Currently I use a\ngraphical IRC Client called ",[1002,78446,78449],{"href":78447,"rel":78448},"http://quassel-irc.org/",[1006],"quassel",", which is running on my right Display. My firefox is\nrunning on the left display, and due to “poor” default settings of Ubuntu, clicking a link within quassel fires up\nkonqueror on my right display, instead of handing it over to the Firefox.",[439,78452,78453,78454,78457],{},"Following the “Ubuntu” way, I did ",[471,78455,78456],{},"update-alternatives --config x-www-browser"," to update the browser settings of my\nmachine to firefox.",[439,78459,78460],{},"This fixed the starting of konqueror, so after clicking on a link in quassel, firefox gets invoked, and presents the\nError “Firefox is already running, but is not responding. To open a new window, you must first close the existing\nFirefox process, or restart your system.”, which in this case simply means that there’s already a Firefox running on the\nother display.",[439,78462,78463,78464,78467,78468,78471],{},"So, firefox needs help to find itself. After checking how my X server labels my left display (:0.1) by executing\n",[471,78465,78466],{},"echo $DISPLAY"," in a shell, and checking Firefox startup options, it seemed clear that adding ",[471,78469,78470],{},"--display=:0.1"," to\nfirefox startup, will fix the issue.",[439,78473,78474],{},"So I created a small script in my homedir ‘~/bin/firefox’, `#!/bin/bash",[439,78476,78477],{},"/usr/bin/firefox --display=:0.1 \"$@\"`, and added the dir ‘~/bin/’ to my PATH environment variable, restarted the\ncorresponding shell, and failed.",[439,78479,78480,78481,78484,78485,78488,78489,1402],{},"Why? When I call ‘firefox’ from my shells, ",[471,78482,78483],{},"~/bin/firefox"," gets executed, but quassel still starts\n",[471,78486,78487],{},"/usr/bin/x-www-browser",", which was set by ",[471,78490,78456],{},[439,78492,78493,78494,78497,78498,78500],{},"So I added my personal script, to the alternatives list by\n",[471,78495,78496],{},"update-alternatives --install /usr/bin/x-www-browser x-www-browser /home/ferstl/bin/firefox 110",". As you’ve may\nspotted, I gave my version of the start script an priority of 110, so it’s usually chosen over konqueror by automated\nconfiguration. After performing another ",[471,78499,78456],{}," and choosing my script, clicking\nlinks in quassel worked.",[439,78502,78503],{},"Due to update-alternatives setting system wide configuration for all users, I added a problem for other users on my\nmachine. That could be fixed by checking for my user in the startup script, but usually I’m the only one using this\ncomputer, so that’s not a big issue.",{"title":469,"searchDepth":507,"depth":507,"links":78505},[],[1031],"2011-01-20T13:44:46","Like a lot of it-staff nowadays, I’ve got two displays connected to my Linux Box. Most of my colleagues like having one\\nbig workspace using Xinerama / TwinView, but I prefer 2 independent workspaces, even if I can’t use drag and drop\\nbetween them.","https://synyx.de/blog/one-firefox-for-two-x-servers/",{},"/blog/one-firefox-for-two-x-servers",{"title":78432,"description":78441},"blog/one-firefox-for-two-x-servers",[78515,20503,78516],"firefox","ubuntu","Like a lot of it-staff nowadays, I’ve got two displays connected to my Linux Box. Most of my colleagues like having one big workspace using Xinerama / TwinView, but I…","EOFRI2e8ZIglLn7ksz0GQIIro2GYcXaCuiiRzYFXgUY",{"id":78520,"title":78521,"author":78522,"body":78523,"category":78682,"date":78683,"description":78684,"extension":1034,"link":78685,"meta":78686,"navigation":916,"path":78687,"seo":78688,"slug":78527,"stem":78689,"tags":78690,"teaser":78691,"__hash__":78692},"blog/blog/bash-prompt-for-git.md","Bash-Prompt for Git",[27],{"type":432,"value":78524,"toc":78680},[78525,78528,78531,78540,78675,78678],[435,78526,78521],{"id":78527},"bash-prompt-for-git",[439,78529,78530],{},"Since Florian Hopf asked me today how I made my Bash prompt show the current git branch, here’s the relevant .bashrc\npart (which evloved from code found on some other blogs I don’t remember anymore).",[439,78532,78533],{},[1002,78534,78537],{"href":78535,"rel":78536},"https://media.synyx.de/uploads//2011/01/screenshot_git_bashpromt.png",[1006],[2205,78538],{"alt":469,"src":78535,"title":78539},"screenshot_git_bashpromt",[464,78541,78543],{"className":16895,"code":78542,"language":16897,"meta":469,"style":469},"function parse_git_branch {\ngit branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \\(.*\\)/(\\1)/'\n}\nfunction promptl {\nlocal BLUE=\"\\[\\033[0;34m\\]\"\nlocal RED=\"\\[\\033[0;31m\\]\"\nlocal LIGHT_RED=\"\\[\\033[1;31m\\]\"\nlocal GREEN=\"\\[\\033[0;32m\\]\"\nlocal LIGHT_GREEN=\"\\[\\033[1;32m\\]\"\nlocal WHITE=\"\\[\\033[1;37m\\]\"\nlocal LIGHT_GRAY=\"\\[\\033[0;37m\\]\"\ncase $TERM in\nxterm*)\nTITLEBAR='\\[\\033]0;\\u@\\h:\\w\\007\\]'\n;;\n*)\nTITLEBAR=\"\"\n;;\nesac\nPS1=\"${TITLEBAR}\\\n$BLUE[$RED\\$(date +%H:%M)$BLUE]\\\n$BLUE[$RED\\u@\\h:\\w$GREEN\\$(parse_git_branch)$BLUE]\\\n$GREEN\\$ $LIGHT_GRAY\"\nPS2='> '\nPS4='+ '\n}\npromptl\n",[471,78544,78545,78550,78555,78559,78564,78569,78574,78579,78584,78589,78594,78599,78604,78609,78614,78618,78623,78628,78632,78636,78641,78646,78651,78656,78661,78666,78670],{"__ignoreMap":469},[474,78546,78547],{"class":476,"line":477},[474,78548,78549],{},"function parse_git_branch {\n",[474,78551,78552],{"class":476,"line":507},[474,78553,78554],{},"git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \\(.*\\)/(\\1)/'\n",[474,78556,78557],{"class":476,"line":547},[474,78558,703],{},[474,78560,78561],{"class":476,"line":584},[474,78562,78563],{},"function promptl {\n",[474,78565,78566],{"class":476,"line":607},[474,78567,78568],{},"local BLUE=\"\\[\\033[0;34m\\]\"\n",[474,78570,78571],{"class":476,"line":642},[474,78572,78573],{},"local RED=\"\\[\\033[0;31m\\]\"\n",[474,78575,78576],{"class":476,"line":663},[474,78577,78578],{},"local LIGHT_RED=\"\\[\\033[1;31m\\]\"\n",[474,78580,78581],{"class":476,"line":694},[474,78582,78583],{},"local GREEN=\"\\[\\033[0;32m\\]\"\n",[474,78585,78586],{"class":476,"line":700},[474,78587,78588],{},"local LIGHT_GREEN=\"\\[\\033[1;32m\\]\"\n",[474,78590,78591],{"class":476,"line":913},[474,78592,78593],{},"local WHITE=\"\\[\\033[1;37m\\]\"\n",[474,78595,78596],{"class":476,"line":920},[474,78597,78598],{},"local LIGHT_GRAY=\"\\[\\033[0;37m\\]\"\n",[474,78600,78601],{"class":476,"line":926},[474,78602,78603],{},"case $TERM in\n",[474,78605,78606],{"class":476,"line":932},[474,78607,78608],{},"xterm*)\n",[474,78610,78611],{"class":476,"line":938},[474,78612,78613],{},"TITLEBAR='\\[\\033]0;\\u@\\h:\\w\\007\\]'\n",[474,78615,78616],{"class":476,"line":944},[474,78617,24122],{},[474,78619,78620],{"class":476,"line":950},[474,78621,78622],{},"*)\n",[474,78624,78625],{"class":476,"line":956},[474,78626,78627],{},"TITLEBAR=\"\"\n",[474,78629,78630],{"class":476,"line":962},[474,78631,24122],{},[474,78633,78634],{"class":476,"line":4876},[474,78635,55178],{},[474,78637,78638],{"class":476,"line":4888},[474,78639,78640],{},"PS1=\"${TITLEBAR}\\\n",[474,78642,78643],{"class":476,"line":4900},[474,78644,78645],{},"$BLUE[$RED\\$(date +%H:%M)$BLUE]\\\n",[474,78647,78648],{"class":476,"line":4913},[474,78649,78650],{},"$BLUE[$RED\\u@\\h:\\w$GREEN\\$(parse_git_branch)$BLUE]\\\n",[474,78652,78653],{"class":476,"line":4921},[474,78654,78655],{},"$GREEN\\$ $LIGHT_GRAY\"\n",[474,78657,78658],{"class":476,"line":4932},[474,78659,78660],{},"PS2='> '\n",[474,78662,78663],{"class":476,"line":4938},[474,78664,78665],{},"PS4='+ '\n",[474,78667,78668],{"class":476,"line":4946},[474,78669,703],{},[474,78671,78672],{"class":476,"line":4952},[474,78673,78674],{},"promptl\n",[439,78676,78677],{},"Feel free to copy into your .bashrc to have a colored prompt showing the current git branch if you are in a git\nrepository.",[1024,78679,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":78681},[],[1031],"2011-01-14T18:15:05","Since Florian Hopf asked me today how I made my Bash prompt show the current git branch, here’s the relevant .bashrc\\npart (which evloved from code found on some other blogs I don’t remember anymore).","https://synyx.de/blog/bash-prompt-for-git/",{},"/blog/bash-prompt-for-git",{"title":78521,"description":78530},"blog/bash-prompt-for-git",[],"Since Florian Hopf asked me today how I made my Bash prompt show the current git branch, here’s the relevant .bashrc part (which evloved from code found on some other…","3qTR4R0-5X8YWlPhZYl5ZUT9A8Zq6vOpP2c6DIkgKN8",{"id":78694,"title":78695,"author":78696,"body":78697,"category":78962,"date":78963,"description":78964,"extension":1034,"link":78965,"meta":78966,"navigation":916,"path":78967,"seo":78968,"slug":78701,"stem":78970,"tags":78971,"teaser":78973,"__hash__":78974},"blog/blog/ui-test-automation.md","UI Test Automation",[223],{"type":432,"value":78698,"toc":78960},[78699,78702,78715,78724,78732,78735,78922,78929,78932,78954,78957],[435,78700,78695],{"id":78701},"ui-test-automation",[439,78703,78704,78705,78710,78711,1402],{},"One of the most impressive talks for me at ",[1002,78706,78709],{"href":78707,"rel":78708},"http://mobile.synyx.de/2010/06/wwdc-2010/",[1006],"WWDC 2010"," was session 306 –\n“Automating Use Interface Testing with Instruments”. I’ve been wanting to check it out ever since iOS 4 was released. A\ncouple of weeks ago, I finally had a chance to give it a test ride using\nour ",[1002,78712,78714],{"href":77938,"rel":78713},[1006],"very own App “I think I spider”",[439,78716,78717,78718,78723],{},"All you need to get started is an App, Instruments and some basic JavaScript skills. Apple provides a set\nof ",[1002,78719,78722],{"href":78720,"rel":78721},"http://developer.apple.com/library/ios/#documentation/ToolsLanguages/Reference/UIATargetClassReference/UIATargetClass/UIATargetClass.html",[1006],"JavaScript libraries",",\nthat you can use to drive your tests and simulate user interaction. Your custom test scripts are run using the\nAutomation Instrument in Apple’s Instruments App, targeting your App either in the Simulator or on an actual device.",[439,78725,78726,78727,78731],{},"You can test almost every aspect of user interaction, using Apple’s JavaScript library. No matter if you want to test\nshaking, device orientation or the basics like tapping and swiping, you can do all that using basic JavaScript function\ncalls.\nApple’s ",[1002,78728,22273],{"href":78729,"rel":78730},"https://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Built-InInstruments/Built-InInstruments.html%23//apple_ref/doc/uid/TP40004652-CH6-SW75",[1006],"\nis quite solid, as most of them are, and explains the process in detail.",[439,78733,78734],{},"For “I think I spider” we covered the basic use cases in terms of UI, to make sure it still works after adding new\nfeatures. Here is a basic example of the JavaScript involved to test “opening” the book, after starting the App:",[464,78736,78738],{"className":15199,"code":78737,"language":15201,"meta":469,"style":469},"// setup\nvar target = UIATarget.localTarget();\nvar appWindow = target.frontMostApp().mainWindow();\nvar element = target;\n// first test\nvar testName = \"Start Screen Test\";\nUIALogger.logStart(testName);\nUIALogger.logMessage(\"Tapping start screen\");\nappWindow.elements()[\"start_screen\"].tap(); // open the book\nif (appWindow.elements()[\"main_screen\"].isValid()) {\n UIALogger.logFail(testName);\n} else {\n UIALogger.logPass(testName);\n}\n",[471,78739,78740,78745,78762,78785,78797,78802,78816,78827,78841,78867,78889,78899,78909,78918],{"__ignoreMap":469},[474,78741,78742],{"class":476,"line":477},[474,78743,78744],{"class":2277},"// setup\n",[474,78746,78747,78749,78752,78754,78757,78760],{"class":476,"line":507},[474,78748,46757],{"class":810},[474,78750,78751],{"class":503}," target ",[474,78753,811],{"class":810},[474,78755,78756],{"class":503}," UIATarget.",[474,78758,78759],{"class":480},"localTarget",[474,78761,15271],{"class":503},[474,78763,78764,78766,78769,78771,78774,78777,78780,78783],{"class":476,"line":547},[474,78765,46757],{"class":810},[474,78767,78768],{"class":503}," appWindow ",[474,78770,811],{"class":810},[474,78772,78773],{"class":503}," target.",[474,78775,78776],{"class":480},"frontMostApp",[474,78778,78779],{"class":503},"().",[474,78781,78782],{"class":480},"mainWindow",[474,78784,15271],{"class":503},[474,78786,78787,78789,78792,78794],{"class":476,"line":584},[474,78788,46757],{"class":810},[474,78790,78791],{"class":503}," element ",[474,78793,811],{"class":810},[474,78795,78796],{"class":503}," target;\n",[474,78798,78799],{"class":476,"line":607},[474,78800,78801],{"class":2277},"// first test\n",[474,78803,78804,78806,78809,78811,78814],{"class":476,"line":642},[474,78805,46757],{"class":810},[474,78807,78808],{"class":503}," testName ",[474,78810,811],{"class":810},[474,78812,78813],{"class":484}," \"Start Screen Test\"",[474,78815,2139],{"class":503},[474,78817,78818,78821,78824],{"class":476,"line":663},[474,78819,78820],{"class":503},"UIALogger.",[474,78822,78823],{"class":480},"logStart",[474,78825,78826],{"class":503},"(testName);\n",[474,78828,78829,78831,78834,78836,78839],{"class":476,"line":694},[474,78830,78820],{"class":503},[474,78832,78833],{"class":480},"logMessage",[474,78835,1483],{"class":503},[474,78837,78838],{"class":484},"\"Tapping start screen\"",[474,78840,1495],{"class":503},[474,78842,78843,78846,78849,78852,78855,78858,78861,78864],{"class":476,"line":700},[474,78844,78845],{"class":503},"appWindow.",[474,78847,78848],{"class":480},"elements",[474,78850,78851],{"class":503},"()[",[474,78853,78854],{"class":484},"\"start_screen\"",[474,78856,78857],{"class":503},"].",[474,78859,78860],{"class":480},"tap",[474,78862,78863],{"class":503},"(); ",[474,78865,78866],{"class":2277},"// open the book\n",[474,78868,78869,78871,78874,78876,78878,78881,78883,78886],{"class":476,"line":913},[474,78870,2283],{"class":810},[474,78872,78873],{"class":503}," (appWindow.",[474,78875,78848],{"class":480},[474,78877,78851],{"class":503},[474,78879,78880],{"class":484},"\"main_screen\"",[474,78882,78857],{"class":503},[474,78884,78885],{"class":480},"isValid",[474,78887,78888],{"class":503},"()) {\n",[474,78890,78891,78894,78897],{"class":476,"line":920},[474,78892,78893],{"class":503}," UIALogger.",[474,78895,78896],{"class":480},"logFail",[474,78898,78826],{"class":503},[474,78900,78901,78904,78907],{"class":476,"line":926},[474,78902,78903],{"class":503},"} ",[474,78905,78906],{"class":810},"else",[474,78908,14027],{"class":503},[474,78910,78911,78913,78916],{"class":476,"line":932},[474,78912,78893],{"class":503},[474,78914,78915],{"class":480},"logPass",[474,78917,78826],{"class":503},[474,78919,78920],{"class":476,"line":938},[474,78921,703],{"class":503},[439,78923,78924,78925,78928],{},"You can see that the JavaScript API is quite easy to use and yet very powerful! After setting this up in the Automation\nInstrument and running it, you can see the Simulator firing up and tapping the ",[990,78926,78927],{},"UIImageView"," with the accessibility\nlabel “startscreen” after it became available. It then tests, if the main screen of the App was loaded and either passes\nor fails the test.",[439,78930,78931],{},"In order to make our App testable, we had to set the accessibility labels on the elements we wanted to reference from\nthe script. That was the only change we had to make in our code. Since you should take accessibility into consideration\nanyways, it was a reasonable effort.",[439,78933,78934,78935,74182,78940,78945,78946,78949,78950,78953],{},"Apple did an awesome job giving us developers the ability to catch regressions and make our life easier. However, there\nis room for improvement, which has been\nnicely ",[1002,78936,78939],{"href":78937,"rel":78938},"http://blog.airsource.co.uk/index.php/2010/08/13/ui-automation-on-the-iphone/",[1006],"summarized",[1002,78941,78944],{"href":78942,"rel":78943},"http://www.airsource.co.uk",[1006],"Air Source",". For us, a missing ",[990,78947,78948],{},"UIALogger.warn"," function in the JavaScript library was\nthe biggest downside. Sometimes it’s okay for a test to fail under certain conditions, but you still want to get a\nwarning about it. We use ",[990,78951,78952],{},"UIALogger.logMessage"," for those cases as a workaround, but it’s quite easy to miss those\nlines, since they don’t stand out.",[439,78955,78956],{},"Overall, we think it’s a huge improvement to have a UI testing tool for iOS Apps at hand. There is room for improvement,\nbut the current state of UI Test Automation is already priceless!",[1024,78958,78959],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":78961},[],[11122,1413],"2011-01-13T09:20:09","One of the most impressive talks for me at WWDC 2010 was session 306 –\\n“Automating Use Interface Testing with Instruments”. I’ve been wanting to check it out ever since iOS 4 was released. A\\ncouple of weeks ago, I finally had a chance to give it a test ride using\\nour very own App “I think I spider”.","https://synyx.de/blog/ui-test-automation/",{},"/blog/ui-test-automation",{"title":78695,"description":78969},"One of the most impressive talks for me at WWDC 2010 was session 306 –\n“Automating Use Interface Testing with Instruments”. I’ve been wanting to check it out ever since iOS 4 was released. A\ncouple of weeks ago, I finally had a chance to give it a test ride using\nour very own App “I think I spider”.","blog/ui-test-automation",[63945,78972,78398,78399,21404],"ipad","One of the most impressive talks for me at WWDC 2010 was session 306 – “Automating Use Interface Testing with Instruments”. I’ve been wanting to check it out ever since…","aYYRgsmcoAvx1nUBfqtRNIHrvF2TO_aTXNHMamZLGWQ",{"id":78976,"title":77277,"author":78977,"body":78978,"category":79062,"date":79063,"description":79064,"extension":1034,"link":79065,"meta":79066,"navigation":916,"path":79067,"seo":79068,"slug":78982,"stem":79070,"tags":79071,"teaser":79073,"__hash__":79074},"blog/blog/spring-ide-into-eclipse.md",[314],{"type":432,"value":78979,"toc":79060},[78980,78983,78998,79015,79018,79021,79026,79041,79044,79047],[435,78981,77277],{"id":78982},"spring-ide-into-eclipse",[439,78984,78985,78986,78991,78992,78997],{},"Today, I tried to install parts of the ",[1002,78987,78990],{"href":78988,"rel":78989},"http://www.springsource.com/developer/sts",[1006],"SpringSource Tool Suite","\ninto ",[1002,78993,78996],{"href":78994,"rel":78995},"http://www.eclipse.org/",[1006],"Eclipse"," Helios SR1 via update-site.",[439,78999,79000,79001,79008,79009,79014],{},"After finding the right update-site of the STS for version 3.6 of eclipse (namely: *\n",[990,79002,79003],{},[1002,79004,79007],{"href":79005,"rel":79006},"http://dist.springsource.com/release/TOOLS/update/e3%5C.6",[1006],"http://dist.springsource.com/release/TOOLS/update/e3\\.6","* and * *",[1002,79010,79013],{"href":79011,"rel":79012},"http://dist.springsource.com/release/TOOLS/composite/e3%5C.6",[1006],"http://dist.springsource.com/release/TOOLS/composite/e3\\.6","**) and following the installation instructions carefully, I\nhad Spring IDE 2.5.2 successfully integrated.",[439,79016,79017],{},"But after restarting eclipse I was shocked, it seemed that the update-manager broke down…",[439,79019,79020],{},"when opening the update-manager the log gasped out:",[439,79022,1454,79023,1458],{},[990,79024,79025],{},"An internal error occurred during: Contacting Software Sites. java.lang.NullPointerException…",[439,79027,79028,79029,79034,79035,79040],{},"didn’t sound good. After quite a lot of researching and try and failing, I was very glad to find\na ",[1002,79030,79033],{"href":79031,"rel":79032},"https://jira.springsource.org/browse/IDE-1163?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#issue-tabs",[1006],"bug report",",\nwhich mentioned that this occurs on updating Spring IDE. Thanks\nto ",[1002,79036,79039],{"href":79037,"rel":79038},"http://www.springsource.com/people/cdupuis",[1006],"Christian Dupuis"," from SpringSource, who provided a workaround\nyesterday. For some reason a second org.apache.commons.httpclient_3.1.0….jar was shipped and confused eclipse. But\nafter commenting one of them out in “configuration/org.eclipse.equinox.simpleconfigurator/bundles.info”",[439,79042,79043],{},"everything was working well again.",[439,79045,79046],{},"Now I can enjoy my Spring IDE and perhaps you can so, too.",[439,79048,79049,79050,79053,79054,79059],{},"edit: in the meantime ",[1002,79051,79039],{"href":79037,"rel":79052},[1006]," already delivered\na ",[1002,79055,79058],{"href":79056,"rel":79057},"http://dist.springsource.com/release/TOOLS/update/2.5.2.SR1/e3.6/springsource-tool-suite-2.5.2.RELEASE-e3.6-updatesite.zip",[1006],"quickly fixed update-archive","\nwhich makes the file manipulating superfluous… Hell these guys are quite fast!",{"title":469,"searchDepth":507,"depth":507,"links":79061},[],[1030],"2011-01-12T18:08:02","Today, I tried to install parts of the SpringSource Tool Suite\\ninto Eclipse Helios SR1 via update-site.","https://synyx.de/blog/spring-ide-into-eclipse/",{},"/blog/spring-ide-into-eclipse",{"title":77277,"description":79069},"Today, I tried to install parts of the SpringSource Tool Suite\ninto Eclipse Helios SR1 via update-site.","blog/spring-ide-into-eclipse",[29262,79072,1426],"eclipse","Today, I tried to install parts of the SpringSource Tool Suite into Eclipse Helios SR1 via update-site. After finding the right update-site of the STS for version 3.6 of eclipse…","Vzr0Xsie2Ex7qlzLaFPeSz-Qud27P-61q_-keeNuXk8",{"id":79076,"title":79077,"author":79078,"body":79079,"category":79947,"date":79948,"description":79949,"extension":1034,"link":79950,"meta":79951,"navigation":916,"path":79952,"seo":79953,"slug":79083,"stem":79955,"tags":79956,"teaser":79957,"__hash__":79958},"blog/blog/scripting-opencms.md","Scripting OpenCms",[148],{"type":432,"value":79080,"toc":79945},[79081,79084,79097,79104,79116,79465,79468,79569,79572,79595,79598,79621,79624,79629,79632,79939,79942],[435,79082,79077],{"id":79083},"scripting-opencms",[439,79085,79086,79090,79091,79096],{},[1002,79087,71236],{"href":79088,"rel":79089},"http://opencms.org/de/",[1006]," ships with a shell script for accessing the virtual file system from the command line.\nThis is useful for a lot of administrative tasks like importing modules or exporting content. You can supply scripts\nthat issue commands or use the shell interactively. As the syntax for the scripts is quite limited some tasks can’t be\nachieved that easily. ",[1002,79092,79095],{"href":79093,"rel":79094},"http://blog.codecentric.de/en/2010/04/how-to-automate-opencms-module-import/",[1006],"This blogpost","\ndescribes a way to circumvent these problems by generating the script files.",[439,79098,79099,79100,79103],{},"I will present a simple extension of this mechanism that makes it possible to access the VFS using\nthe ",[1002,79101,28458],{"href":71640,"rel":79102},[1006]," scripting language that also runs on the Java Virtual Machine. It can be useful\nto import users or content from different systems, create content for test runs or any other task you can think of.",[439,79105,79106,79107,79110,79111,79115],{},"The original script file for accessing the cms shell is located in ",[471,79108,79109],{},"WEB-INF/cmsshell.sh"," (\nSee ",[1002,79112,22916],{"href":79113,"rel":79114},"http://blog.synyx.de/2010/11/using-cmsshell-on-ubuntu-systems/",[1006]," on how to make it work on Ubuntu systems).\nThe following file is a modification of the original file that doesn’t call the underlying Java class but another groovy\nscript and should be placed next to the original:",[464,79117,79119],{"className":54685,"code":79118,"language":54687,"meta":469,"style":469},"\n#!/bin/sh\n# Script for evaluating groovy scripts with access to opencms.\n#\n# Please make sure that \"servlet-api.jar\" and \"jsp-api.jar\" are found.\n#\n# get path to opencms base directory\nOPENCMS_BASE=`pwd`\n# get path to tomcat home\nif [ -z \"$TOMCAT_HOME\" ]; then\n [ -n \"$CATALINA_HOME\" ] && TOMCAT_HOME=\"$CATALINA_HOME\"\n [ -z \"$TOMCAT_HOME\" ] && TOMCAT_HOME=\"$OPENCMS_BASE\"/../../..\nfi\nTOMCAT_CLASSPATH=\"\"\n# Support for tomcat 5\nfor JAR in ${TOMCAT_HOME}/common/lib/*.jar; do\n TOMCAT_CLASSPATH=\"${TOMCAT_CLASSPATH}:${JAR}\"\ndone\nfor JAR in ${TOMCAT_HOME}/shared/lib/*.jar; do\n TOMCAT_CLASSPATH=\"${TOMCAT_CLASSPATH}:${JAR}\"\ndone\n# Support for tomcat 6\nfor JAR in ${TOMCAT_HOME}/lib/*.jar; do\n TOMCAT_CLASSPATH=\"${TOMCAT_CLASSPATH}:${JAR}\"\ndone\nOPENCMS_CLASSPATH=\"\"\nfor JAR in ${OPENCMS_BASE}/lib/*.jar; do\n OPENCMS_CLASSPATH=\"${OPENCMS_CLASSPATH}:${JAR}\"\ndone\ngroovy -classpath \"${OPENCMS_CLASSPATH}:${TOMCAT_CLASSPATH}:classes\" evalscripts.groovy -base=\"${OPENCMS_BASE}\" \"$@\"\n\n",[471,79120,79121,79125,79129,79134,79138,79143,79147,79152,79166,79171,79190,79215,79239,79243,79253,79258,79277,79297,79301,79318,79334,79338,79343,79360,79376,79380,79389,79406,79423,79427],{"__ignoreMap":469},[474,79122,79123],{"class":476,"line":477},[474,79124,917],{"emptyLinePlaceholder":916},[474,79126,79127],{"class":476,"line":507},[474,79128,6785],{"class":2277},[474,79130,79131],{"class":476,"line":547},[474,79132,79133],{"class":2277},"# Script for evaluating groovy scripts with access to opencms.\n",[474,79135,79136],{"class":476,"line":584},[474,79137,40924],{"class":2277},[474,79139,79140],{"class":476,"line":607},[474,79141,79142],{"class":2277},"# Please make sure that \"servlet-api.jar\" and \"jsp-api.jar\" are found.\n",[474,79144,79145],{"class":476,"line":642},[474,79146,40924],{"class":2277},[474,79148,79149],{"class":476,"line":663},[474,79150,79151],{"class":2277},"# get path to opencms base directory\n",[474,79153,79154,79157,79159,79161,79164],{"class":476,"line":694},[474,79155,79156],{"class":503},"OPENCMS_BASE",[474,79158,811],{"class":810},[474,79160,8990],{"class":484},[474,79162,79163],{"class":510},"pwd",[474,79165,9018],{"class":484},[474,79167,79168],{"class":476,"line":700},[474,79169,79170],{"class":2277},"# get path to tomcat home\n",[474,79172,79173,79175,79177,79179,79181,79184,79186,79188],{"class":476,"line":913},[474,79174,2283],{"class":810},[474,79176,2286],{"class":503},[474,79178,55212],{"class":810},[474,79180,2720],{"class":484},[474,79182,79183],{"class":503},"$TOMCAT_HOME",[474,79185,2289],{"class":484},[474,79187,2339],{"class":503},[474,79189,2308],{"class":810},[474,79191,79192,79195,79197,79199,79202,79204,79207,79209,79211,79213],{"class":476,"line":920},[474,79193,79194],{"class":503}," [ ",[474,79196,68189],{"class":810},[474,79198,2720],{"class":484},[474,79200,79201],{"class":503},"$CATALINA_HOME",[474,79203,2289],{"class":484},[474,79205,79206],{"class":503}," ] && TOMCAT_HOME",[474,79208,811],{"class":810},[474,79210,2289],{"class":484},[474,79212,79201],{"class":503},[474,79214,2744],{"class":484},[474,79216,79217,79219,79221,79223,79225,79227,79229,79231,79233,79236],{"class":476,"line":926},[474,79218,79194],{"class":503},[474,79220,55212],{"class":810},[474,79222,2720],{"class":484},[474,79224,79183],{"class":503},[474,79226,2289],{"class":484},[474,79228,79206],{"class":503},[474,79230,811],{"class":810},[474,79232,2289],{"class":484},[474,79234,79235],{"class":503},"$OPENCMS_BASE",[474,79237,79238],{"class":484},"\"/../../..\n",[474,79240,79241],{"class":476,"line":932},[474,79242,2354],{"class":810},[474,79244,79245,79248,79250],{"class":476,"line":938},[474,79246,79247],{"class":503},"TOMCAT_CLASSPATH",[474,79249,811],{"class":810},[474,79251,79252],{"class":484},"\"\"\n",[474,79254,79255],{"class":476,"line":944},[474,79256,79257],{"class":2277},"# Support for tomcat 5\n",[474,79259,79260,79262,79265,79267,79270,79273,79275],{"class":476,"line":950},[474,79261,2674],{"class":810},[474,79263,79264],{"class":503}," JAR ",[474,79266,2680],{"class":810},[474,79268,79269],{"class":503}," ${TOMCAT_HOME}",[474,79271,79272],{"class":484},"/common/lib/*.jar",[474,79274,55136],{"class":503},[474,79276,2701],{"class":810},[474,79278,79279,79282,79284,79286,79288,79291,79294],{"class":476,"line":956},[474,79280,79281],{"class":503}," TOMCAT_CLASSPATH",[474,79283,811],{"class":810},[474,79285,68238],{"class":484},[474,79287,79247],{"class":503},[474,79289,79290],{"class":484},"}:${",[474,79292,79293],{"class":503},"JAR",[474,79295,79296],{"class":484},"}\"\n",[474,79298,79299],{"class":476,"line":962},[474,79300,2879],{"class":810},[474,79302,79303,79305,79307,79309,79311,79314,79316],{"class":476,"line":4876},[474,79304,2674],{"class":810},[474,79306,79264],{"class":503},[474,79308,2680],{"class":810},[474,79310,79269],{"class":503},[474,79312,79313],{"class":484},"/shared/lib/*.jar",[474,79315,55136],{"class":503},[474,79317,2701],{"class":810},[474,79319,79320,79322,79324,79326,79328,79330,79332],{"class":476,"line":4888},[474,79321,79281],{"class":503},[474,79323,811],{"class":810},[474,79325,68238],{"class":484},[474,79327,79247],{"class":503},[474,79329,79290],{"class":484},[474,79331,79293],{"class":503},[474,79333,79296],{"class":484},[474,79335,79336],{"class":476,"line":4900},[474,79337,2879],{"class":810},[474,79339,79340],{"class":476,"line":4913},[474,79341,79342],{"class":2277},"# Support for tomcat 6\n",[474,79344,79345,79347,79349,79351,79353,79356,79358],{"class":476,"line":4921},[474,79346,2674],{"class":810},[474,79348,79264],{"class":503},[474,79350,2680],{"class":810},[474,79352,79269],{"class":503},[474,79354,79355],{"class":484},"/lib/*.jar",[474,79357,55136],{"class":503},[474,79359,2701],{"class":810},[474,79361,79362,79364,79366,79368,79370,79372,79374],{"class":476,"line":4932},[474,79363,79281],{"class":503},[474,79365,811],{"class":810},[474,79367,68238],{"class":484},[474,79369,79247],{"class":503},[474,79371,79290],{"class":484},[474,79373,79293],{"class":503},[474,79375,79296],{"class":484},[474,79377,79378],{"class":476,"line":4938},[474,79379,2879],{"class":810},[474,79381,79382,79385,79387],{"class":476,"line":4946},[474,79383,79384],{"class":503},"OPENCMS_CLASSPATH",[474,79386,811],{"class":810},[474,79388,79252],{"class":484},[474,79390,79391,79393,79395,79397,79400,79402,79404],{"class":476,"line":4952},[474,79392,2674],{"class":810},[474,79394,79264],{"class":503},[474,79396,2680],{"class":810},[474,79398,79399],{"class":503}," ${OPENCMS_BASE}",[474,79401,79355],{"class":484},[474,79403,55136],{"class":503},[474,79405,2701],{"class":810},[474,79407,79408,79411,79413,79415,79417,79419,79421],{"class":476,"line":4957},[474,79409,79410],{"class":503}," OPENCMS_CLASSPATH",[474,79412,811],{"class":810},[474,79414,68238],{"class":484},[474,79416,79384],{"class":503},[474,79418,79290],{"class":484},[474,79420,79293],{"class":503},[474,79422,79296],{"class":484},[474,79424,79425],{"class":476,"line":4969},[474,79426,2879],{"class":810},[474,79428,79429,79431,79434,79437,79439,79441,79443,79446,79449,79452,79454,79456,79458,79460,79463],{"class":476,"line":4990},[474,79430,28458],{"class":480},[474,79432,79433],{"class":510}," -classpath",[474,79435,79436],{"class":484}," \"${",[474,79438,79384],{"class":503},[474,79440,79290],{"class":484},[474,79442,79247],{"class":503},[474,79444,79445],{"class":484},"}:classes\"",[474,79447,79448],{"class":484}," evalscripts.groovy",[474,79450,79451],{"class":510}," -base=",[474,79453,68238],{"class":484},[474,79455,79156],{"class":503},[474,79457,14141],{"class":484},[474,79459,2720],{"class":484},[474,79461,79462],{"class":510},"$@",[474,79464,2744],{"class":484},[439,79466,79467],{},"As you can see, a groovy script named “evalscripts.groovy” is called and all options are passed to it. The script:",[464,79469,79471],{"className":28456,"code":79470,"language":28458,"meta":469,"style":469},"\nimport org.opencms.main.CmsShell;\nimport org.opencms.file.CmsObject;\nbase = args[0].substring(CmsShell.SHELL_PARAM_BASE.length());\nshell = new CmsShell(base, null, null, \">\", null) {\n CmsObject getCmsObject() {\n return m_cms;\n }\n}\nuser = \"Admin\";\npass = \"admin\";\ncms = shell.getCmsObject();\ncms.loginUser(user, pass);\nbinding1 = new Binding();\nbinding1.setProperty('cmsObject' , cms);\ngroovyShell = new GroovyShell(binding1);\nfor (int i = 1; i \u003C args.length; i++) {\n groovyShell.evaluate(new File(args[i]))\n}\nshell.exit();\n\n",[471,79472,79473,79477,79482,79487,79492,79497,79502,79507,79511,79515,79520,79525,79530,79535,79540,79545,79550,79555,79560,79564],{"__ignoreMap":469},[474,79474,79475],{"class":476,"line":477},[474,79476,917],{"emptyLinePlaceholder":916},[474,79478,79479],{"class":476,"line":507},[474,79480,79481],{},"import org.opencms.main.CmsShell;\n",[474,79483,79484],{"class":476,"line":547},[474,79485,79486],{},"import org.opencms.file.CmsObject;\n",[474,79488,79489],{"class":476,"line":584},[474,79490,79491],{},"base = args[0].substring(CmsShell.SHELL_PARAM_BASE.length());\n",[474,79493,79494],{"class":476,"line":607},[474,79495,79496],{},"shell = new CmsShell(base, null, null, \">\", null) {\n",[474,79498,79499],{"class":476,"line":642},[474,79500,79501],{}," CmsObject getCmsObject() {\n",[474,79503,79504],{"class":476,"line":663},[474,79505,79506],{}," return m_cms;\n",[474,79508,79509],{"class":476,"line":694},[474,79510,1276],{},[474,79512,79513],{"class":476,"line":700},[474,79514,703],{},[474,79516,79517],{"class":476,"line":913},[474,79518,79519],{},"user = \"Admin\";\n",[474,79521,79522],{"class":476,"line":920},[474,79523,79524],{},"pass = \"admin\";\n",[474,79526,79527],{"class":476,"line":926},[474,79528,79529],{},"cms = shell.getCmsObject();\n",[474,79531,79532],{"class":476,"line":932},[474,79533,79534],{},"cms.loginUser(user, pass);\n",[474,79536,79537],{"class":476,"line":938},[474,79538,79539],{},"binding1 = new Binding();\n",[474,79541,79542],{"class":476,"line":944},[474,79543,79544],{},"binding1.setProperty('cmsObject' , cms);\n",[474,79546,79547],{"class":476,"line":950},[474,79548,79549],{},"groovyShell = new GroovyShell(binding1);\n",[474,79551,79552],{"class":476,"line":956},[474,79553,79554],{},"for (int i = 1; i \u003C args.length; i++) {\n",[474,79556,79557],{"class":476,"line":962},[474,79558,79559],{}," groovyShell.evaluate(new File(args[i]))\n",[474,79561,79562],{"class":476,"line":4876},[474,79563,703],{},[474,79565,79566],{"class":476,"line":4888},[474,79567,79568],{},"shell.exit();\n",[439,79570,79571],{},"We start by creating an instance of the CmsShell class and make the underlying CmsObject accessible. We login using the\nAdmin user and bind the instance so we can use it in the scripts that are doing the real work. This is where you come\ninto play: You can write any groovy script that uses this CmsObject and do whatever you want. Some ideas? Why not create\nsome users:",[464,79573,79575],{"className":28456,"code":79574,"language":28458,"meta":469,"style":469},"\n10.times {\n cmsObject.createUser(\"User$it\", \"Pass$it\", \"\", new HashMap());\n}\n\n",[471,79576,79577,79581,79586,79591],{"__ignoreMap":469},[474,79578,79579],{"class":476,"line":477},[474,79580,917],{"emptyLinePlaceholder":916},[474,79582,79583],{"class":476,"line":507},[474,79584,79585],{},"10.times {\n",[474,79587,79588],{"class":476,"line":547},[474,79589,79590],{}," cmsObject.createUser(\"User$it\", \"Pass$it\", \"\", new HashMap());\n",[474,79592,79593],{"class":476,"line":584},[474,79594,703],{},[439,79596,79597],{},"Or list all users:",[464,79599,79601],{"className":28456,"code":79600,"language":28458,"meta":469,"style":469},"\ncmsObject.getUsers().each {\n println it.name\n}\n\n",[471,79602,79603,79607,79612,79617],{"__ignoreMap":469},[474,79604,79605],{"class":476,"line":477},[474,79606,917],{"emptyLinePlaceholder":916},[474,79608,79609],{"class":476,"line":507},[474,79610,79611],{},"cmsObject.getUsers().each {\n",[474,79613,79614],{"class":476,"line":547},[474,79615,79616],{}," println it.name\n",[474,79618,79619],{"class":476,"line":584},[474,79620,703],{},[439,79622,79623],{},"How do you use it? You pass the path to the scripts that contain your logic to the shell script and it will execute them\nautomatically. Suppose the shell script is named groovyshell.sh and the groovy files are named createUsers.groovy and\nlistUsers.groovy. Execute them like this:",[439,79625,79626],{},[471,79627,79628],{},"./groovyshell.sh createUsers.groovy listUsers.groovy",[439,79630,79631],{},"You will see the familiar OpenCms startup sequence followed by the output of the second script:",[464,79633,79635],{"className":54685,"code":79634,"language":54687,"meta":469,"style":469},"\n...\nWelcome to the OpenCms shell!\nThis is OpenCms 7.5.x.\nCopyright (c) 2010 Alkacon Software GmbH\nOpenCms comes with ABSOLUTELY NO WARRANTY\nThis is free software, and you are welcome to\nredistribute it under certain conditions.\nPlease see the GNU Lesser General Public Licence for\nfurther details.\nhelp Shows this text.\nhelp * Shows the signatures of all available methods.\nhelp {string} Shows the signatures of all methods containing this string.\nexit or quit Leaves this OpenCms Shell.\nAdmin\nExport\nGuest\nUser0\nUser1\nUser2\nUser3\nUser4\nUser5\nUser6\nUser7\nUser8\nUser9\nGoodbye!\n...\n\n",[471,79636,79637,79641,79645,79660,79672,79680,79698,79724,79741,79769,79777,79790,79815,79844,79865,79870,79875,79880,79885,79890,79895,79900,79905,79910,79915,79920,79925,79930,79935],{"__ignoreMap":469},[474,79638,79639],{"class":476,"line":477},[474,79640,917],{"emptyLinePlaceholder":916},[474,79642,79643],{"class":476,"line":507},[474,79644,14198],{"class":510},[474,79646,79647,79650,79652,79654,79657],{"class":476,"line":547},[474,79648,79649],{"class":480},"Welcome",[474,79651,799],{"class":484},[474,79653,533],{"class":484},[474,79655,79656],{"class":484}," OpenCms",[474,79658,79659],{"class":484}," shell!\n",[474,79661,79662,79665,79667,79669],{"class":476,"line":584},[474,79663,79664],{"class":480},"This",[474,79666,15134],{"class":484},[474,79668,79656],{"class":484},[474,79670,79671],{"class":484}," 7.5.x.\n",[474,79673,79674,79677],{"class":476,"line":607},[474,79675,79676],{"class":480},"Copyright",[474,79678,79679],{"class":503}," (c) 2010 Alkacon Software GmbH\n",[474,79681,79682,79684,79687,79689,79692,79695],{"class":476,"line":642},[474,79683,71236],{"class":480},[474,79685,79686],{"class":484}," comes",[474,79688,1594],{"class":484},[474,79690,79691],{"class":484}," ABSOLUTELY",[474,79693,79694],{"class":484}," NO",[474,79696,79697],{"class":484}," WARRANTY\n",[474,79699,79700,79702,79704,79707,79710,79712,79715,79718,79721],{"class":476,"line":663},[474,79701,79664],{"class":480},[474,79703,15134],{"class":484},[474,79705,79706],{"class":484}," free",[474,79708,79709],{"class":484}," software,",[474,79711,2605],{"class":484},[474,79713,79714],{"class":484}," you",[474,79716,79717],{"class":484}," are",[474,79719,79720],{"class":484}," welcome",[474,79722,79723],{"class":484}," to\n",[474,79725,79726,79729,79732,79735,79738],{"class":476,"line":694},[474,79727,79728],{"class":480},"redistribute",[474,79730,79731],{"class":484}," it",[474,79733,79734],{"class":484}," under",[474,79736,79737],{"class":484}," certain",[474,79739,79740],{"class":484}," conditions.\n",[474,79742,79743,79746,79749,79751,79754,79757,79760,79763,79766],{"class":476,"line":700},[474,79744,79745],{"class":480},"Please",[474,79747,79748],{"class":484}," see",[474,79750,533],{"class":484},[474,79752,79753],{"class":484}," GNU",[474,79755,79756],{"class":484}," Lesser",[474,79758,79759],{"class":484}," General",[474,79761,79762],{"class":484}," Public",[474,79764,79765],{"class":484}," Licence",[474,79767,79768],{"class":484}," for\n",[474,79770,79771,79774],{"class":476,"line":913},[474,79772,79773],{"class":480},"further",[474,79775,79776],{"class":484}," details.\n",[474,79778,79779,79782,79785,79787],{"class":476,"line":920},[474,79780,79781],{"class":480},"help",[474,79783,79784],{"class":484}," Shows",[474,79786,2596],{"class":484},[474,79788,79789],{"class":484}," text.\n",[474,79791,79792,79794,79796,79799,79801,79804,79806,79809,79812],{"class":476,"line":926},[474,79793,79781],{"class":480},[474,79795,1474],{"class":510},[474,79797,79798],{"class":484}," Shows",[474,79800,533],{"class":484},[474,79802,79803],{"class":484}," signatures",[474,79805,530],{"class":484},[474,79807,79808],{"class":484}," all",[474,79810,79811],{"class":484}," available",[474,79813,79814],{"class":484}," methods.\n",[474,79816,79817,79819,79822,79825,79827,79829,79831,79833,79836,79839,79841],{"class":476,"line":932},[474,79818,79781],{"class":480},[474,79820,79821],{"class":484}," {string}",[474,79823,79824],{"class":484}," Shows",[474,79826,533],{"class":484},[474,79828,79803],{"class":484},[474,79830,530],{"class":484},[474,79832,79808],{"class":484},[474,79834,79835],{"class":484}," methods",[474,79837,79838],{"class":484}," containing",[474,79840,2596],{"class":484},[474,79842,79843],{"class":484}," string.\n",[474,79845,79846,79849,79852,79855,79858,79860,79862],{"class":476,"line":938},[474,79847,79848],{"class":510},"exit",[474,79850,79851],{"class":484}," or",[474,79853,79854],{"class":484}," quit",[474,79856,79857],{"class":484}," Leaves",[474,79859,2596],{"class":484},[474,79861,79656],{"class":484},[474,79863,79864],{"class":484}," Shell.\n",[474,79866,79867],{"class":476,"line":944},[474,79868,79869],{"class":480},"Admin\n",[474,79871,79872],{"class":476,"line":950},[474,79873,79874],{"class":480},"Export\n",[474,79876,79877],{"class":476,"line":956},[474,79878,79879],{"class":480},"Guest\n",[474,79881,79882],{"class":476,"line":962},[474,79883,79884],{"class":480},"User0\n",[474,79886,79887],{"class":476,"line":4876},[474,79888,79889],{"class":480},"User1\n",[474,79891,79892],{"class":476,"line":4888},[474,79893,79894],{"class":480},"User2\n",[474,79896,79897],{"class":476,"line":4900},[474,79898,79899],{"class":480},"User3\n",[474,79901,79902],{"class":476,"line":4913},[474,79903,79904],{"class":480},"User4\n",[474,79906,79907],{"class":476,"line":4921},[474,79908,79909],{"class":480},"User5\n",[474,79911,79912],{"class":476,"line":4932},[474,79913,79914],{"class":480},"User6\n",[474,79916,79917],{"class":476,"line":4938},[474,79918,79919],{"class":480},"User7\n",[474,79921,79922],{"class":476,"line":4946},[474,79923,79924],{"class":480},"User8\n",[474,79926,79927],{"class":476,"line":4952},[474,79928,79929],{"class":480},"User9\n",[474,79931,79932],{"class":476,"line":4957},[474,79933,79934],{"class":480},"Goodbye!\n",[474,79936,79937],{"class":476,"line":4969},[474,79938,14198],{"class":510},[439,79940,79941],{},"I think this will be useful for us in the future, maybe also for you?",[1024,79943,79944],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":79946},[],[1030,1412],"2011-01-10T15:16:36","OpenCms ships with a shell script for accessing the virtual file system from the command line.\\nThis is useful for a lot of administrative tasks like importing modules or exporting content. You can supply scripts\\nthat issue commands or use the shell interactively. As the syntax for the scripts is quite limited some tasks can’t be\\nachieved that easily. This blogpost\\ndescribes a way to circumvent these problems by generating the script files.","https://synyx.de/blog/scripting-opencms/",{},"/blog/scripting-opencms",{"title":79077,"description":79954},"OpenCms ships with a shell script for accessing the virtual file system from the command line.\nThis is useful for a lot of administrative tasks like importing modules or exporting content. You can supply scripts\nthat issue commands or use the shell interactively. As the syntax for the scripts is quite limited some tasks can’t be\nachieved that easily. This blogpost\ndescribes a way to circumvent these problems by generating the script files.","blog/scripting-opencms",[28458,64517],"OpenCms ships with a shell script for accessing the virtual file system from the command line. This is useful for a lot of administrative tasks like importing modules or exporting…","v8fncIfA9ZnYPM1Luj9Uvx7ZbqqqhEZaXHZoobwoM-0",{"id":79960,"title":79961,"author":79962,"body":79963,"category":80362,"date":80363,"description":80364,"extension":1034,"link":80365,"meta":80366,"navigation":916,"path":80367,"seo":80368,"slug":79967,"stem":80370,"tags":80371,"teaser":80372,"__hash__":80373},"blog/blog/integration-tests-for-your-solr-config.md","Integration tests for your Solr config",[148],{"type":432,"value":79964,"toc":80360},[79965,79968,79979,79987,79990,80118,80121,80124,80127,80358],[435,79966,79961],{"id":79967},"integration-tests-for-your-solr-config",[439,79969,79970,79973,79974,79978],{},[1002,79971,76852],{"href":76850,"rel":79972},[1006]," is a search server that bundles a lot of\nuseful ",[1002,79975,79977],{"href":76856,"rel":79976},[1006],"Lucene"," modules and provides an HTTP interface for querying and updating the data.\nThe index and most of the query mechanisms are configured using XML documents, client applications normally don’t need\nto be changed when adjusting the server configuration. As the server configuration heavily influences the quality of\nyour users search experience it’s a good idea to implement some integration tests that validate your functionality.",[439,79980,79981,79982,79986],{},"Solr ships with a useful abstract ",[1002,79983,34176],{"href":79984,"rel":79985},"http://junit.org/",[1006]," test case that can be used as a basis for your integration\ntests. I will demonstrate how to fire up a simple test using maven.",[439,79988,79989],{},"The most important part is the dependencies section of the pom:",[464,79991,79993],{"className":16895,"code":79992,"language":16897,"meta":469,"style":469},"\n \u003Cdependency>\n \u003CgroupId>junit\u003C/groupId>\n \u003CartifactId>junit\u003C/artifactId>\n \u003Cversion>4.7\u003C/version>\n \u003Ctype>jar\u003C/type>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n \u003C!-- dependencies needed for Solr integration test-->\n \u003Cdependency>\n \u003CgroupId>org.apache.solr\u003C/groupId>\n \u003CartifactId>solr-core\u003C/artifactId>\n \u003Cversion>1.4.1\u003C/version>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>org.slf4j\u003C/groupId>\n \u003CartifactId>slf4j-simple\u003C/artifactId>\n \u003Cversion>1.6.1\u003C/version>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n \u003Cdependency>\n \u003CgroupId>javax.servlet\u003C/groupId>\n \u003CartifactId>servlet-api\u003C/artifactId>\n \u003Cversion>2.5\u003C/version>\n \u003Cscope>test\u003C/scope>\n \u003C/dependency>\n\n",[471,79994,79995,79999,80003,80008,80013,80018,80023,80028,80032,80037,80041,80046,80051,80056,80060,80064,80068,80073,80078,80083,80087,80091,80095,80100,80105,80110,80114],{"__ignoreMap":469},[474,79996,79997],{"class":476,"line":477},[474,79998,917],{"emptyLinePlaceholder":916},[474,80000,80001],{"class":476,"line":507},[474,80002,50062],{},[474,80004,80005],{"class":476,"line":547},[474,80006,80007],{}," \u003CgroupId>junit\u003C/groupId>\n",[474,80009,80010],{"class":476,"line":584},[474,80011,80012],{}," \u003CartifactId>junit\u003C/artifactId>\n",[474,80014,80015],{"class":476,"line":607},[474,80016,80017],{}," \u003Cversion>4.7\u003C/version>\n",[474,80019,80020],{"class":476,"line":642},[474,80021,80022],{}," \u003Ctype>jar\u003C/type>\n",[474,80024,80025],{"class":476,"line":663},[474,80026,80027],{}," \u003Cscope>test\u003C/scope>\n",[474,80029,80030],{"class":476,"line":694},[474,80031,50077],{},[474,80033,80034],{"class":476,"line":700},[474,80035,80036],{}," \u003C!-- dependencies needed for Solr integration test-->\n",[474,80038,80039],{"class":476,"line":913},[474,80040,50062],{},[474,80042,80043],{"class":476,"line":920},[474,80044,80045],{}," \u003CgroupId>org.apache.solr\u003C/groupId>\n",[474,80047,80048],{"class":476,"line":926},[474,80049,80050],{}," \u003CartifactId>solr-core\u003C/artifactId>\n",[474,80052,80053],{"class":476,"line":932},[474,80054,80055],{}," \u003Cversion>1.4.1\u003C/version>\n",[474,80057,80058],{"class":476,"line":938},[474,80059,80027],{},[474,80061,80062],{"class":476,"line":944},[474,80063,50077],{},[474,80065,80066],{"class":476,"line":950},[474,80067,50062],{},[474,80069,80070],{"class":476,"line":956},[474,80071,80072],{}," \u003CgroupId>org.slf4j\u003C/groupId>\n",[474,80074,80075],{"class":476,"line":962},[474,80076,80077],{}," \u003CartifactId>slf4j-simple\u003C/artifactId>\n",[474,80079,80080],{"class":476,"line":4876},[474,80081,80082],{}," \u003Cversion>1.6.1\u003C/version>\n",[474,80084,80085],{"class":476,"line":4888},[474,80086,80027],{},[474,80088,80089],{"class":476,"line":4900},[474,80090,50077],{},[474,80092,80093],{"class":476,"line":4913},[474,80094,50062],{},[474,80096,80097],{"class":476,"line":4921},[474,80098,80099],{}," \u003CgroupId>javax.servlet\u003C/groupId>\n",[474,80101,80102],{"class":476,"line":4932},[474,80103,80104],{}," \u003CartifactId>servlet-api\u003C/artifactId>\n",[474,80106,80107],{"class":476,"line":4938},[474,80108,80109],{}," \u003Cversion>2.5\u003C/version>\n",[474,80111,80112],{"class":476,"line":4946},[474,80113,80027],{},[474,80115,80116],{"class":476,"line":4952},[474,80117,50077],{},[439,80119,80120],{},"We are including a recent JUnit version to enjoy some of the annotation goodness. solr-core contains all of the server\ncomponents as well as the test case, slf4j is used for logging. Of course you have to check if any of the artifacts\nconflict with runtime or compile time dependencies.",[439,80122,80123],{},"To run a simple test case against the example index config shipped with solr 1.4.1 copy or link the folder\napache-solr-1.4.1/example/solr/ to your projects basedir.",[439,80125,80126],{},"An example test case that checks if a value is found for a valid search:",[464,80128,80130],{"className":16895,"code":80129,"language":16897,"meta":469,"style":469},"\nimport java.io.IOException;\nimport org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;\nimport org.apache.solr.util.AbstractSolrTestCase;\nimport org.apache.solr.client.solrj.SolrQuery;\nimport org.apache.solr.client.solrj.SolrServer;\nimport org.apache.solr.client.solrj.SolrServerException;\nimport org.apache.solr.client.solrj.response.QueryResponse;\nimport org.apache.solr.common.SolrInputDocument;\nimport org.apache.solr.common.params.SolrParams;\nimport org.junit.Before;\nimport org.junit.Test;\nimport static org.junit.Assert.assertEquals;\npublic class SolrSearchConfigTest extends AbstractSolrTestCase {\n private SolrServer server;\n @Override\n public String getSchemaFile() {\n return \"solr/conf/schema.xml\";\n }\n @Override\n public String getSolrConfigFile() {\n return \"solr/conf/solrconfig.xml\";\n }\n @Before\n @Override\n public void setUp() throws Exception {\n super.setUp();\n server = new EmbeddedSolrServer(h.getCoreContainer(), h.getCore().getName());\n }\n @Test\n public void testThatNoResultsAreReturned() throws SolrServerException {\n SolrParams params = new SolrQuery(\"text that is not found\");\n QueryResponse response = server.query(params);\n assertEquals(0L, response.getResults().getNumFound());\n }\n @Test\n public void testThatDocumentIsFound() throws SolrServerException, IOException {\n SolrInputDocument document = new SolrInputDocument();\n document.addField(\"id\", \"1\");\n document.addField(\"name\", \"my name\");\n server.add(document);\n server.commit();\n SolrParams params = new SolrQuery(\"name\");\n QueryResponse response = server.query(params);\n assertEquals(1L, response.getResults().getNumFound());\n assertEquals(\"1\", response.getResults().get(0).get(\"id\"));\n }\n}\n\n",[471,80131,80132,80136,80141,80146,80151,80156,80161,80166,80171,80176,80181,80186,80190,80195,80200,80205,80209,80214,80219,80223,80227,80232,80237,80241,80246,80250,80255,80260,80265,80269,80273,80278,80283,80288,80293,80297,80301,80306,80311,80316,80321,80326,80331,80336,80340,80345,80350,80354],{"__ignoreMap":469},[474,80133,80134],{"class":476,"line":477},[474,80135,917],{"emptyLinePlaceholder":916},[474,80137,80138],{"class":476,"line":507},[474,80139,80140],{},"import java.io.IOException;\n",[474,80142,80143],{"class":476,"line":547},[474,80144,80145],{},"import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;\n",[474,80147,80148],{"class":476,"line":584},[474,80149,80150],{},"import org.apache.solr.util.AbstractSolrTestCase;\n",[474,80152,80153],{"class":476,"line":607},[474,80154,80155],{},"import org.apache.solr.client.solrj.SolrQuery;\n",[474,80157,80158],{"class":476,"line":642},[474,80159,80160],{},"import org.apache.solr.client.solrj.SolrServer;\n",[474,80162,80163],{"class":476,"line":663},[474,80164,80165],{},"import org.apache.solr.client.solrj.SolrServerException;\n",[474,80167,80168],{"class":476,"line":694},[474,80169,80170],{},"import org.apache.solr.client.solrj.response.QueryResponse;\n",[474,80172,80173],{"class":476,"line":700},[474,80174,80175],{},"import org.apache.solr.common.SolrInputDocument;\n",[474,80177,80178],{"class":476,"line":913},[474,80179,80180],{},"import org.apache.solr.common.params.SolrParams;\n",[474,80182,80183],{"class":476,"line":920},[474,80184,80185],{},"import org.junit.Before;\n",[474,80187,80188],{"class":476,"line":926},[474,80189,73790],{},[474,80191,80192],{"class":476,"line":932},[474,80193,80194],{},"import static org.junit.Assert.assertEquals;\n",[474,80196,80197],{"class":476,"line":938},[474,80198,80199],{},"public class SolrSearchConfigTest extends AbstractSolrTestCase {\n",[474,80201,80202],{"class":476,"line":944},[474,80203,80204],{}," private SolrServer server;\n",[474,80206,80207],{"class":476,"line":950},[474,80208,21043],{},[474,80210,80211],{"class":476,"line":956},[474,80212,80213],{}," public String getSchemaFile() {\n",[474,80215,80216],{"class":476,"line":962},[474,80217,80218],{}," return \"solr/conf/schema.xml\";\n",[474,80220,80221],{"class":476,"line":4876},[474,80222,1276],{},[474,80224,80225],{"class":476,"line":4888},[474,80226,21043],{},[474,80228,80229],{"class":476,"line":4900},[474,80230,80231],{}," public String getSolrConfigFile() {\n",[474,80233,80234],{"class":476,"line":4913},[474,80235,80236],{}," return \"solr/conf/solrconfig.xml\";\n",[474,80238,80239],{"class":476,"line":4921},[474,80240,1276],{},[474,80242,80243],{"class":476,"line":4932},[474,80244,80245],{}," @Before\n",[474,80247,80248],{"class":476,"line":4938},[474,80249,21043],{},[474,80251,80252],{"class":476,"line":4946},[474,80253,80254],{}," public void setUp() throws Exception {\n",[474,80256,80257],{"class":476,"line":4952},[474,80258,80259],{}," super.setUp();\n",[474,80261,80262],{"class":476,"line":4957},[474,80263,80264],{}," server = new EmbeddedSolrServer(h.getCoreContainer(), h.getCore().getName());\n",[474,80266,80267],{"class":476,"line":4969},[474,80268,1276],{},[474,80270,80271],{"class":476,"line":4990},[474,80272,1344],{},[474,80274,80275],{"class":476,"line":5001},[474,80276,80277],{}," public void testThatNoResultsAreReturned() throws SolrServerException {\n",[474,80279,80280],{"class":476,"line":5013},[474,80281,80282],{}," SolrParams params = new SolrQuery(\"text that is not found\");\n",[474,80284,80285],{"class":476,"line":5024},[474,80286,80287],{}," QueryResponse response = server.query(params);\n",[474,80289,80290],{"class":476,"line":5035},[474,80291,80292],{}," assertEquals(0L, response.getResults().getNumFound());\n",[474,80294,80295],{"class":476,"line":5047},[474,80296,1276],{},[474,80298,80299],{"class":476,"line":5055},[474,80300,1344],{},[474,80302,80303],{"class":476,"line":5062},[474,80304,80305],{}," public void testThatDocumentIsFound() throws SolrServerException, IOException {\n",[474,80307,80308],{"class":476,"line":5067},[474,80309,80310],{}," SolrInputDocument document = new SolrInputDocument();\n",[474,80312,80313],{"class":476,"line":5072},[474,80314,80315],{}," document.addField(\"id\", \"1\");\n",[474,80317,80318],{"class":476,"line":5084},[474,80319,80320],{}," document.addField(\"name\", \"my name\");\n",[474,80322,80323],{"class":476,"line":5100},[474,80324,80325],{}," server.add(document);\n",[474,80327,80328],{"class":476,"line":5111},[474,80329,80330],{}," server.commit();\n",[474,80332,80333],{"class":476,"line":5122},[474,80334,80335],{}," SolrParams params = new SolrQuery(\"name\");\n",[474,80337,80338],{"class":476,"line":5134},[474,80339,80287],{},[474,80341,80342],{"class":476,"line":5145},[474,80343,80344],{}," assertEquals(1L, response.getResults().getNumFound());\n",[474,80346,80347],{"class":476,"line":5156},[474,80348,80349],{}," assertEquals(\"1\", response.getResults().get(0).get(\"id\"));\n",[474,80351,80352],{"class":476,"line":5163},[474,80353,1276],{},[474,80355,80356],{"class":476,"line":5170},[474,80357,703],{},[1024,80359,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":80361},[],[1030,1412],"2011-01-01T19:31:22","Solr is a search server that bundles a lot of\\nuseful Lucene modules and provides an HTTP interface for querying and updating the data.\\nThe index and most of the query mechanisms are configured using XML documents, client applications normally don’t need\\nto be changed when adjusting the server configuration. As the server configuration heavily influences the quality of\\nyour users search experience it’s a good idea to implement some integration tests that validate your functionality.","https://synyx.de/blog/integration-tests-for-your-solr-config/",{},"/blog/integration-tests-for-your-solr-config",{"title":79961,"description":80369},"Solr is a search server that bundles a lot of\nuseful Lucene modules and provides an HTTP interface for querying and updating the data.\nThe index and most of the query mechanisms are configured using XML documents, client applications normally don’t need\nto be changed when adjusting the server configuration. As the server configuration heavily influences the quality of\nyour users search experience it’s a good idea to implement some integration tests that validate your functionality.","blog/integration-tests-for-your-solr-config",[22988,64519,18497],"Solr is a search server that bundles a lot of useful Lucene modules and provides an HTTP interface for querying and updating the data. The index and most of the…","tIqboctYIr9FB0W_MW0lZsbkuMIgue2OGo8FnULd54M",{"id":80375,"title":80376,"author":80377,"body":80378,"category":80471,"date":80472,"description":80473,"extension":1034,"link":80474,"meta":80475,"navigation":916,"path":80476,"seo":80477,"slug":80479,"stem":80480,"tags":80481,"teaser":80483,"__hash__":80484},"blog/blog/devoxx-2010-revisited.md","Devoxx 2010 – Revisited using Parleys",[166],{"type":432,"value":80379,"toc":80469},[80380,80383,80404,80407,80410,80413,80422,80431,80439,80446,80456],[435,80381,80376],{"id":80382},"devoxx-2010-revisited-using-parleys",[439,80384,80385,80386,80390,80391,1489,80395,1489,80399,80403],{},"This year in November three of my colleagues and I were visiting the best Java conference\never – ",[1002,80387,11462],{"href":80388,"rel":80389},"http://devoxx.com/display/Devoxx2K10/Home",[1006]," in Antwerp (blogs\nhere: ",[1002,80392,5008],{"href":80393,"rel":80394},"http://blog.synyx.de/2010/11/devoxx-2010-part-1/",[1006],[1002,80396,4871],{"href":80397,"rel":80398},"http://blog.synyx.de/2010/11/devoxx-university/",[1006],[1002,80400,15946],{"href":80401,"rel":80402},"http://blog.synyx.de/2010/11/devoxx-2010-part-2/",[1006],").\nNow, since more than a month of time went by its time for recall and see, what about Devoxx is still present in my mind.",[439,80405,80406],{},"The fist thing is, that the conference was very very well organized and the location was almost perfect.",[439,80408,80409],{},"The second thing, that is still in my mind is the awful “Coffee” they served. This stuff was not worth to be called\ncoffee and for a Java conference, this seems to be just wrong, guys ;). Please make the tickets to be more expensive\nnext year and get some good coffee-machines for the extra money you earn. The nerds will thank you.",[439,80411,80412],{},"But the most important thing I remember is the quality of talks and speakers. They helped me a lot and improved my work\nalready. I think Synyx invested the money for Devoxx just right, because all of us enjoyed the conference and also have\na whole new set of ideas and tools ready for production now.",[439,80414,80415,80416,80421],{},"Unfortunately, as on every conference I missed some of the great talks I was really interested in because I was busy\nseeing other talks in parallel. As I said, this happens on every conference. But Devoxx got kind of around that\nusing ",[1002,80417,80420],{"href":80418,"rel":80419},"http://parleys.com",[1006],"Parleys.com",", where you can watch all talks of Devoxx for around 100$ a year. Over the last\ndays, I’ve seen just about all of the ones I could not see because they were conflicting with other talks and it is just\ngreat. It seems its the perfect thing to do in nights around Christmas if you neither have Internet around, nor a TV\naccessible.",[439,80423,80424,80425,80430],{},"Parleys is a fancy webbased application (",[1002,80426,80429],{"href":80427,"rel":80428},"http://get.adobe.com/air/",[1006],"Adobe Air",") where you can browse through all of the\ntalks and watch them as you were used to at the Devoxx’ big cinema-Screens: They offer the video of the speaker next to\nthe slides as well as the demo-videos in a synchronous manner. You get also some nice options like enlarging one of the\nvideos, rate them, and so on.",[439,80432,80433,80434,80438],{},"But the best feature is definitely the “download” feature, since this allows you to watch the talks offline at your\ngrandmothers using the Desktop-Client. By the way, on my Linux machines I could not install this bastard using Firefox\nand ",[1002,80435,80436],{"href":80436,"rel":80437},"http://parleys.com/desktop/",[1006]," but i had to wget the .air file as to be found in the pages sourcecode and then run\nthe installer manually using a pre-installed Adobe Air.",[439,80440,80441,80442,80445],{},"So over the last days I’ve seen some great talks from Devoxx 2010 which I could not attend at the conference and I\ndefinitely shouldn’t have missed. Also, if you were not at Devoxx this is a perfect thing to get some of the feeling and\nknowledge at a nice price. Parleys also helped me a lot the last weeks at work where we\nintroduced ",[1002,80443,66556],{"href":66553,"rel":80444},[1006]," as the frontend technology of a new application. I could hand my\ncolleagues the 3 hour University-Talk about Wicket as an introduction which turned out to be great since they could\nstart using it right away afterwards.",[439,80447,80448],{},[1002,80449,80452],{"href":80450,"rel":80451},"https://media.synyx.de/uploads//2010/12/devoxx_revisited.jpg",[1006],[2205,80453],{"alt":469,"src":80454,"title":80455},"https://media.synyx.de/uploads//2010/12/devoxx_revisited-300x215.jpg","Talk in Parleys",[439,80457,80458,80459,520,80462,18175,80465,80468],{},"The downside of Parleys in my opinion is the way too fancy Adobe Air style of it. As this might be great for designers\nand “regulars”, I’d rather prefer more usability and performance while browsing and searching the videos over some nice\nanimations and effects. The developers of Parleys should consider to include some fast and non-fancy search-able and\nfilterable list of all talks with options like ",[990,80460,80461],{},"queue to download",[990,80463,80464],{},"mark as to be watched",[990,80466,80467],{},"watch",". I really hope\nto see these features announced when we’re coming back to Devoxx 2011.",{"title":469,"searchDepth":507,"depth":507,"links":80470},[],[1030],"2010-12-28T17:46:47","This year in November three of my colleagues and I were visiting the best Java conference\\never – Devoxx in Antwerp (blogs\\nhere: 1,2,3).\\nNow, since more than a month of time went by its time for recall and see, what about Devoxx is still present in my mind.","https://synyx.de/blog/devoxx-2010-revisited/",{},"/blog/devoxx-2010-revisited",{"title":80376,"description":80478},"This year in November three of my colleagues and I were visiting the best Java conference\never – Devoxx in Antwerp (blogs\nhere: 1,2,3).\nNow, since more than a month of time went by its time for recall and see, what about Devoxx is still present in my mind.","devoxx-2010-revisited","blog/devoxx-2010-revisited",[37617,711,80482,66555],"parleys","This year in November three of my colleagues and I were visiting the best Java conference ever – Devoxx in Antwerp (blogs here: 1,2,3). Now, since more than a month…","Z1fZisn0gpr-Zz9EzBvMQujY-4TzXOPqcP77pPmog84",{"id":80486,"title":80487,"author":80488,"body":80489,"category":80534,"date":80535,"description":80496,"extension":1034,"link":80536,"meta":80537,"navigation":916,"path":80538,"seo":80539,"slug":80493,"stem":80540,"tags":80541,"teaser":80542,"__hash__":80543},"blog/blog/das-jahr-2010-geht-zu-ende.md","Das Jahr 2010 geht zu Ende",[314],{"type":432,"value":80490,"toc":80532},[80491,80494,80497,80500,80510,80513,80523,80526,80529],[435,80492,80487],{"id":80493},"das-jahr-2010-geht-zu-ende",[439,80495,80496],{},"… Und es war ein erfolgreiches Jahr!",[439,80498,80499],{},"Um gemeinsam darauf anzustoßen, noch bevor uns die Weihnachts-Urlaubs-Welle erwischt, veranstalteten wir letzten\nFreitag ein zum Jahresende traditionelles Raclette mit, zur kalten Jahreszeit passend, heißem Glühwein.",[439,80501,80502],{},[1002,80503,80506],{"href":80504,"rel":80505},"https://media.synyx.de/uploads//2010/12/gabentisch.jpg",[1006],[2205,80507],{"alt":80508,"src":80509,"title":80508},"reich gedeckter Gabentisch","https://media.synyx.de/uploads//2010/12/gabentisch-150x150.jpg",[439,80511,80512],{},"Durch fleißige Schnippel-Helfer, einem reich gedeckten Gabentisch, genügend Raclette-Geräten und einem gefüllten\nGlühweinkocher waren die Vorraussetzungen für ein gemütliches Beisammensein schnell geschaffen.",[439,80514,80515],{},[1002,80516,80519],{"href":80517,"rel":80518},"https://media.synyx.de/uploads//2010/12/P10801421.jpg",[1006],[2205,80520],{"alt":469,"src":80521,"title":80522},"https://media.synyx.de/uploads//2010/12/P10801421-150x150.jpg","Vorfreude auf Leckeres vom Grill",[439,80524,80525],{},"Andere zuträgliche Komponenten wie gute Laune und Vorfreude auf Leckeres vom Grill ließen nicht lange auf sich warten.\nZwar war knuspriger Bacon (mit dem sich angeblich wirklich alles verbessern lässt) der absolute Hit, aber auch unsere\nVegetarier kamen mit genügend grünen Alternativen voll auf ihre Kosten. Im Laufe des Abends stellte sich auch heraus,\ndass karamelisierte Ananas mit etwas Zimt eine köstliche und obendrein vegetarische Leckerei abgibt.",[439,80527,80528],{},"Das Essen war natürlich nur ein Teil der Festlichkeit, unter anderem fand auch noch eine Krönung zum “Git-König” statt.\nFür die Ideengebung mit git-svn sei besser zu mergen wurde unser Neuzugang Florian Krupicka festlich mit\nPapier-Krönchen gekürt.",[439,80530,80531],{},"So haben wir das Jahr 2010 gebührend verabschiedet und freuen uns schon auf ein gemeinsames, erfolgreiches Jahr 2011.",{"title":469,"searchDepth":507,"depth":507,"links":80533},[],[1031],"2010-12-22T17:20:01","https://synyx.de/blog/das-jahr-2010-geht-zu-ende/",{},"/blog/das-jahr-2010-geht-zu-ende",{"title":80487,"description":80496},"blog/das-jahr-2010-geht-zu-ende",[],"… Und es war ein erfolgreiches Jahr! Um gemeinsam darauf anzustoßen, noch bevor uns die Weihnachts-Urlaubs-Welle erwischt, veranstalteten wir letzten Freitag ein zum Jahresende traditionelles Raclette mit, zur kalten Jahreszeit…","2OJ0MF2f8ElQUcYEgSq29FaMu5rloPvuIYuW4Jy-CyY",{"id":80545,"title":80546,"author":80547,"body":80548,"category":80590,"date":80591,"description":80592,"extension":1034,"link":80593,"meta":80594,"navigation":916,"path":80595,"seo":80596,"slug":80552,"stem":80598,"tags":80599,"teaser":80601,"__hash__":80602},"blog/blog/netbeans-and-opencms.md","Netbeans and OpenCms",[148],{"type":432,"value":80549,"toc":80588},[80550,80553,80560,80573,80576,80585],[435,80551,80546],{"id":80552},"netbeans-and-opencms",[439,80554,80555,80559],{},[1002,80556,71236],{"href":80557,"rel":80558},"http://opencms.org",[1006]," stores all its content like JSP-templates, images and CSS files in a virtual file system\nwhich makes it impossible to use normal development models. As we are using it for several years now we’ve always been\ntrying to improve our development processes.",[439,80561,80562,80563,80568,80569,1402],{},"One of the results is a custom ",[1002,80564,80567],{"href":80565,"rel":80566},"http://netbeans.org",[1006],"Netbeans"," plugin for working with OpenCms. We’ve been using it\nsuccessfully in several projects but didn’t find the time to release it to the public until today. The plugin can now be\ndownloaded on our ",[1002,80570,80572],{"href":77323,"rel":80571},[1006],"open source project site",[439,80574,80575],{},"It consists of a combination of a Netbeans plugin and an OpenCms module and is targeted at development environments.\nFiles can be uploaded using the Netbeans context menu. This drastically improves the development time as changes can be\napplied and checked immediately.",[439,80577,80578],{},[1002,80579,80582],{"href":80580,"rel":80581},"https://media.synyx.de/uploads//2010/11/netbeans-dialogsmall.png",[1006],[2205,80583],{"alt":469,"src":80580,"title":80584},"Netbeans OpenCms Plugin in Action",[439,80586,80587],{},"Of course we are interested if this plugin is useful for anybody so leave a comment if you like it. If you are\ninterested in any enhancements or you found a bug just issue a ticket in redmine.",{"title":469,"searchDepth":507,"depth":507,"links":80589},[],[1030,1412],"2010-11-26T18:31:57","OpenCms stores all its content like JSP-templates, images and CSS files in a virtual file system\\nwhich makes it impossible to use normal development models. As we are using it for several years now we’ve always been\\ntrying to improve our development processes.","https://synyx.de/blog/netbeans-and-opencms/",{},"/blog/netbeans-and-opencms",{"title":80546,"description":80597},"OpenCms stores all its content like JSP-templates, images and CSS files in a virtual file system\nwhich makes it impossible to use normal development models. As we are using it for several years now we’ve always been\ntrying to improve our development processes.","blog/netbeans-and-opencms",[80600,64517],"netbeans","OpenCms stores all its content like JSP-templates, images and CSS files in a virtual file system which makes it impossible to use normal development models. As we are using it…","DKQPV0NiiTLbLUo2cgEV9z9ImPS8XwZCoBeEsPwOfgM",{"id":80604,"title":80605,"author":80606,"body":80607,"category":80651,"date":80652,"description":80653,"extension":1034,"link":80654,"meta":80655,"navigation":916,"path":80656,"seo":80657,"slug":80611,"stem":80659,"tags":80660,"teaser":80662,"__hash__":80663},"blog/blog/logging-to-multiple-files-using-log4j.md","Logging to multiple files using log4j",[148],{"type":432,"value":80608,"toc":80649},[80609,80612,80621,80624,80627,80630,80633,80636,80639,80646],[435,80610,80605],{"id":80611},"logging-to-multiple-files-using-log4j",[439,80613,80614,80615,80620],{},"Sometimes even the simple things take quite some time. For a special application log I’ve been searching for a way to\nwrite log calls for one package to a separate file using Log4j. I’ve been searching the web, skimming through the log4j\ndocumentation but didn’t really find anything. ",[1002,80616,80619],{"href":80617,"rel":80618},"http://www.junlu.com/msg/93394.html",[1006],"This message",", though only a\nquestion, contained the missing hint.",[439,80622,80623],{},"Suppose you have a log configuration that already has root loggers configured. To make only one package log to another\nfile, but not to the root logger appenders, you add something like this:",[439,80625,80626],{},"`log4j.logger.my.Logger=INFO,FILE",[439,80628,80629],{},"log4j.additivity.my.Logger=false",[439,80631,80632],{},"log4j.appender.FILE=org.apache.log4j.RollingFileAppender",[439,80634,80635],{},"log4j.appender.FILE.File=include-performance.log",[439,80637,80638],{},"log4j.appender.FILE.layout=org.apache.log4j.PatternLayout",[439,80640,80641,80642,80645],{},"log4j.appender.FILE.layout.ConversionPattern=%d{DATE} %5p ",[474,80643,80644],{},"%30.30C:%4L"," %m%n`",[439,80647,80648],{},"What does it do? In the first line you create a logger that logs anything above info inclusive. In the same line,\nseparated by a comma, you add the name of your appender. By setting additivity to false you prevent the logger to\ninherit any loggers from ancestors (including any root loggers).",{"title":469,"searchDepth":507,"depth":507,"links":80650},[],[1030,1412],"2010-11-24T18:46:17","Sometimes even the simple things take quite some time. For a special application log I’ve been searching for a way to\\nwrite log calls for one package to a separate file using Log4j. I’ve been searching the web, skimming through the log4j\\ndocumentation but didn’t really find anything. This message, though only a\\nquestion, contained the missing hint.","https://synyx.de/blog/logging-to-multiple-files-using-log4j/",{},"/blog/logging-to-multiple-files-using-log4j",{"title":80605,"description":80658},"Sometimes even the simple things take quite some time. For a special application log I’ve been searching for a way to\nwrite log calls for one package to a separate file using Log4j. I’ve been searching the web, skimming through the log4j\ndocumentation but didn’t really find anything. This message, though only a\nquestion, contained the missing hint.","blog/logging-to-multiple-files-using-log4j",[76414,80661],"logging","Sometimes even the simple things take quite some time. For a special application log I’ve been searching for a way to write log calls for one package to a separate…","sdX013OPkMBq0Tfuuz2mTT8DTbfSaM4rGRwxRY32Qzw",{"id":80665,"title":80666,"author":80667,"body":80668,"category":80717,"date":80718,"description":80719,"extension":1034,"link":11465,"meta":80720,"navigation":916,"path":80721,"seo":80722,"slug":80672,"stem":80723,"tags":80724,"teaser":80725,"__hash__":80726},"blog/blog/devoxx-2010-part-2.md","Devoxx 2010 – Part 2",[166],{"type":432,"value":80669,"toc":80715},[80670,80673,80676,80689,80692,80695,80698,80709,80712],[435,80671,80666],{"id":80672},"devoxx-2010-part-2",[439,80674,80675],{},"So this is the second part of my experiences with Devoxx 2010. I’ll still stick to not writing about the talks in\ndetail but telling my thoughts as a “first time visitor”.",[439,80677,80678,80679,80683,80684,80688],{},"So day three to five at Devoxx were “Conference” days which have – in opposite to the\nUniversity-Talks ",[1002,80680,80682],{"href":80397,"rel":80681},[1006],"Florian"," and I mentioned earlier – much shorter\ntalks. This had the trade-off ",[1002,80685,80687],{"href":80393,"rel":80686},[1006],"I mentioned before",": You have many\nswitches of topics between the talks which actually made it sometimes kind of hard to even remember at the evening what\nI saw in the morning. But what I have to admit is, that even if there were more talks a day the quality of them was\nexcellent. There were so many brilliant Speakers (Josh Bloch, Neil Ford and Matt Reible just to mention very few of\nthem) and almost all talks were as well interesting and entertaining.",[439,80690,80691],{},"So we had a lot of fun listening and I personally can say I am full (actually more than full) of new ideas and\ninspiration about what to do next and how to adopt or use things that were talked about at Synyx.",[439,80693,80694],{},"Another thing that I recognized is, that many many of the ideas, tools, frameworks etc that were showed at the different\ntalks are already in production at Synyx, which confirms us and shows that we’re on the right road. But of course there\nis still a lot of work to do, a lot of code to write and a lot of and ways to improve ourselves. Well, this was one of\nthe reasons why we’ve been visiting Devoxx anyway.",[439,80696,80697],{},"A downside of the Conference days was, that there were about 5 times as much visitors than in the first two days, which\nmade the rooms and hall really crowded and you had to wait often (getting in/out of room or going to the restrooms).\nDuring some really popular talks people (including us ;-)) had to sit at stairs or at the floor to be able listening to\nthem.",[439,80699,80700],{},[1002,80701,80704],{"href":80702,"rel":80703},"https://media.synyx.de/uploads//2010/11/devoxx-bloch-crowded.jpg",[1006],[2205,80705],{"alt":80706,"src":80707,"title":80708},"Crowded Talk of Josh Bloch","https://media.synyx.de/uploads//2010/11/devoxx-bloch-crowded-300x46.jpg","A lot of people",[439,80710,80711],{},"I think what could be improved for the next time are the “flows” of people during the breaks. Maybe only allow people\nleaving the rooms at the lower exits and enter them at the upper ones or manage them another smart way. And maybe they\ncould have some overflow-rooms that can be dynamically used if a room gets too crowded.",[439,80713,80714],{},"To sum things up it was a great time we had at Devoxx 2010 and I’d love to come back again since this conference is\nreally the best – at least the best I’ve been so far. Keep up the good work, we really enjoyed it!",{"title":469,"searchDepth":507,"depth":507,"links":80716},[],[1030],"2010-11-20T14:11:02","So this is the second part of my experiences with Devoxx 2010. I’ll still stick to not writing about the talks in\\ndetail but telling my thoughts as a “first time visitor”.",{},"/blog/devoxx-2010-part-2",{"title":80666,"description":80675},"blog/devoxx-2010-part-2",[8276,37617],"So this is the second part of my experiences with Devoxx 2010. I’ll still stick to not writing about the talks in detail but telling my thoughts as a “first…","0ra_yuooEsYpbGA2b35qf3EAF3Wch522S6YjDI-idv0",{"id":80728,"title":80729,"author":80730,"body":80731,"category":80744,"date":80745,"description":469,"extension":1034,"link":80746,"meta":80747,"navigation":916,"path":80748,"seo":80749,"slug":80735,"stem":80750,"tags":80751,"teaser":80752,"__hash__":80753},"blog/blog/game-center-presentation-at-cocoa-heads-karlsruhe.md","Game Center Presentation at Cocoa Heads Karlsruhe",[223],{"type":432,"value":80732,"toc":80742},[80733,80736],[435,80734,80729],{"id":80735},"game-center-presentation-at-cocoa-heads-karlsruhe",[439,80737,80738],{},[2205,80739],{"alt":80740,"src":80741},"hero-gamecenter.png","https://media.synyx.de/uploads//2010/11/hero-gamecenter.png",{"title":469,"searchDepth":507,"depth":507,"links":80743},[],[11122],"2010-11-19T09:56:35","https://synyx.de/blog/game-center-presentation-at-cocoa-heads-karlsruhe/",{},"/blog/game-center-presentation-at-cocoa-heads-karlsruhe",{"title":80729,"description":469},"blog/game-center-presentation-at-cocoa-heads-karlsruhe",[63945,78972,78398,78399,63949],"On Wednesday, I’ll be giving a presentation on Apple’s Game Center at the local CocoaHeads Group, here in Karlsruhe. Game Center is Apple’s social gaming network, that lets you invite…","GiUsd6F3L7seTHDgO_RaxV4qb-rQR5cfhuleZQHMNhQ",{"id":80755,"title":80756,"author":80757,"body":80758,"category":80831,"date":80832,"description":80833,"extension":1034,"link":80834,"meta":80835,"navigation":916,"path":80836,"seo":80837,"slug":80762,"stem":80839,"tags":80840,"teaser":80843,"__hash__":80844},"blog/blog/devoxx-university.md","Devoxx University",[148],{"type":432,"value":80759,"toc":80829},[80760,80763,80771,80784,80808,80823,80826],[435,80761,80756],{"id":80762},"devoxx-university",[439,80764,80765,80766,80770],{},"As the university days on Devoxx are nearly finished I’d like to summarize some of the more interesting talks that\nhappened during the first two days. Marc already\nwrote ",[1002,80767,80769],{"href":80393,"rel":80768},[1006],"some words on the conference itself"," so I will focus on the\ntalks.",[439,80772,80773,80774,80777,80778,80783],{},"Monday started off with ",[1002,80775,72283],{"href":72281,"rel":80776},[1006]," talking about Productive Programmers. The topics he\ncovered are quite basic but nevertheless important to know for every developer. The first part showed how to optimize\nyour daily work, circling around the terms automation, acceleration, canonicality and focus. Some useful tools and\ntechniques were presented e.g. an ",[1002,80779,80782],{"href":80780,"rel":80781},"http://www.mousefeed.com/",[1006],"Eclipse plugin for learning keyboard shortcuts"," that\nsounds really nice: Every time you are using the mouse to navigate or select an action a message is displayed that tells\nyou how to do the same thing just with the keyboard. I’d be glad to have a plugin like this for Netbeans! During the\nsecond part a lot of best practices for coding were covered, always good to keep those in mind. All in all a very\ninspiring talk and a good start of the week.",[439,80785,80786,80787,80790,80791,80796,80797,80802,80803,80807],{},"For the afternoon session I chose ",[1002,80788,71000],{"href":70998,"rel":80789},[1006],", the document oriented database. Being already\nfamiliar with the basic concepts from ",[1002,80792,80795],{"href":80793,"rel":80794},"http://blog.synyx.de/2010/08/froscon-2010/",[1006],"FrOSCon"," and several podcasts I’ve\nbeen especially interested in seeing it in action using Java. Two basic approaches were introduced: The raw Java driver\nthat is developed by the MongoDB team lets you work on quite a low level handling Maps of Strings and Objects. A more\nsophisticated approach is to use the community developed ",[1002,80798,80801],{"href":80799,"rel":80800},"http://code.google.com/p/morphia/",[1006],"Morphia","-driver which\nallows to annotate POJOs just like when using JPA. I wouldn’t have expected to see such a nice abstraction for MongoDB\nyet, definitely something to keep an eye on. I am curious if ",[1002,80804,47662],{"href":80805,"rel":80806},"http://www.springsource.org/spring-data",[1006]," can\noffer something similar in the near future.",[439,80809,80810,80811,80816,80817,80822],{},"On tuesday I had to get up extra early: the Scala lab offered by Dick Wall of the ",[1002,80812,80815],{"href":80813,"rel":80814},"http://www.javaposse.com/",[1006],"Javaposse","\nand Bill Venners was scheduled for the BOF rooms which only fit around 50 people. Definitely the highlight of the\nconference so far as with only few attendees there was a lot of time for excercises and support by these two experts. I\nhope I can start using Scala at work soon, ",[1002,80818,80821],{"href":80819,"rel":80820},"http://www.scalatest.org/",[1006],"ScalaTest"," is supposed to be a good starting\npoint for learning the language without having to integrate it in a production system.",[439,80824,80825],{},"Back to Java in the afternoon: Emmanuel Bernard demonstrated new features in Hibernate and JPA 2. Besides the typesafe\nCriteria API it was really nice to see the fluent APIs used for Hibernate Search. Seems like Hibernate Search can free\nyou from a lot of programming work when integrating Lucene and Hibernate.",[439,80827,80828],{},"Looking forward to the rest of the conference which will surely bring more interesting talks during the rest of the\nweek!",{"title":469,"searchDepth":507,"depth":507,"links":80830},[],[1030],"2010-11-17T09:13:24","As the university days on Devoxx are nearly finished I’d like to summarize some of the more interesting talks that\\nhappened during the first two days. Marc already\\nwrote some words on the conference itself so I will focus on the\\ntalks.","https://synyx.de/blog/devoxx-university/",{},"/blog/devoxx-university",{"title":80756,"description":80838},"As the university days on Devoxx are nearly finished I’d like to summarize some of the more interesting talks that\nhappened during the first two days. Marc already\nwrote some words on the conference itself so I will focus on the\ntalks.","blog/devoxx-university",[8276,37617,711,80841,80842],"mongodb","scala","As the university days on Devoxx are nearly finished I’d like to summarize some of the more interesting talks that happened during the first two days. Marc already wrote some…","Fa30-dvVDcsLnSS5JZec5ayVcHOjfehzKa_MalPRWgY",{"id":80846,"title":80847,"author":80848,"body":80849,"category":80896,"date":80897,"description":80898,"extension":1034,"link":11460,"meta":80899,"navigation":916,"path":80900,"seo":80901,"slug":80853,"stem":80903,"tags":80904,"teaser":80905,"__hash__":80906},"blog/blog/devoxx-2010-part-1.md","Devoxx 2010 – Part 1",[166],{"type":432,"value":80850,"toc":80894},[80851,80854,80862,80865,80868,80891],[435,80852,80847],{"id":80853},"devoxx-2010-part-1",[439,80855,80856,80857,80861],{},"Employees of Synyx are going to ",[1002,80858,11462],{"href":80859,"rel":80860},"http://devoxx.com",[1006]," in Antwerp since three years. I’ve never been here before\nbut my colleagues have always reportet that they loved the conference. So this is the first time I could also make it\nand I must say, just right after less than the first half: It rocks!",[439,80863,80864],{},"I am not gonna bore you with the details of the talks I visited because I leave this up to one of the other guys from\nSynyx that are here with me. 😉 Instead I’ll try to explain what I liked most/not/whatever about it from a\n“Devoxx-Newbie” perspective.",[439,80866,80867],{},"The best thing to mention so far are the University-Talks. These extra-long talks take place (unfortunately) only\nwithin the first two days of Devoxx. The special thing about them is that they last 3 hours each. This lets the speakers\ndig much deeper into their topic as they could do during a 1 or 1.5 hour talk. Additionally most of the sessions I’ve\nseen so far have some bigger demos and live-coding included. Another good thing about this is that since you can only\nvisit two a day (one in the morning and one in the afternoon) you won’t have to “switch tasks” so often, meaning you can\nkeep concentrating one one topic for a longer period of time. I like this much better than other conferences where\nyou’ll have like 6 Sessions in a row without anything in common (ok, you can follow the tracks that should have related\ntopics, but who does that…).",[439,80869,80870,80871,520,80876,520,80881,520,80885,80890],{},"By the way, if someone is interested in what University-Talks I’ve been\nvisiting: ",[1002,80872,80875],{"href":80873,"rel":80874},"http://devoxx.com/display/Devoxx2K10/Hadoop+Fundamentals++HDFS%2C+MapReduce%2C+Pig%2C+and+Hive",[1006],"Hadoop Fundamentals",[1002,80877,80880],{"href":80878,"rel":80879},"http://devoxx.com/display/Devoxx2K10/Extreme+Productivity+with+Spring+Roo",[1006],"Spring Roo",[1002,80882,66556],{"href":80883,"rel":80884},"http://devoxx.com/display/Devoxx2K10/Introducing+Wicket",[1006],[1002,80886,80889],{"href":80887,"rel":80888},"http://devoxx.com/display/Devoxx2K10/What%27s+new+in+Hibernate++a+JPA+2+perspective",[1006],"Hibernate / JPA2"," (\nclick on the links for details and full names of the talks).",[439,80892,80893],{},"So I am looking forward to the next 3 Days that are hopefully packed with new stuff to learn. So stay tuned for the\nsecond part and my resumee of Devoxx 2010.",{"title":469,"searchDepth":507,"depth":507,"links":80895},[],[1030],"2010-11-16T20:59:06","Employees of Synyx are going to Devoxx in Antwerp since three years. I’ve never been here before\\nbut my colleagues have always reportet that they loved the conference. So this is the first time I could also make it\\nand I must say, just right after less than the first half: It rocks!",{},"/blog/devoxx-2010-part-1",{"title":80847,"description":80902},"Employees of Synyx are going to Devoxx in Antwerp since three years. I’ve never been here before\nbut my colleagues have always reportet that they loved the conference. So this is the first time I could also make it\nand I must say, just right after less than the first half: It rocks!","blog/devoxx-2010-part-1",[8276,37617],"Employees of Synyx are going to Devoxx in Antwerp since three years. I’ve never been here before but my colleagues have always reportet that they loved the conference. So this…","1-xRQoTgv9V8nWr1fLF13SM0SjtHqa6gog2KJo__WEI",{"id":80908,"title":80909,"author":80910,"body":80911,"category":81021,"date":81022,"description":80918,"extension":1034,"link":81023,"meta":81024,"navigation":916,"path":81025,"seo":81026,"slug":81027,"stem":81028,"tags":81029,"teaser":81030,"__hash__":81031},"blog/blog/negating-matches-in-apache-location.md","Negating matches in Apache locations",[148],{"type":432,"value":80912,"toc":81019},[80913,80916,80919,80933,80968,80977,81011,81014,81017],[435,80914,80909],{"id":80915},"negating-matches-in-apache-locations",[439,80917,80918],{},"It took me some time to figure it out so why not sharing it with the world?",[439,80920,80921,80926,80927,80932],{},[1002,80922,80925],{"href":80923,"rel":80924},"http://httpd.apache.org/",[1006],"Apache"," allows you to add basic auth to parts of your site using\nthe ",[1002,80928,80931],{"href":80929,"rel":80930},"http://httpd.apache.org/docs/2.2/mod/core.html#location",[1006],"Location"," directive. When restricting access to all\nresources you might add a section like this to your VirtualHost:",[464,80934,80936],{"className":16895,"code":80935,"language":16897,"meta":469,"style":469},"\u003CLocation />\n AuthUserFile /path/to/.htpasswd\n AuthName \"geschuetzter Bereich\"\n AuthType Basic\n require valid-user\n\u003C/Location>\n\n",[471,80937,80938,80943,80948,80953,80958,80963],{"__ignoreMap":469},[474,80939,80940],{"class":476,"line":477},[474,80941,80942],{},"\u003CLocation />\n",[474,80944,80945],{"class":476,"line":507},[474,80946,80947],{}," AuthUserFile /path/to/.htpasswd\n",[474,80949,80950],{"class":476,"line":547},[474,80951,80952],{}," AuthName \"geschuetzter Bereich\"\n",[474,80954,80955],{"class":476,"line":584},[474,80956,80957],{}," AuthType Basic\n",[474,80959,80960],{"class":476,"line":607},[474,80961,80962],{}," require valid-user\n",[474,80964,80965],{"class":476,"line":642},[474,80966,80967],{},"\u003C/Location>\n",[439,80969,80970,80971,80976],{},"/ means that any access to your server is restricted. Today I’ve been looking for a way to restrict all resources on the\nserver but one. It’s not that easy using standard regular expression but as Apache\nuses ",[1002,80972,80975],{"href":80973,"rel":80974},"http://www.pcre.org/",[1006],"Perl compatible regular expressions"," you can use lookahead assertions to negate an\nexpressions:",[464,80978,80980],{"className":16895,"code":80979,"language":16897,"meta":469,"style":469},"\u003CLocation ~ \"^/(?!path/that/doesnt/need/auth)\">\n AuthUserFile /path/to/.htpasswd\n AuthName \"geschuetzter Bereich\"\n AuthType Basic\n require valid-user\n\u003C/Location>\n",[471,80981,80982,80987,80992,80997,81002,81007],{"__ignoreMap":469},[474,80983,80984],{"class":476,"line":477},[474,80985,80986],{},"\u003CLocation ~ \"^/(?!path/that/doesnt/need/auth)\">\n",[474,80988,80989],{"class":476,"line":507},[474,80990,80991],{}," AuthUserFile /path/to/.htpasswd\n",[474,80993,80994],{"class":476,"line":547},[474,80995,80996],{}," AuthName \"geschuetzter Bereich\"\n",[474,80998,80999],{"class":476,"line":584},[474,81000,81001],{}," AuthType Basic\n",[474,81003,81004],{"class":476,"line":607},[474,81005,81006],{}," require valid-user\n",[474,81008,81009],{"class":476,"line":642},[474,81010,80967],{},[439,81012,81013],{},"With ~ you are telling Apache that you are using an extended regular expression. ^ is the beginning of the line, ?!\ninitializes a negated lookahead assertion. Any path that is not in the String given above will require authentication.",[439,81015,81016],{},"Big thank you to our administrators who’ve been kind enough to share a lot of their wisdom with me.",[1024,81018,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":81020},[],[9045],"2010-11-10T20:41:53","https://synyx.de/blog/negating-matches-in-apache-location/",{},"/blog/negating-matches-in-apache-location",{"title":80909,"description":80918},"negating-matches-in-apache-location","blog/negating-matches-in-apache-location",[73058],"It took me some time to figure it out so why not sharing it with the world? Apache allows you to add basic auth to parts of your site using…","SY_tZ8i6xf7etcZBs9jW1JoRvctIkAFZQnJ2P494kCs",{"id":81033,"title":81034,"author":81035,"body":81036,"category":81200,"date":81201,"description":81202,"extension":1034,"link":81203,"meta":81204,"navigation":916,"path":81205,"seo":81206,"slug":81040,"stem":81208,"tags":81209,"teaser":81210,"__hash__":81211},"blog/blog/using-cmsshell-on-ubuntu-systems.md","Using CmsShell on Ubuntu systems",[148],{"type":432,"value":81037,"toc":81198},[81038,81041,81050,81053,81059,81066,81080,81083,81088,81091,81096,81102,81176,81179,81185,81188,81193,81196],[435,81039,81034],{"id":81040},"using-cmsshell-on-ubuntu-systems",[439,81042,81043,81044,81047,81048,1402],{},"CmsShell is a neat tool to access the ",[1002,81045,71236],{"href":80557,"rel":81046},[1006]," VFS from the command line. This can be extremely\nuseful when your system can’t be accessed anymore using the web interface or when performing batch updates. The script\ncan be found in the OpenCms webapp at ",[471,81049,79109],{},[439,81051,81052],{},"Unfortunately the script that ships with the OpenCms webapp doesn’t work on Ubuntu systems as it is. These are the steps\nto make it run.",[439,81054,81055,81056],{},"First be sure that it’s executable: ",[471,81057,81058],{},"chmod +x cmsshell.sh",[439,81060,81061,81062,81065],{},"When trying to run it using ",[471,81063,81064],{},"./cmsshell.sh"," you will very likely see an error:",[464,81067,81069],{"className":16895,"code":81068,"language":16897,"meta":469,"style":469},"\nbash: ./cmsshell.sh: /bin/sh^M: bad interpreter: No such file or directory\n\n",[471,81070,81071,81075],{"__ignoreMap":469},[474,81072,81073],{"class":476,"line":477},[474,81074,917],{"emptyLinePlaceholder":916},[474,81076,81077],{"class":476,"line":507},[474,81078,81079],{},"bash: ./cmsshell.sh: /bin/sh^M: bad interpreter: No such file or directory\n",[439,81081,81082],{},"This is caused by dos line breaks that are included in the file. A tool to remove those can be found in the package\ntofrodos (used to be in sysutils):",[439,81084,81085],{},[471,81086,81087],{},"sudo apt-get install tofrodos",[439,81089,81090],{},"Run it on the file by issuing",[439,81092,81093],{},[471,81094,81095],{},"dos2unix cmsshell.sh",[439,81097,81098,81099,81101],{},"Try to run the file again: ",[471,81100,81064],{},", which will result in another error:",[464,81103,81105],{"className":16895,"code":81104,"language":16897,"meta":469,"style":469},"\n/cmsshell.sh: 8: pushd: not found\n./cmsshell.sh: 9: dirs: not found\n./cmsshell.sh: 10: popd: not found\nException in thread \"main\" java.lang.NoClassDefFoundError: org/opencms/main/CmsShell\nCaused by: java.lang.ClassNotFoundException: org.opencms.main.CmsShell\n at java.net.URLClassLoader$1.run(URLClassLoader.java:200)\n at java.security.AccessController.doPrivileged(Native Method)\n at java.net.URLClassLoader.findClass(URLClassLoader.java:188)\n at java.lang.ClassLoader.loadClass(ClassLoader.java:307)\n at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)\n at java.lang.ClassLoader.loadClass(ClassLoader.java:252)\n at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)\nCould not find the main class: org.opencms.main.CmsShell. Program will exit.\n\n",[471,81106,81107,81111,81116,81121,81126,81131,81136,81141,81146,81151,81156,81161,81166,81171],{"__ignoreMap":469},[474,81108,81109],{"class":476,"line":477},[474,81110,917],{"emptyLinePlaceholder":916},[474,81112,81113],{"class":476,"line":507},[474,81114,81115],{},"/cmsshell.sh: 8: pushd: not found\n",[474,81117,81118],{"class":476,"line":547},[474,81119,81120],{},"./cmsshell.sh: 9: dirs: not found\n",[474,81122,81123],{"class":476,"line":584},[474,81124,81125],{},"./cmsshell.sh: 10: popd: not found\n",[474,81127,81128],{"class":476,"line":607},[474,81129,81130],{},"Exception in thread \"main\" java.lang.NoClassDefFoundError: org/opencms/main/CmsShell\n",[474,81132,81133],{"class":476,"line":642},[474,81134,81135],{},"Caused by: java.lang.ClassNotFoundException: org.opencms.main.CmsShell\n",[474,81137,81138],{"class":476,"line":663},[474,81139,81140],{}," at java.net.URLClassLoader$1.run(URLClassLoader.java:200)\n",[474,81142,81143],{"class":476,"line":694},[474,81144,81145],{}," at java.security.AccessController.doPrivileged(Native Method)\n",[474,81147,81148],{"class":476,"line":700},[474,81149,81150],{}," at java.net.URLClassLoader.findClass(URLClassLoader.java:188)\n",[474,81152,81153],{"class":476,"line":913},[474,81154,81155],{}," at java.lang.ClassLoader.loadClass(ClassLoader.java:307)\n",[474,81157,81158],{"class":476,"line":920},[474,81159,81160],{}," at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)\n",[474,81162,81163],{"class":476,"line":926},[474,81164,81165],{}," at java.lang.ClassLoader.loadClass(ClassLoader.java:252)\n",[474,81167,81168],{"class":476,"line":932},[474,81169,81170],{}," at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)\n",[474,81172,81173],{"class":476,"line":938},[474,81174,81175],{},"Could not find the main class: org.opencms.main.CmsShell. Program will exit.\n",[439,81177,81178],{},"To make it work you need to open the script and change the line",[439,81180,81181,81184],{},[471,81182,81183],{},"OPENCMS_BASE=","dirs +0``",[439,81186,81187],{},"to",[439,81189,81190,81192],{},[471,81191,81183],{},"pwd``",[439,81194,81195],{},"When saving the change and executing the script you should see OpenCms start and end at the prompt where you can login\nand execute any useful action you can think of.",[1024,81197,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":81199},[],[9045,1412],"2010-11-03T17:13:14","CmsShell is a neat tool to access the OpenCms VFS from the command line. This can be extremely\\nuseful when your system can’t be accessed anymore using the web interface or when performing batch updates. The script\\ncan be found in the OpenCms webapp at WEB-INF/cmsshell.sh.","https://synyx.de/blog/using-cmsshell-on-ubuntu-systems/",{},"/blog/using-cmsshell-on-ubuntu-systems",{"title":81034,"description":81207},"CmsShell is a neat tool to access the OpenCms VFS from the command line. This can be extremely\nuseful when your system can’t be accessed anymore using the web interface or when performing batch updates. The script\ncan be found in the OpenCms webapp at WEB-INF/cmsshell.sh.","blog/using-cmsshell-on-ubuntu-systems",[20503,64517],"CmsShell is a neat tool to access the OpenCms VFS from the command line. This can be extremely useful when your system can’t be accessed anymore using the web interface…","D3PyhEOAgZTPGgEbOBhZLYK4-r5C8IWO88rDvPj_q-k",{"id":81213,"title":81214,"author":81215,"body":81216,"category":81337,"date":81338,"description":81339,"extension":1034,"link":81340,"meta":81341,"navigation":916,"path":81342,"seo":81343,"slug":81220,"stem":81344,"tags":81345,"teaser":81348,"__hash__":81349},"blog/blog/maven-2-inheritance-before-interpolation.md","Maven 2: Inheritance before Interpolation",[166],{"type":432,"value":81217,"toc":81335},[81218,81221,81224,81231,81240,81247,81251,81254,81258,81262,81273,81276,81285,81288,81291,81298,81304,81307,81312,81317,81323,81326,81329,81332],[435,81219,81214],{"id":81220},"maven-2-inheritance-before-interpolation",[439,81222,81223],{},"Some days ago I came along a problem with our beloved build tool Maven2. Since this was the first real problem with the\ntool I could not solve or find a good workaround which I think is worth a blog post.",[439,81225,81226,81230],{},[1002,81227,81229],{"href":47203,"rel":81228},[1006],"Maven 2"," relies on a project descriptor for each project to build, which is XML in a file\ncalled pom.xml at the root of a project. Within that file you define how your project is to be built, what dependencies\nit needs and much more.",[439,81232,81233,81234,81239],{},"It is\na ",[1002,81235,81238],{"href":81236,"rel":81237},"http://www.sonatype.com/people/2010/08/how-to-migrate-from-ant-to-maven-project-structure/",[1006],"commonly used pattern"," to\navoid a monolithic build and divide your stuff to several smaller projects as the project grows (e.g. having an\napi-package, a core, an web-client and so on). Since all of these projects actually belong together you may want to\navoid duplication of xml within the project descriptors. So maven provides a powerful feature for this: pom *\n*inheritance**. Each project may have a project as parent from whose pom information gets inherited. Using this you can\ndefine common stuff like repositories, reporting-settings and project information at the the “parent-pom”.",[439,81241,81242,81243,1402],{},"So one of the entries we defined in the parent-pom is the repository-node which tells Maven places where it looks for\npackages needed for the build (e.g. dependencies). In our case this points to a repository hosted by ourselves at an\ninstance of ",[1002,81244,54629],{"href":81245,"rel":81246},"http://nexus.sonatype.org/",[1006],[439,81248,8990,81249],{},[1067,81250],{},[14402,81252,81253],{},"\ninternal.nexus.example.org\n",[81255,81256,81257],"name",{},"\nInternal Repo\n",[81259,81260,81261],"url",{},"\n${infrastructure.nexus.host}/content/groups/internal\n",[439,81263,81264,81265,81268,81269,81272],{},"As you can see we defined the hostname of the repository as a property which Maven resolves during the evaluation of the\nproject descriptor. This process is called ",[448,81266,81267],{},"Interpolation",". At some other place (within a pom or within global\nconfiguration) you are able to set the property ",[471,81270,81271],{},"infrastructure.nexus.host",". We did this since the hostnames are subject\nto change, as soon as the projects infrastructure is moved to our customers.",[439,81274,81275],{},"Ok, the first (solvabe) problem that occurred here is some kind of a chicken-egg problem. The parent itself is hosted\nat the internal repository which makes it impossible for maven to download before it knows from which repository.",[439,81277,81278,81279,81284],{},"So maven has to resolve inheritance first which means the repository, where the parent of a project can be downloaded\nmust be either defined in the child’s pom or the parent must be available in the\nglobal ",[1002,81280,81283],{"href":81281,"rel":81282},"https://web.archive.org/web/20220313074201/https://repo1.maven.org/maven2/",[1006],"maven central repository"," (which was\nnot an option for us). I know there is another way which means defining the repository in the global settings.xml file\nwhich I wanted to avoid because it brings along other, in my opinion even bigger, problems.",[439,81286,81287],{},"So we decided to add the repository-node (as seen above) to each projects’ pom.xml so that maven can download the\nparent project from the internal Nexus first. Since we still needed to be able to override the hostname the property\nstill needed to be defined somewhere else than in the parent (e.g. on commandline or using settings.xml).",[439,81289,81290],{},"But as soon as you try to build your project maven fails:",[439,81292,81293,81294],{},"`$ mvn validate -Dinfrastructure.nexus.host=",[1002,81295,81296],{"href":81296,"rel":81297},"http://example.org",[1006],[439,81299,81300,81303],{},[474,81301,81302],{},"INFO"," Scanning for projects...",[439,81305,81306],{},"Downloading: $\n{infrastructure.nexus.host}/content/groups/internal/org/example/parent/1.0.0-SNAPSHOT/parent-1.0.0-SNAPSHOT.pom",[439,81308,81309,81311],{},[474,81310,81302],{}," Unable to find resource 'org.example:parent:pom:1.0.0-SNAPSHOT' in repository internal.nexus.example.org ($\n{infrastructure.nexus.host}/content/groups/internal)",[439,81313,81314,81316],{},[474,81315,81302],{}," ------------------------------------------------------------------------",[439,81318,81319,81322],{},[474,81320,81321],{},"ERROR"," FATAL ERROR",[439,81324,81325],{},"...`",[439,81327,81328],{},"As you could see, in the URL where maven tried to download the parent-project it did not resolve the property.",[439,81330,81331],{},"This is because maven FIRST does inheritance and THEN interpolation. Its clear that it cannot do it the other way\nround (since the parent project may define properties that are needed for interpolation). But I cannot understand why\nmaven does not do FIRST interpolation, THEN inheritance and THEN interpolates again. Does anyone know?",[439,81333,81334],{},"To “solve” our problem we currently do not use interpolation within the child’s repository-configuration which sucks\nbecause it means to change a lot of pom.xml files later (and even released ones…).",{"title":469,"searchDepth":507,"depth":507,"links":81336},[],[1030],"2010-10-27T11:12:04","Some days ago I came along a problem with our beloved build tool Maven2. Since this was the first real problem with the\\ntool I could not solve or find a good workaround which I think is worth a blog post.","https://synyx.de/blog/maven-2-inheritance-before-interpolation/",{},"/blog/maven-2-inheritance-before-interpolation",{"title":81214,"description":81223},"blog/maven-2-inheritance-before-interpolation",[50285,81346,22988,81347],"infrasturcture","project","Some days ago I came along a problem with our beloved build tool Maven2. Since this was the first real problem with the tool I could not solve or find…","DE9b4EbyThYYTZsKfp0jo0ecDbOKqlG1HLKUNXmIuYE",{"id":81351,"title":81352,"author":81353,"body":81354,"category":81462,"date":81463,"description":81464,"extension":1034,"link":81465,"meta":81466,"navigation":916,"path":81467,"seo":81468,"slug":81469,"stem":81470,"tags":81471,"teaser":81472,"__hash__":81473},"blog/blog/neue-synyx-lounge-schafft-atmosphare.md","Neue Synyx Lounge schafft Atmosphäre",[172],{"type":432,"value":81355,"toc":81460},[81356,81359,81373,81381,81384,81387,81392,81395,81405,81408,81418,81421,81431,81434,81444,81447,81457],[435,81357,81352],{"id":81358},"neue-synyx-lounge-schafft-atmosphäre",[439,81360,81361,81370],{},[1002,81362,81365],{"href":81363,"rel":81364},"https://media.synyx.de/uploads//2010/10/informatiker.png",[1006],[2205,81366],{"alt":81367,"src":81368,"title":81369},"Informatiker","https://media.synyx.de/uploads//2010/10/informatiker-200x300.png","informatiker",[448,81371,81372],{},"Trister Büroalltag eines Informatikers: Großraumbüro, Monitor neben Monitor, Schreibtisch neben Schreibtisch,\nFachlektüre und Kundenmappen stapeln sich in den Ablagen. Karges Licht von Leuchtstoffröhren bestimmen den\nArbeitsalltag. Ist dies Vorurteil oder entspricht es der Realität? Synyx zeigt, dass es auch anders geht. Denn\nWohlfühlen schafft Kreativität und fördert die Leistung eines Einzelnen.",[439,81374,81375,81376,81380],{},"Bereits 2009 erweiterte Synyx die Räumlichkeiten in der Karlstraße um eine zusätzliche Etage. So entstanden vier weitere\nkomfortable Arbeitsbereiche. Durch das Geschäftsfeld ",[1002,81377,81379],{"href":72426,"rel":81378},[1006],"“Code Clinic”","\nwurde der Bedarf an Haus-interen Workshops immer bedeutender. Hierzu benötigte man selbstverständlich Räumlichkeiten\nmit einer guten Infrastruktur und effektivem Arbeitsklima.",[439,81382,81383],{},"So entstand im ersten Stock der Karlstraße ein weiterer Meetingraum und die sogenannte „Synyx Lounge“ mit Kaffeebar. Die\nLounge-Atmosphäre verschafft den Mitarbeitern, die Möglichkeit der kurzen Entspannung oder aber auch kleinere Meetings\nabzuhalten. Um das Ganze abzurunden, gibt es in der „Synyx Lounge“ ein „Games-Corner“ mit diversen Spielkonsolen, einem\nTischkicker und dem Synyx Info-Terminal. Besucher können sich hier weitere Informationen über das Unternehmen einholen\noder im Web surfen. Auch der neue Meetingraum wird bereits rege für zahlreiche Workshops genutzt und hat sich dadurch\nlängst bewährt.",[439,81385,81386],{},"Marius van Herpen, CONTARGO trimodal network:",[439,81388,81389],{},[990,81390,81391],{},"“Beim ersten Besuch in eurem Haus fiel schon die gemütliche Sitzecke auf, wo man sich mal vor Anfang der Arbeit, oder\nzwischendurch zur Pause hinsetzen kann. Die Computer aus der 80er Jahren riefen auch noch ein Paar Erinnerungen aus\ndieser Zeit auf. Und auch nicht gerade unwichtig, der Kaffee schmeckt auch gut!”",[439,81393,81394],{},"Verschaffen Sie sich einen kleinen Eindruck von unseren neuen Räumlichkeiten:",[439,81396,81397],{},[1002,81398,81401],{"href":81399,"rel":81400},"https://media.synyx.de/uploads//2010/10/lobby_05.png",[1006],[2205,81402],{"alt":81403,"src":81399,"title":81404},"Kaffe Lounge","lobby_05",[439,81406,81407],{},"Die Synyx Lounge",[439,81409,81410],{},[1002,81411,81414],{"href":81412,"rel":81413},"https://media.synyx.de/uploads//2010/10/lobby_03.png",[1006],[2205,81415],{"alt":81416,"src":81412,"title":81417},"Lounge","lobby_03",[439,81419,81420],{},"Gemütliches Meeting bei einer Tasse Kaffee",[439,81422,81423],{},[1002,81424,81427],{"href":81425,"rel":81426},"https://media.synyx.de/uploads//2010/10/gamecorner_01.png",[1006],[2205,81428],{"alt":81429,"src":81425,"title":81430},"Gamescorner","gamecorner_01",[439,81432,81433],{},"Games-Corner: Um mal abzuspannen stehen hier den Mitarbeitern und Besuchern diverse Spielekonsolen zur Verfügung (Wii,\nSuper Nintendo, Schneider CPC 464, C64). Weiterhin gibt es hier unser Info-Terminal.",[439,81435,81436],{},[1002,81437,81440],{"href":81438,"rel":81439},"https://media.synyx.de/uploads//2010/10/regal_01.png",[1006],[2205,81441],{"alt":81442,"src":81438,"title":81443},"Leseraum","regal_01",[439,81445,81446],{},"Es stehen ausreichend Fachliteratur und aktuelle Fachzeitschriften zur Verfügung.",[439,81448,81449],{},[1002,81450,81453],{"href":81451,"rel":81452},"https://media.synyx.de/uploads//2010/10/meetingraum_03.png",[1006],[2205,81454],{"alt":81455,"src":81451,"title":81456},"Meetingraum","meetingraum_03",[439,81458,81459],{},"Der Meetingraum kann individuell angepasst werden, je nachdem für wie viel Personen oder welchen Zweck er benötigt wird.",{"title":469,"searchDepth":507,"depth":507,"links":81461},[],[1031],"2010-10-26T09:49:02","Trister Büroalltag eines Informatikers: Großraumbüro, Monitor neben Monitor, Schreibtisch neben Schreibtisch,\\nFachlektüre und Kundenmappen stapeln sich in den Ablagen. Karges Licht von Leuchtstoffröhren bestimmen den\\nArbeitsalltag. Ist dies Vorurteil oder entspricht es der Realität? Synyx zeigt, dass es auch anders geht. Denn\\nWohlfühlen schafft Kreativität und fördert die Leistung eines Einzelnen.","https://synyx.de/blog/neue-synyx-lounge-schafft-atmosphare/",{},"/blog/neue-synyx-lounge-schafft-atmosphare",{"title":81352,"description":81372},"neue-synyx-lounge-schafft-atmosphare","blog/neue-synyx-lounge-schafft-atmosphare",[],"Trister Büroalltag eines Informatikers: Großraumbüro, Monitor neben Monitor, Schreibtisch neben Schreibtisch, Fachlektüre und Kundenmappen stapeln sich in den Ablagen. Karges Licht von Leuchtstoffröhren bestimmen den Arbeitsalltag. Ist dies Vorurteil oder…","yuFoxl74iIwUF67MMsYqEZPg_53MSsXbHeKo2kd8BZY",{"id":81475,"title":81476,"author":81477,"body":81478,"category":81564,"date":81565,"description":81566,"extension":1034,"link":81567,"meta":81568,"navigation":916,"path":81569,"seo":81570,"slug":81571,"stem":81572,"tags":81573,"teaser":81575,"__hash__":81576},"blog/blog/ionice.md","ionice and the lullaby",[88],{"type":432,"value":81479,"toc":81562},[81480,81483,81486,81500,81521,81524,81533,81536,81539,81542,81545,81548,81551],[435,81481,81476],{"id":81482},"ionice-and-the-lullaby",[439,81484,81485],{},"Back in 2003, I’ve been working as trainee at a big webhosting company. Back in the days I’ve been really keen on the\nupcoming 2.6 linux kernel series, and compiled nearly every RC, but when the real working at the providers abuse\ndepartment started I’ve lost track of all the new features that are implemented into the kernel, besides the “popular”\nstuff, like btrfs…",[439,81487,81488,81489,15250,81494,81499],{},"That’s the reason I totally missed out the CFQ io scheduler, which is currently the default scheduler for io used by\nvanilla kernels, and is available in linux kernel\nsince ",[1002,81490,81493],{"href":81491,"rel":81492},"http://kernelnewbies.org/Linux_2_6_13",[1006],"2.6.13",[1002,81495,81498],{"href":81496,"rel":81497},"http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=22e2c507c301c3dbbcf91b4948b88f78842ee6c9",[1006],"commit","),\nuntil yesterday in the evening, when I was “forced” to refresh my knowledge on io scheduling.",[439,81501,81502,81503,81508,81509,81514,81515,81520],{},"While aptitude was extracting and installing loads of updates, and my torrent client doing some voodoo while downloading\nsome episodes of ",[1002,81504,81507],{"href":81505,"rel":81506},"http://chaosradio.ccc.de/chaosradio_express.html",[1006],"Chaos Radio Express"," my raid got performance issues\nwhen I tried to watch\na ",[1002,81510,81513],{"href":81511,"rel":81512},"https://ftp.scene.org/mirrors/breakpoint2008/concerts/BP08_Concert_ReynOuwehand.mp4",[1006],"Concert Video","\nof ",[1002,81516,81519],{"href":81517,"rel":81518},"http://en.wikipedia.org/wiki/Reyn_Ouwehand",[1006],"Reyn Ouwehand",", kinda the nerd version of mothers good old lullaby.",[439,81522,81523],{},"So, being a bit sleepy the first thing i thought was “Hey, in 2010 there must be a way to schedule io on a linux\nsystem.”, so I opened up a shell, typed io\\t\\t and, like a punch in my face, ionice showed up…",[439,81525,81526,81527,81532],{},"So I read the ",[1002,81528,81531],{"href":81529,"rel":81530},"http://linux.die.net/man/1/ionice",[1006],"manpage",", which is short and good, so there’s no need to explain all\nthe stuff here. The most important, and the only 2 switches for scheduling your io are -c and -n",[439,81534,81535],{},"`---snip---",[439,81537,81538],{},"-c class",[439,81540,81541],{},"The scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.",[439,81543,81544],{},"-n classdata",[439,81546,81547],{},"The scheduling class data. This defines the class data, if the class accepts an argument. For real time and best effort,\n0-7 is valid data.",[439,81549,81550],{},"---snip---`",[439,81552,81553,81554,81557,81558,81561],{},"When I fired up my video with the command ",[471,81555,81556],{},"ionice -n 0 mplayer BP08_Concert_ReynOuwehand.mp4",", the video still stopped\nevery now and then… After blaming my hardware, I came up with the idea of checking which priorities are used by the\nsoftware blocking my io. I was sure the torrent client uses the default value for ubuntu (-n 4), but when I checked\naptitude by ",[471,81559,81560],{},"ionice -p $aptitude_PID"," I discovered that aptitude seems to use -n 0, so I lowered the priority of\naptitude and was finally able to view the video without random hangs, but at the time the need of a lullaby was gone ;)",{"title":469,"searchDepth":507,"depth":507,"links":81563},[],[9045],"2010-10-20T17:43:35","Back in 2003, I’ve been working as trainee at a big webhosting company. Back in the days I’ve been really keen on the\\nupcoming 2.6 linux kernel series, and compiled nearly every RC, but when the real working at the providers abuse\\ndepartment started I’ve lost track of all the new features that are implemented into the kernel, besides the “popular”\\nstuff, like btrfs…","https://synyx.de/blog/ionice/",{},"/blog/ionice",{"title":81476,"description":81485},"ionice","blog/ionice",[81574,20503],"kernel","Back in 2003, I’ve been working as trainee at a big webhosting company. Back in the days I’ve been really keen on the upcoming 2.6 linux kernel series, and compiled…","9yWjE_EHIy3Rkzo1cCiVQH7WTlMWKjwsevrZUWFW148",{"id":81578,"title":81579,"author":81580,"body":81581,"category":81666,"date":81667,"description":81588,"extension":1034,"link":81668,"meta":81669,"navigation":916,"path":81670,"seo":81671,"slug":81672,"stem":81673,"tags":81674,"teaser":81676,"__hash__":81677},"blog/blog/simple-shell-script-to-use-dict-leo-org-in-your-shell.md","Simple Shell-Script to use dict.leo.org in your shell",[51],{"type":432,"value":81582,"toc":81664},[81583,81586,81589,81592,81595,81598,81605,81608,81616,81619,81622,81625,81628,81630,81632,81637,81640,81643,81646,81649,81652,81655,81658,81661],[435,81584,81579],{"id":81585},"simple-shell-script-to-use-dictleoorg-in-your-shell",[439,81587,81588],{},"Just create a new file like “vim leo”.",[439,81590,81591],{},"Insert the following script code:",[439,81593,81594],{},"`#!/bin/sh",[439,81596,81597],{},"t(){",[439,81599,81600,81601,81604],{},"while ",[474,81602,81603],{}," -n \"$1\"","; do",[439,81606,81607],{},"T=/tmp/$$.html",[439,81609,81610,81611],{},"lynx -source \"",[1002,81612,81615],{"href":81613,"rel":81614},"http://dict.leo.org/?search=$1%22%7C%5C",[1006],"http://dict.leo.org/?search=$1\"|\\",[439,81617,81618],{},"grep results >$T",[439,81620,81621],{},"w3m -dump $T",[439,81623,81624],{},"rm $T",[439,81626,81627],{},"shift",[439,81629,29454],{},[439,81631,19768],{},[439,81633,81634,81636],{},[474,81635,81603],{},"&&\\",[439,81638,81639],{},"t \"$@\"||\\",[439,81641,81642],{},"while read -ep dict2> W; do",[439,81644,81645],{},"t $W|more",[439,81647,81648],{},"done`",[439,81650,81651],{},"You need lynx and w3m.",[439,81653,81654],{},"Make the file executable (chmod +x leo)",[439,81656,81657],{},"enjoy 😉",[439,81659,81660],{},"e.g. ./leo Übersetzung",[439,81662,81663],{},"I have found this script somewhere in the internet but I don’t know where, so I can not refrence to the original source.\nI apologize for this.",{"title":469,"searchDepth":507,"depth":507,"links":81665},[],[1030],"2010-10-16T20:01:04","https://synyx.de/blog/simple-shell-script-to-use-dict-leo-org-in-your-shell/",{},"/blog/simple-shell-script-to-use-dict-leo-org-in-your-shell",{"title":81579,"description":81588},"simple-shell-script-to-use-dict-leo-org-in-your-shell","blog/simple-shell-script-to-use-dict-leo-org-in-your-shell",[81675,56694],"leo","Just create a new file like “vim leo”. Insert the following script code: #!/bin/sh t(){ while [ -n '$1' ]; do T=/tmp/$$.html lynx -source 'http://dict.leo.org/?search=$1'| grep results >$T w3m -dump…","wDc0iR9K9xzpPFHzMjeJiZ7qqTp6bcL7y_c0_GgI-gU",{"id":81679,"title":81680,"author":81681,"body":81682,"category":81718,"date":81719,"description":81720,"extension":1034,"link":81721,"meta":81722,"navigation":916,"path":81723,"seo":81724,"slug":81686,"stem":81725,"tags":81726,"teaser":81727,"__hash__":81728},"blog/blog/context-reload-with-tomcat.md","Context reload with Tomcat",[148],{"type":432,"value":81683,"toc":81716},[81684,81687,81694,81697,81702,81705,81708],[435,81685,81680],{"id":81686},"context-reload-with-tomcat",[439,81688,81689,81690,81693],{},"Ever wondered why ",[1002,81691,54620],{"href":54618,"rel":81692},[1006]," reloads the context when editing web.xml?",[439,81695,81696],{},"This is a default configuration that can also be adjusted to your needs. The file conf/context.xml is the default\ncontext configuration that is used for all webapps. In this file you can find the line",[439,81698,81699],{},[471,81700,81701],{},"\u003CWatchedResource>WEB-INF/web.xml\u003C/WatchedResource>",[439,81703,81704],{},"which triggers the reload for any web.xml.",[439,81706,81707],{},"You can either add more resources here or, preferably, add your own context configuration with your resources.",[439,81709,81710,81711,1402],{},"Find out more about context configuration in\nthe ",[1002,81712,81715],{"href":81713,"rel":81714},"http://tomcat.apache.org/tomcat-6.0-doc/config/context.html",[1006],"Tomcat documentation",{"title":469,"searchDepth":507,"depth":507,"links":81717},[],[1030,1412],"2010-10-13T16:08:56","Ever wondered why Tomcat reloads the context when editing web.xml?","https://synyx.de/blog/context-reload-with-tomcat/",{},"/blog/context-reload-with-tomcat",{"title":81680,"description":81720},"blog/context-reload-with-tomcat",[51154],"Ever wondered why Tomcat reloads the context when editing web.xml? This is a default configuration that can also be adjusted to your needs. The file conf/context.xml is the default context…","rqgNbT2dAL4FFc59uZqZxj2Vd1hSH_5x_lWFp8y9NCs",{"id":81730,"title":81731,"author":81732,"body":81733,"category":81806,"date":81807,"description":81808,"extension":1034,"link":81809,"meta":81810,"navigation":916,"path":81811,"seo":81812,"slug":81737,"stem":81813,"tags":81814,"teaser":81815,"__hash__":81816},"blog/blog/console-logging-with-opencms.md","Console logging with OpenCms",[148],{"type":432,"value":81734,"toc":81804},[81735,81738,81743,81746,81770,81772,81799,81802],[435,81736,81731],{"id":81737},"console-logging-with-opencms",[439,81739,81740],{},[990,81741,81742],{},"We are currently in the process of cleaning up our internal wiki. A lot of information is quite outdated but it also\ncontains some useful snippets that we would like to share with the rest of the world.",[439,81744,81745],{},"The log4j ConsoleAppender is already configured in OpenCms log4j.properties but not enabled by default. To enable it\nchange the configuration",[464,81747,81749],{"className":59265,"code":81748,"language":59267,"meta":469,"style":469},"\nlog4j.rootLogger=\\\n ERROR,\\\n OC\n\n",[471,81750,81751,81755,81760,81765],{"__ignoreMap":469},[474,81752,81753],{"class":476,"line":477},[474,81754,917],{"emptyLinePlaceholder":916},[474,81756,81757],{"class":476,"line":507},[474,81758,81759],{},"log4j.rootLogger=\\\n",[474,81761,81762],{"class":476,"line":547},[474,81763,81764],{}," ERROR,\\\n",[474,81766,81767],{"class":476,"line":584},[474,81768,81769],{}," OC\n",[439,81771,81187],{},[464,81773,81775],{"className":59265,"code":81774,"language":59267,"meta":469,"style":469},"\nlog4j.rootLogger=\\\n ERROR,\\\n OC,\\\n CONSOLE\n\n",[471,81776,81777,81781,81785,81789,81794],{"__ignoreMap":469},[474,81778,81779],{"class":476,"line":477},[474,81780,917],{"emptyLinePlaceholder":916},[474,81782,81783],{"class":476,"line":507},[474,81784,81759],{},[474,81786,81787],{"class":476,"line":547},[474,81788,81764],{},[474,81790,81791],{"class":476,"line":584},[474,81792,81793],{}," OC,\\\n",[474,81795,81796],{"class":476,"line":607},[474,81797,81798],{}," CONSOLE\n",[439,81800,81801],{},"This is useful when starting OpenCms from within an IDE like Netbeans or Eclipse which display the Console window by\ndefault.",[1024,81803,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":81805},[],[1030,1412],"2010-10-12T18:44:46","We are currently in the process of cleaning up our internal wiki. A lot of information is quite outdated but it also\\ncontains some useful snippets that we would like to share with the rest of the world.","https://synyx.de/blog/console-logging-with-opencms/",{},"/blog/console-logging-with-opencms",{"title":81731,"description":81742},"blog/console-logging-with-opencms",[64517],"We are currently in the process of cleaning up our internal wiki. A lot of information is quite outdated but it also contains some useful snippets that we would like…","YLxxqXYBKnGrglUy2I9bdVGutI6JPiBeaHv7xk9P0FM",{"id":81818,"title":81819,"author":81820,"body":81821,"category":81896,"date":81897,"description":81898,"extension":1034,"link":81899,"meta":81900,"navigation":916,"path":81901,"seo":81902,"slug":81904,"stem":81905,"tags":81906,"teaser":81909,"__hash__":81910},"blog/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann.md","'Fragile Agile' von Pavlo Baron und Michael Hüttermann",[15],{"type":432,"value":81822,"toc":81894},[81823,81827,81850,81855,81858,81861,81866,81869,81874,81877,81880,81883,81886,81889],[435,81824,81826],{"id":81825},"fragile-agile-von-pavlo-baron-und-michael-hüttermann","\"Fragile Agile\" von Pavlo Baron und Michael Hüttermann",[439,81828,81829,81838,81839,2040,81844,81849],{},[1002,81830,81833],{"href":81831,"rel":81832},"https://media.synyx.de/uploads//2010/09/fragile_agile.png",[1006],[2205,81834],{"alt":81835,"src":81836,"title":81837},"Buch Fragile Agile","https://media.synyx.de/uploads//2010/09/fragile_agile-238x300.png","fragile_agile","\nGespannt habe ich auf das neue Buch der Kollegen ",[1002,81840,81843],{"href":81841,"rel":81842},"http://www.pbit.org",[1006],"Baron",[1002,81845,81848],{"href":81846,"rel":81847},"http://huettermann.net/",[1006],"Hüttermann","\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.",[439,81851,81852],{},[990,81853,81854],{},"“In der freien Wirtschaft ist nur selten Platz für rein technische Spielereien. Gut, bei Google oder Microsoft\nvielleicht, deswegen zieht es da ja auch so viele Geeks hin, als wäre ihr Logo mit Honig beschmiert”",[439,81856,81857],{},"Heutzutage wird Agilität fast immer direkt gleichgesetzt mit Scrum und/oder Kanban.",[439,81859,81860],{},"Damit beginnt das Buch und räumt hier auch direkt mit dieser Fehlinterpretation auf, um in den folgenden Kapiteln\nMissverständnis für Missverständnis aufzuräumen. Hierbei gelingt es den Kollegen, man merkt ihnen die vielen Jahre\nErfahrung an, durch geschickte Metaphern, trotz des eigentlich sehr anstrengenden Stoffs, den Leser bei Laune und beim\nLesen und Verstehen zu halten. Zumindest erging es mir so. Es gibt wenige Fachbücher, die ich in 2 ICE-Sessions\ndurchgelesen hatte und direkt auch rezensieren wollte.",[439,81862,81863],{},[990,81864,81865],{},"“Truckfaktor: Die Anzahl von Menschen im Projektteam, die von einem Truck erfasst werden müssen, bevor das Projekt in\nernsthafte Schwierigkeiten gerät”",[439,81867,81868],{},"Das Buch erläutert sachlich, manchmal durchaus auch ein wenig (bewusst) polemisch, direkte Implikationen innerhalb von\nTeams. Besonderen Wert legen die Autoren hier auf die sozialen Komponenten des Menschen im Teams: Motivation,\nKommunikation und Nachhaltigkeit, wobei Letzteres aus meiner Sicht ein klein wenig zu kurz kommt.",[439,81870,81871],{},[990,81872,81873],{},"“Sie arbeiten schnell und somit agil? Falsch! Agil heißt, den Kunden zufriedenzustellen, nicht schnell etwas\nherunterzuklopfen”",[439,81875,81876],{},"Was man aber nicht erwarten darf, sind die 10 Tipps, die es dem Mitarbeiter im Projekt einfacher machen, erfolgreicher,\neffizienter zu arbeiten. Vermutlich ist das auch Absicht, damit der Leser mehr nachdenkt und nicht “stumpf” einem\nKatalog von Pattern folgt 😉",[439,81878,81879],{},"Die Beispiele und Berichte aus der realen Welt sind durchaus stimmig gewählt und bringen viele der Argumente noch klarer\nzum Vorschein. Besonders in den Kapiteln 9 und 10 (Funktionierende Software, Nachhaltige Geschwindigkeit) wird sich\nsicherlich fast jeder Entwickler wieder finden und die Argumente bestätigen können.",[439,81881,81882],{},"Als Ergänzung liegt dem Buch übrigens ein Gutscheincode für einen eBook Download bei! Hierfür muss man dem Hanser Verlag\ndicken Respekt zollen!",[439,81884,81885],{},"Ich kann dieses Buch jedem Projektleiter, jedem Entwickler aber auch jedem “Chef” nur wärmstens empfehlen, um zu\nbegreifen, wie die sozialen Aspekte direkt auf den Projekterfolg einwirken. Was ich besonders gelungen finde, ist das\nsich das Buch nicht an Knaben oder Serum entlang hangelt, sondern auch deutlich macht, dass Agilität zuerst einmal\nnichts mit einem Prozess- / Vorgehensmodell zu tun hat, sondern eigentlich schlicht das Zusammenwirken sozialer\nKomponenten in einem Team von “Machern” ist.",[439,81887,81888],{},"Das ist eines der Bücher, obwohl deutschsprachig, welches wirklich jeder einmal lesen sollte, der es mit Agilität ernst\nnimmt! Kurz, knackig, ohne Buzzwordanhäufung und wilde Tipps, sondern viel mehr zum selbst nachdenken anregend! (und das\nist aus meiner Sicht wiederum agil 🙂 )",[439,81890,81891],{},[990,81892,81893],{},"“Design is not what it looks like and feels like. Design is how it works!” (Steve Jobs)",{"title":469,"searchDepth":507,"depth":507,"links":81895},[],[1031],"2010-09-28T09:17:01","\\nGespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann\\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.","https://synyx.de/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann/",{},"/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann",{"title":81819,"description":81903},"\nGespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.","fragile-agile-von-pavlo-baron-und-michael-huttermann","blog/fragile-agile-von-pavlo-baron-und-michael-huttermann",[31767,81907,81908,37776,77893,47537,9557],"baron","huttermann","Gespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann über die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich Agilität aufgenommen…","yjJDu2eI2ec2lTpFsxyvPMyU7zDZ48e4gcJsLOIe-Aw",{"id":81912,"title":81913,"author":81914,"body":81915,"category":81951,"date":81952,"description":81953,"extension":1034,"link":81954,"meta":81955,"navigation":916,"path":81956,"seo":81957,"slug":81919,"stem":81958,"tags":81959,"teaser":81960,"__hash__":81961},"blog/blog/sommerfeier-2010.md","Sommerfeier 2010",[88],{"type":432,"value":81916,"toc":81949},[81917,81920,81931,81934,81946],[435,81918,81913],{"id":81919},"sommerfeier-2010",[439,81921,81922,81930],{},[1002,81923,81926],{"href":81924,"rel":81925},"https://media.synyx.de/uploads//2010/09/raw_DSC_01361.jpg",[1006],[2205,81927],{"alt":469,"src":81928,"title":81929},"https://media.synyx.de/uploads//2010/09/raw_DSC_01361-150x150.jpg","raw_DSC_0136","\nVor knapp einem Monat, am 28.08 fand die Synyx Sommerfeier mit Kollegen, Partnern und ehemaligen Kollegen statt. Die\nFeier fand auf dem Gelände des PHV statt, welches im Karlsruher Westen, am Rande von Knielingen liegt, und damit auch\nfür unsere Kollegen aus der Pfalz der Anfahrtsweg nicht allzu lang ist.",[439,81932,81933],{},"Wie es sich für ein Sommerfest gehört, wurde natürlich gegrillt, was nicht nur bei unseren Karnivoren, sondern auch bei\nunseren Veganern und Vegetariern das Wasser im Mund zusammenlaufen ließ. Nachdem wir durch unseren Hunger getrieben uns\nam Grillgut und den Salaten gesättigt hatten, konnten wir die angrenzende Wiese mit Hilfe eines Fussballs und Frisbees\ndie frischen Kalorieen gleich abbauen.",[439,81935,81936,81945],{},[1002,81937,81940],{"href":81938,"rel":81939},"https://media.synyx.de/uploads//2010/09/raw_DSC_0185.jpg",[1006],[2205,81941],{"alt":81942,"src":81943,"title":81944},"offline spass mit loopin' louie","https://media.synyx.de/uploads//2010/09/raw_DSC_0185-150x150.jpg","infinitive loop","\nDurch die leider nicht ganz sommerlichen Temperaturen, verlagerte sich die Feier nach und nach in das Vereinslokal des\nPHV, in welchem schon 2 unserer Geschäftsführer nach unsere Pfeife tanzten, und für die durstigen Kehlen leckere\nCocktails mixten. Durch looping louie, Bällen zum Jonglieren, Jojos und anderem Utensil verstrich die Zeit wie im Flug,\nund das Ende des Abends war wie im Flug erreicht.",[439,81947,81948],{},"Nun bleibt uns nur noch, uns an die schönen Momente zu erinnern, und auf die Nächste Feier zu freuen.",{"title":469,"searchDepth":507,"depth":507,"links":81950},[],[1031],"2010-09-21T11:07:39","\\nVor knapp einem Monat, am 28.08 fand die Synyx Sommerfeier mit Kollegen, Partnern und ehemaligen Kollegen statt. Die\\nFeier fand auf dem Gelände des PHV statt, welches im Karlsruher Westen, am Rande von Knielingen liegt, und damit auch\\nfür unsere Kollegen aus der Pfalz der Anfahrtsweg nicht allzu lang ist.","https://synyx.de/blog/sommerfeier-2010/",{},"/blog/sommerfeier-2010",{"title":81913,"description":81930},"blog/sommerfeier-2010",[],"Vor knapp einem Monat, am 28.08 fand die Synyx Sommerfeier mit Kollegen, Partnern und ehemaligen Kollegen statt. Die Feier fand auf dem Gelände des PHV statt, welches im Karlsruher Westen,…","f1HvqKyNHasiUfp8T-5cWv9irwhpWlJuL_BAXYaAWNE",{"id":81963,"title":81964,"author":81965,"body":81966,"category":82013,"date":82014,"description":81973,"extension":1034,"link":82015,"meta":82016,"navigation":916,"path":82017,"seo":82018,"slug":81970,"stem":82019,"tags":82020,"teaser":82021,"__hash__":82022},"blog/blog/baden-lief-und-wir-waren-dabei.md","Baden lief und wir waren dabei",[314],{"type":432,"value":81967,"toc":82011},[81968,81971,81974,81977,81996,82005,82008],[435,81969,81964],{"id":81970},"baden-lief-und-wir-waren-dabei",[439,81972,81973],{},"Gestern am Sonntag den 19.09.2010 war es wieder so weit, der alljährliche Baden-Marathon fand statt.",[439,81975,81976],{},"Als Informatiker sind wir natürlich nicht alle einen separaten 42,195 km langen Marathon gelaufen, sondern teilten diese\nStrecke beim 6. “PSD Business-Marathon” zu dritt als Staffel.",[439,81978,81979,81983,81984,81989,81990,81995],{},[1002,81980,80682],{"href":81981,"rel":81982},"http://blog.synyx.de/autoren/?uid=17",[1006]," lief 21,1 km, ",[1002,81985,81988],{"href":81986,"rel":81987},"http://blog.synyx.de/autoren/?uid=20",[1006],"Ich"," lief die 15\nkm, und ",[1002,81991,81994],{"href":81992,"rel":81993},"http://blog.synyx.de/autoren/?uid=6",[1006],"Markus",", der heute übrigens Geburtstag hat (Alles Gute an dieser Stelle),\ndie 6 km zum Ziel ins Beiertheim-Stadion.",[439,81997,81998,81999,82004],{},"Wir haben mit einer zufriedenstellenden Zeit von 4:30:29 den Marathon erfolgreich absolviert, und unterwegs eine Menge\nSpaß mit den Zuschauern und verschiedenen Bands und Tanzgruppen, die einen förmlich über\ndie ",[1002,82000,82003],{"href":82001,"rel":82002},"http://www.badenmarathon.de/cms/fileadmin/Inhalte/Inhaltsbilder/strecke2010.jpg",[1006],"schöne Laufstrecke"," trugen,\ngehabt.",[439,82006,82007],{},"Für nächstes Jahr peilen wir eine noch bessere Zeit, ob beim separaten Halbmarathon oder bei der Herausforderung als\nTeam, an.",[439,82009,82010],{},"Doch auch dann wird hoffentlich wieder die Freude über das gemeinsame Meistern der Herausforderung überwiegen.",{"title":469,"searchDepth":507,"depth":507,"links":82012},[],[1031],"2010-09-20T11:20:07","https://synyx.de/blog/baden-lief-und-wir-waren-dabei/",{},"/blog/baden-lief-und-wir-waren-dabei",{"title":81964,"description":81973},"blog/baden-lief-und-wir-waren-dabei",[],"Gestern am Sonntag den 19.09.2010 war es wieder so weit, der alljährliche Baden-Marathon fand statt. Als Informatiker sind wir natürlich nicht alle einen separaten 42,195 km langen Marathon gelaufen, sondern…","vfHWyV1vcwvjr4ys_1vcmVC5KC5HebrqyicqYXO_jrY",{"id":82024,"title":82025,"author":82026,"body":82027,"category":82494,"date":82495,"description":82496,"extension":1034,"link":82497,"meta":82498,"navigation":916,"path":82499,"seo":82500,"slug":82031,"stem":82502,"tags":82503,"teaser":82506,"__hash__":82507},"blog/blog/android-roboguice-against-oncreate-boilerplate.md","Android: RoboGuice against onCreate boilerplate",[205],{"type":432,"value":82028,"toc":82488},[82029,82032,82043,82047,82075,82079,82082,82267,82273,82419,82425,82429,82467,82471,82483,82486],[435,82030,82025],{"id":82031},"android-roboguice-against-oncreate-boilerplate",[439,82033,82034,82035,82038,82039,82042],{},"When prototyping Android activities with a lot of view elements, the ",[471,82036,82037],{},"onCreate"," method can quickly become cluttered.\nSetup code that simply retrieves the views from the declarative layout (by using ",[471,82040,82041],{},"findViewById(int id)",") quickly fills\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\non it’s actual task again!",[3938,82044,82046],{"id":82045},"enter-dependency-injection","Enter dependency injection",[439,82048,82049,82050,82055,82056,82059,82060,82065,82066,82071,82072,82074],{},"A popular design pattern — usually accompanied by a framework — to get rid of object setup clutter\nis ",[1002,82051,82054],{"href":82052,"rel":82053},"http://en.wikipedia.org/wiki/Dependency_injection",[1006],"Dependency Injection"," (in short DI). The basic idea is, that\ninstead of pulling in all your dependencies (for example the views in our case) , a central point (the injection\ncontainer) manages and coordinates the creation of your dependencies and ",[990,82057,82058],{},"injects"," them into the objects that require\nthem. In the Java world several of these containers and frameworks exist, from the heavy weights like Spring to the very\nlightweight like ",[1002,82061,82064],{"href":82062,"rel":82063},"http://code.google.com/p/google-guice/",[1006],"Google Guice",". For the latter a young but promising add-on\nlibrary has come to fruition: ",[1002,82067,82070],{"href":82068,"rel":82069},"http://code.google.com/p/roboguice/",[1006],"RoboGuice"," makes DI available for your Android\nclasses. We will walk you through a small example, showing how it simplifies your ",[471,82073,82037],{}," method by comparing it\nbefore and after introduction of RoboGuice into your code. Afterwards we will give a short scoop on how to drop it into\nyour Android Eclipse project and start working with it right away.",[3938,82076,82078],{"id":82077},"before-and-after","Before and after",[439,82080,82081],{},"We start right away with a small code snippet from an Android activity:",[464,82083,82085],{"className":709,"code":82084,"language":711,"meta":469,"style":469},"public class SpiderActivity extends Activity {\n // [..snip..] lots of other members not needed for the example\n private RelativeLayout rating;\n private ImageView ratingStars;\n private Button infoButton;\n private TextView type;\n private TextView language;\n private TextView spider;\n private TextView translation;\n private TextView author;\n private TextView page;\n private RatingBar ratingBar;\n private Handler handler;\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.main);\n infoButton = (Button) findViewById(R.id.InfoButton);\n infoButton.setOnClickListener(new InfosOnClickListener());\n rating = (RelativeLayout) findViewById(R.id.ratingDialog);\n ratingStars = (ImageView) findViewById(R.id.ratingStars);\n ratingStars.setOnClickListener(new MenuOnClickListener());\n type = (TextView) findViewById(R.id.TypeText);\n language = (TextView) findViewById(R.id.LanguageText);\n spider = (TextView) findViewById(R.id.SpiderText);\n translation = (TextView) findViewById(R.id.TranslationText);\n author = (TextView) findViewById(R.id.AuthorText);\n page = (TextView) findViewById(R.id.PageText);\n ratingBar = (RatingBar) findViewById(R.id.ratingBar);\n if (hasUnusualDeviceDimensions()) {\n adjustLayout();\n }\n if (!isLayoutSupported()) {\n Toast.makeText(this.getApplicationContext(),\n \"Sorry, this phone resolution is not supported.\",\n Toast.LENGTH_LONG).show();\n }\n }\n",[471,82086,82087,82092,82097,82102,82107,82112,82117,82122,82127,82132,82137,82142,82147,82152,82156,82160,82165,82170,82175,82180,82185,82190,82195,82200,82205,82210,82215,82220,82225,82230,82235,82239,82244,82249,82254,82259,82263],{"__ignoreMap":469},[474,82088,82089],{"class":476,"line":477},[474,82090,82091],{},"public class SpiderActivity extends Activity {\n",[474,82093,82094],{"class":476,"line":507},[474,82095,82096],{}," // [..snip..] lots of other members not needed for the example\n",[474,82098,82099],{"class":476,"line":547},[474,82100,82101],{}," private RelativeLayout rating;\n",[474,82103,82104],{"class":476,"line":584},[474,82105,82106],{}," private ImageView ratingStars;\n",[474,82108,82109],{"class":476,"line":607},[474,82110,82111],{}," private Button infoButton;\n",[474,82113,82114],{"class":476,"line":642},[474,82115,82116],{}," private TextView type;\n",[474,82118,82119],{"class":476,"line":663},[474,82120,82121],{}," private TextView language;\n",[474,82123,82124],{"class":476,"line":694},[474,82125,82126],{}," private TextView spider;\n",[474,82128,82129],{"class":476,"line":700},[474,82130,82131],{}," private TextView translation;\n",[474,82133,82134],{"class":476,"line":913},[474,82135,82136],{}," private TextView author;\n",[474,82138,82139],{"class":476,"line":920},[474,82140,82141],{}," private TextView page;\n",[474,82143,82144],{"class":476,"line":926},[474,82145,82146],{}," private RatingBar ratingBar;\n",[474,82148,82149],{"class":476,"line":932},[474,82150,82151],{}," private Handler handler;\n",[474,82153,82154],{"class":476,"line":938},[474,82155,28602],{},[474,82157,82158],{"class":476,"line":944},[474,82159,28607],{},[474,82161,82162],{"class":476,"line":950},[474,82163,82164],{}," setContentView(R.layout.main);\n",[474,82166,82167],{"class":476,"line":956},[474,82168,82169],{}," infoButton = (Button) findViewById(R.id.InfoButton);\n",[474,82171,82172],{"class":476,"line":962},[474,82173,82174],{}," infoButton.setOnClickListener(new InfosOnClickListener());\n",[474,82176,82177],{"class":476,"line":4876},[474,82178,82179],{}," rating = (RelativeLayout) findViewById(R.id.ratingDialog);\n",[474,82181,82182],{"class":476,"line":4888},[474,82183,82184],{}," ratingStars = (ImageView) findViewById(R.id.ratingStars);\n",[474,82186,82187],{"class":476,"line":4900},[474,82188,82189],{}," ratingStars.setOnClickListener(new MenuOnClickListener());\n",[474,82191,82192],{"class":476,"line":4913},[474,82193,82194],{}," type = (TextView) findViewById(R.id.TypeText);\n",[474,82196,82197],{"class":476,"line":4921},[474,82198,82199],{}," language = (TextView) findViewById(R.id.LanguageText);\n",[474,82201,82202],{"class":476,"line":4932},[474,82203,82204],{}," spider = (TextView) findViewById(R.id.SpiderText);\n",[474,82206,82207],{"class":476,"line":4938},[474,82208,82209],{}," translation = (TextView) findViewById(R.id.TranslationText);\n",[474,82211,82212],{"class":476,"line":4946},[474,82213,82214],{}," author = (TextView) findViewById(R.id.AuthorText);\n",[474,82216,82217],{"class":476,"line":4952},[474,82218,82219],{}," page = (TextView) findViewById(R.id.PageText);\n",[474,82221,82222],{"class":476,"line":4957},[474,82223,82224],{}," ratingBar = (RatingBar) findViewById(R.id.ratingBar);\n",[474,82226,82227],{"class":476,"line":4969},[474,82228,82229],{}," if (hasUnusualDeviceDimensions()) {\n",[474,82231,82232],{"class":476,"line":4990},[474,82233,82234],{}," adjustLayout();\n",[474,82236,82237],{"class":476,"line":5001},[474,82238,5704],{},[474,82240,82241],{"class":476,"line":5013},[474,82242,82243],{}," if (!isLayoutSupported()) {\n",[474,82245,82246],{"class":476,"line":5024},[474,82247,82248],{}," Toast.makeText(this.getApplicationContext(),\n",[474,82250,82251],{"class":476,"line":5035},[474,82252,82253],{}," \"Sorry, this phone resolution is not supported.\",\n",[474,82255,82256],{"class":476,"line":5047},[474,82257,82258],{}," Toast.LENGTH_LONG).show();\n",[474,82260,82261],{"class":476,"line":5055},[474,82262,5704],{},[474,82264,82265],{"class":476,"line":5062},[474,82266,1276],{},[439,82268,82269,82270,82272],{},"It’s easy to see, that the code that is actually doing something useful — check for unusual device dimensions and adjust\nlayout parameters — is only a small part of the ",[471,82271,82037],{}," method. Let’s see how this would look like with RoboGuice\napplied to your project:",[464,82274,82276],{"className":709,"code":82275,"language":711,"meta":469,"style":469},"// these are the needed classes from the RoboGuice framework\nimport roboguice.activity.RoboActivity;\nimport roboguice.inject.InjectView;\n// [..snip..]\npublic class SpiderActivity extends RoboActivity {\n @InjectView(R.id.ratingDialog ) private RelativeLayout rating;\n @InjectView(R.id.ratingStars) private ImageView ratingStars;\n @InjectView(R.id.InfoButton) private Button infoButton;\n @InjectView(R.id.TypeText) private TextView type;\n @InjectView(R.id.LanguageText) private TextView language;\n @InjectView(R.id.SpiderText) private TextView spider;\n @InjectView(R.id.TranslationText) private TextView translation;\n @InjectView(R.id.AuthorText) private TextView author;\n @InjectView(R.id.PageText) private TextView page;\n @InjectView(R.id.ratingBar) private RatingBar ratingBar;\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n // the following call actually injects your views ...\n setContentView(R.layout.main);\n // they are available afterwards\n infoButton.setOnClickListener(new InfosOnClickListener());\n ratingStars.setOnClickListener(new MenuOnClickListener());\n if (hasUnusualDeviceDimensions()) {\n adjustLayout();\n }\n if (!isLayoutSupported()) {\n Toast.makeText(this.getApplicationContext(),\n \"Sorry, this phone resolution is not supported.\",\n Toast.LENGTH_LONG).show();\n }\n }\n",[471,82277,82278,82283,82288,82293,82298,82303,82308,82313,82318,82323,82328,82333,82338,82343,82348,82353,82357,82361,82366,82370,82375,82379,82383,82387,82391,82395,82399,82403,82407,82411,82415],{"__ignoreMap":469},[474,82279,82280],{"class":476,"line":477},[474,82281,82282],{},"// these are the needed classes from the RoboGuice framework\n",[474,82284,82285],{"class":476,"line":507},[474,82286,82287],{},"import roboguice.activity.RoboActivity;\n",[474,82289,82290],{"class":476,"line":547},[474,82291,82292],{},"import roboguice.inject.InjectView;\n",[474,82294,82295],{"class":476,"line":584},[474,82296,82297],{},"// [..snip..]\n",[474,82299,82300],{"class":476,"line":607},[474,82301,82302],{},"public class SpiderActivity extends RoboActivity {\n",[474,82304,82305],{"class":476,"line":642},[474,82306,82307],{}," @InjectView(R.id.ratingDialog ) private RelativeLayout rating;\n",[474,82309,82310],{"class":476,"line":663},[474,82311,82312],{}," @InjectView(R.id.ratingStars) private ImageView ratingStars;\n",[474,82314,82315],{"class":476,"line":694},[474,82316,82317],{}," @InjectView(R.id.InfoButton) private Button infoButton;\n",[474,82319,82320],{"class":476,"line":700},[474,82321,82322],{}," @InjectView(R.id.TypeText) private TextView type;\n",[474,82324,82325],{"class":476,"line":913},[474,82326,82327],{}," @InjectView(R.id.LanguageText) private TextView language;\n",[474,82329,82330],{"class":476,"line":920},[474,82331,82332],{}," @InjectView(R.id.SpiderText) private TextView spider;\n",[474,82334,82335],{"class":476,"line":926},[474,82336,82337],{}," @InjectView(R.id.TranslationText) private TextView translation;\n",[474,82339,82340],{"class":476,"line":932},[474,82341,82342],{}," @InjectView(R.id.AuthorText) private TextView author;\n",[474,82344,82345],{"class":476,"line":938},[474,82346,82347],{}," @InjectView(R.id.PageText) private TextView page;\n",[474,82349,82350],{"class":476,"line":944},[474,82351,82352],{}," @InjectView(R.id.ratingBar) private RatingBar ratingBar;\n",[474,82354,82355],{"class":476,"line":950},[474,82356,28602],{},[474,82358,82359],{"class":476,"line":956},[474,82360,28607],{},[474,82362,82363],{"class":476,"line":962},[474,82364,82365],{}," // the following call actually injects your views ...\n",[474,82367,82368],{"class":476,"line":4876},[474,82369,82164],{},[474,82371,82372],{"class":476,"line":4888},[474,82373,82374],{}," // they are available afterwards\n",[474,82376,82377],{"class":476,"line":4900},[474,82378,82174],{},[474,82380,82381],{"class":476,"line":4913},[474,82382,82189],{},[474,82384,82385],{"class":476,"line":4921},[474,82386,82229],{},[474,82388,82389],{"class":476,"line":4932},[474,82390,82234],{},[474,82392,82393],{"class":476,"line":4938},[474,82394,5704],{},[474,82396,82397],{"class":476,"line":4946},[474,82398,82243],{},[474,82400,82401],{"class":476,"line":4952},[474,82402,82248],{},[474,82404,82405],{"class":476,"line":4957},[474,82406,82253],{},[474,82408,82409],{"class":476,"line":4969},[474,82410,82258],{},[474,82412,82413],{"class":476,"line":4990},[474,82414,5704],{},[474,82416,82417],{"class":476,"line":5001},[474,82418,1276],{},[439,82420,82421,82422,82424],{},"See how the ",[471,82423,82037],{}," method now communicates more clearly what it actually does? At the same time we made the\nassociation between the member variables for the views and their counterpart in the declarative layout more visible, by\nputting this information right where the member is declared. This makes the code instantly more accessible.",[3938,82426,82428],{"id":82427},"setting-up-roboguice","Setting up RoboGuice",[439,82430,82431,82432,82437,82438,82442,82443,82446,82447,82450,82451,82454,82455,82458,82459,82462,82463,82466],{},"Now to get you up and running with RoboGuice, we will give quick instructions on how to obtain the necessary\ndependencies and how to configure your Android Eclipse project. First we need the JAR files:\ndownload ",[1002,82433,82436],{"href":82434,"rel":82435},"https://web.archive.org/web/20160713065637/http://google-guice.googlecode.com:80/files/guice-2.0-no_aop.jar",[1006],"Guice 2.0"," (\nwhich is the base for RoboGuice)\nand ",[1002,82439,82070],{"href":82440,"rel":82441},"http://download.java.net/maven/2/roboguice/roboguice/1.1-SNAPSHOT/roboguice-1.1-20100408.222944-3.jar",[1006]," (\nin our example we used the development version 1.1) into a ",[471,82444,82445],{},"lib"," subdirectory of your project. Add both the libraries\nto your build path by selecting ",[448,82448,82449],{},"Build Path > Add to Build Path"," from the context menu of each of the libraries.\nFinally a small change to your ",[471,82452,82453],{},"AndroidManifest.xml"," will be needed, to make RoboGuice work. In the ",[448,82456,82457],{},"Application"," tab\nset the ",[448,82460,82461],{},"Name"," attribute to ",[471,82464,82465],{},"roboguice.application.RoboApplication"," (or make your own Application class inherit from\nit).",[3938,82468,82470],{"id":82469},"finishing-words","Finishing words",[439,82472,82473,82474,82478,82479,82482],{},"While this is only an appetizer for using Dependency Injection in your Android application and making your code more\nexpressive, there are still more resources to be found on the corresponding project pages:\nthe ",[1002,82475,82477],{"href":82062,"rel":82476},[1006],"Guice"," pages introduce the general idioms of the framework, while\nthe ",[1002,82480,82070],{"href":82068,"rel":82481},[1006]," pages contain some more examples and the documentation.",[439,82484,82485],{},"I hope this short introduction proves useful and helps you making your applications code more readable and testable in\nthe end.",[1024,82487,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":82489},[82490,82491,82492,82493],{"id":82045,"depth":507,"text":82046},{"id":82077,"depth":507,"text":82078},{"id":82427,"depth":507,"text":82428},{"id":82469,"depth":507,"text":82470},[11122,1413],"2010-09-17T18:06:36","When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered.\\nSetup code that simply retrieves the views from the declarative layout (by using findViewById(int id)) quickly fills\\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\\non it’s actual task again!","https://synyx.de/blog/android-roboguice-against-oncreate-boilerplate/",{},"/blog/android-roboguice-against-oncreate-boilerplate",{"title":82025,"description":82501},"When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered.\nSetup code that simply retrieves the views from the declarative layout (by using findViewById(int id)) quickly fills\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\non it’s actual task again!","blog/android-roboguice-against-oncreate-boilerplate",[11132,82504,82505,1413],"guice","roboguice","When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered. Setup code that simply retrieves the views from the declarative layout (by using…","WWGtuzKM-rsAWuMzV5nJuXOh5e4_kiJ7iFMPe0sPkMY",{"id":82509,"title":82510,"author":82511,"body":82512,"category":82524,"date":82525,"description":469,"extension":1034,"link":82526,"meta":82527,"navigation":916,"path":82528,"seo":82529,"slug":82516,"stem":82530,"tags":82531,"teaser":82534,"__hash__":82535},"blog/blog/apn-device-tokens.md","APN Device Tokens",[223],{"type":432,"value":82513,"toc":82522},[82514,82517],[435,82515,82510],{"id":82516},"apn-device-tokens",[439,82518,82519],{},[2205,82520],{"alt":78385,"src":82521},"https://media.synyx.de/uploads//2010/07/512px.png",{"title":469,"searchDepth":507,"depth":507,"links":82523},[],[11122,3799,1413],"2010-09-14T06:46:26","https://synyx.de/blog/apn-device-tokens/",{},"/blog/apn-device-tokens",{"title":82510,"description":469},"blog/apn-device-tokens",[82532,63945,78972,78398,82533],"apn","push-notifications","When you enable Apple Push Notifications (APN) for your App, your device generates a unique device token and pass it to the didRegisterForRemoteNotificationsWithDeviceToken method in your App delegate. Usually, you’ll…","aMPN6FYFi3_lDwUhQjGwaJBpcUeq7eDCk1IVolOP3Vg",{"id":82537,"title":82538,"author":82539,"body":82540,"category":82551,"date":82552,"description":469,"extension":1034,"link":82553,"meta":82554,"navigation":916,"path":82555,"seo":82556,"slug":82557,"stem":82558,"tags":82559,"teaser":82561,"__hash__":82562},"blog/blog/i-think-i-spider-1-0-released.md","I think I spider 1.0 released",[223],{"type":432,"value":82541,"toc":82549},[82542,82545],[435,82543,82538],{"id":82544},"i-think-i-spider-10-released",[439,82546,82547],{},[2205,82548],{"alt":78385,"src":78386},{"title":469,"searchDepth":507,"depth":507,"links":82550},[],[11122,3799],"2010-09-11T17:54:14","https://synyx.de/blog/i-think-i-spider-1-0-released/",{},"/blog/i-think-i-spider-1-0-released",{"title":82538,"description":469},"i-think-i-spider-1-0-released","blog/i-think-i-spider-1-0-released",[11132,63945,78398,82560,22737],"ithinkispider","Today we are proud to present our own very first App (it’s actually our second Android App) that made it to the App Store and Android Market – I think…","ihp4cPfv4tMEA3ebuCKmOKtft0Yrr9slF_Lsd0LB1fw",{"id":82564,"title":82565,"author":82566,"body":82567,"category":82636,"date":82637,"description":82638,"extension":1034,"link":82639,"meta":82640,"navigation":916,"path":82641,"seo":82642,"slug":82644,"stem":82645,"tags":82646,"teaser":82647,"__hash__":82648},"blog/blog/mobile-solutions-summary-5.md","Mobile Solutions – Summary",[223],{"type":432,"value":82568,"toc":82634},[82569,82572,82586,82594,82599,82608],[435,82570,82565],{"id":82571},"mobile-solutions-summary",[439,82573,82574,82575,82580,82581,1402],{},"It has been a while since my ",[1002,82576,82579],{"href":82577,"rel":82578},"http://blog.synyx.de/2010/07/mobile-solutions-summary-2/",[1006],"last update"," on our efforts over\nat the ",[1002,82582,82585],{"href":82583,"rel":82584},"http://mobile.synyx.de/",[1006],"mobile solutions blog",[439,82587,82588,82589,6562],{},"The most important announcement was without any doubt the imminent release of our new side\nproject: ",[1002,82590,82593],{"href":82591,"rel":82592},"http://mobile.synyx.de/2010/07/i-think-i-spider/",[1006],"“I think I spider”",[11947,82595,82596],{},[439,82597,82598],{},"The “I think I spider”App idea was born, when we discovered the corresponding web site. We had so much fun with it,\nthat we decided to bring all the joy to the major mobile platforms.",[439,82600,82601,82602,82607],{},"Although, we haven’t revealed any specific features yet, reading\nour ",[1002,82603,82606],{"href":82604,"rel":82605},"http://mobile.synyx.de/category/our-apps/",[1006],"vast number of blog posts"," on “I think I spider”, you’ve probably got an\nidea of what the App is gonna bring to you.",[439,82609,82610,82611,82616,82617,82622,82623,82627,82628,82633],{},"A very interesting topic\nis ",[1002,82612,82615],{"href":82613,"rel":82614},"http://dlinsin.blogspot.com/2010/03/is-future-of-mobile-apps-web.html",[1006],"cross-device mobile development",", which\nmight be where the future lies, especially with Apple’s\nrevised ",[1002,82618,82621],{"href":82619,"rel":82620},"http://daringfireball.net/2010/09/app_store_guidelines",[1006],"iOS Developer Program License Agreements",". Our very\nown ",[1002,82624,80682],{"href":82625,"rel":82626},"http://mobile.synyx.de/authors/?uid=9",[1006]," wrote\nan ",[1002,82629,82632],{"href":82630,"rel":82631},"http://mobile.synyx.de/2010/09/on-cross-device-mobile-development-part-2/",[1006],"extensive two-part piece"," on the state\nof cross-device mobile development, which is definitely worth a read.",{"title":469,"searchDepth":507,"depth":507,"links":82635},[],[1030],"2010-09-10T08:44:39","It has been a while since my last update on our efforts over\\nat the mobile solutions blog.","https://synyx.de/blog/mobile-solutions-summary-5/",{},"/blog/mobile-solutions-summary-5",{"title":82565,"description":82643},"It has been a while since my last update on our efforts over\nat the mobile solutions blog.","mobile-solutions-summary-5","blog/mobile-solutions-summary-5",[11132,63945,78398,18496],"It has been a while since my last update on our efforts over at the mobile solutions blog. The most important announcement was without any doubt the imminent release of…","JLhxo_R5oDX4p05l4611cjUW8lEQUM0SDnOGCfiC8yw",{"id":82650,"title":82651,"author":82652,"body":82653,"category":82745,"date":82746,"description":469,"extension":1034,"link":82747,"meta":82748,"navigation":916,"path":82749,"seo":82750,"slug":82751,"stem":82752,"tags":82753,"teaser":82756,"__hash__":82757},"blog/blog/resolutions-on-android.md","Android resolution and layout problems",[190],{"type":432,"value":82654,"toc":82739},[82655,82658,82662,82666,82669,82672,82675,82678,82681,82685,82688,82691,82697,82700,82704,82707,82710,82713,82716,82719,82722,82725,82728,82731,82733,82736],[435,82656,82651],{"id":82657},"android-resolution-and-layout-problems",[439,82659,82660],{},[2205,82661],{"alt":78385,"src":82521},[3938,82663,82665],{"id":82664},"dp-or-not-dp","dp or not dp?",[439,82667,82668],{},"It was some work, but after a little time, the layout fitted for each density – well, at least so it seemed…",[439,82670,82671],{},"It fitted only for the three default dpi values (120, 160 and 240).",[439,82673,82674],{},"If you used a larger / smaller screen with the same density, the positions didn’t match exactly any more.",[439,82676,82677],{},"The problem was, that we used (as we are also said to always use) dp to set the views. It’s true that its nice to do\nthis, if you use the standard widgets and if you don’t have such a highly customized layout, but in our case it seemed\nlike the wrong decision to use dp.",[439,82679,82680],{},"The solution to this was to use pixel values instead and to create the layouts for the different resolutions and not the\ndensities. The new values were easy to calculate, because you only needed to take the values of the 480x320px\nresolution (exactly the same values as in dp) and multiply them by 0.75 for the 320x240px resolution and by 1.5 for\nthe 854×480 one (and adjust the height a little here…).",[3938,82682,82684],{"id":82683},"changing-the-layout-in-your-code","Changing the layout in your code",[439,82686,82687],{},"The next problem was (even before we converted the values in px) to display the layout on the 800×480 and 854×480\nresolutions, because you can’t declare layouts for both of them – they always take the same one.",[439,82689,82690],{},"We also didn’t want to have a black border for the bigger resolution, scaling the background to a bigger length was also\nno problem, so we decided to adjust the layout for this particular case in our code:",[464,82692,82695],{"className":82693,"code":82694,"language":14509},[14508],"\n//called in onCreate()\nif (getWindowManager().getDefaultDisplay().getHeight() == 800) {\n AbsoluteLayout.LayoutParams params;\n params = (LayoutParams) findViewById(R.id.someView).getLayoutParams();\n params.y = params.y - 7;\n// more adjusting here...\n}\n\n",[471,82696,82694],{"__ignoreMap":469},[439,82698,82699],{},"Well, maybe it isn’t a good solution, but we didn’t find any other possibility here.",[3938,82701,82703],{"id":82702},"using-resource-qualifiers-for-the-different-resolutions","Using resource qualifiers for the different resolutions",[439,82705,82706],{},"A downside of this approach is that you have to add adjustments to every resolution that is available now and that comes\nout in the future, like the bigger ones for tablets.",[439,82708,82709],{},"Right now, you can support most of the resolutions by simply declaring different layouts by using different folders (\nexcept for 800×480 and 854×480), but I’m not sure how it is going to be with new resolutions.",[439,82711,82712],{},"The layouts we declare are seperated in the following folders:",[439,82714,82715],{},"layout-normal-mdpi -> 320×480",[439,82717,82718],{},"layout-normal-hdpi -> 800×480 and 854×480 (adjusted in the code)",[439,82720,82721],{},"layout-normal-ldpi -> 400×240",[439,82723,82724],{},"layout-large-mdpi -> 800×480 tablet (mostly the same as the layout-normal-hdpi layout, but needs to be in this\nfolder)",[439,82726,82727],{},"layout-small-ldpi -> 320×240",[439,82729,82730],{},"Also one -landscape folder for each of them for the landscape layout for the widget (the rest of the app is portrait\nonly)",[3938,82732,9392],{"id":9391},[439,82734,82735],{},"If someone downloads the App with an unsupported resolution, it will not be displayed correctly. For such cases it could\nbe helpful if you could declare the resolutions that your app supports, or at least the aspect ratios and to have\nresource directory qualifiers for this, so that you don’t have to adjust your layout in the code.",[439,82737,82738],{},"My conclusion of this is that the wide variety of different resolutions, densities and especially aspect ratios makes it\nreally hard to create a good looking app that supports all of them. Google should really have put more restrictions to\nthese terms to make it easier for the developers so that they can provide better quality apps for the users.",{"title":469,"searchDepth":507,"depth":507,"links":82740},[82741,82742,82743,82744],{"id":82664,"depth":507,"text":82665},{"id":82683,"depth":507,"text":82684},{"id":82702,"depth":507,"text":82703},{"id":9391,"depth":507,"text":9392},[11122,3799,1413],"2010-09-08T13:28:24","https://synyx.de/blog/resolutions-on-android/",{},"/blog/resolutions-on-android",{"title":82651,"description":469},"resolutions-on-android","blog/resolutions-on-android",[11132,82754,82560,60933,82755],"i-think-i-spider","resolution","During the process of developing I think I spider we discovered various problems regarding Android’s different resolutions. Here you can see which problems we encountered and how we solved them:…","zE1O2G5yrUPOJAy03zZlQOFstFCOfkCJc5_scwvunN8",{"id":82759,"title":82760,"author":82761,"body":82762,"category":82812,"date":82813,"description":82814,"extension":1034,"link":82815,"meta":82816,"navigation":916,"path":82817,"seo":82818,"slug":82819,"stem":82820,"tags":82821,"teaser":82823,"__hash__":82824},"blog/blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse.md","Wer zu spät kommt, den bestraft die Scrum-Fail-Tasse.",[172],{"type":432,"value":82763,"toc":82810},[82764,82767,82778,82786,82795,82807],[435,82765,82760],{"id":82766},"wer-zu-spät-kommt-den-bestraft-die-scrum-fail-tasse",[439,82768,82769,82777],{},[1002,82770,82773],{"href":82771,"rel":82772},"https://media.synyx.de/uploads//2010/09/scrum_02.jpg",[1006],[2205,82774],{"alt":9557,"src":82775,"title":82776},"https://media.synyx.de/uploads//2010/09/scrum_02-223x300.jpg","scrum_02","\nNiemand ist perfekt, das ist allgemein bekannt. Manchmal muss man auch zu weniger makellosen Eigenschaften stehen und\nversuchen das Beste daraus zu machen. So versuchen die Mitarbeiter von Synyx in erster Linie die Untugend des\nZuspätkommens erst gar nicht aufkommen zu lassen. Sollte es aber doch mal nicht funktionieren, dann in etwas Sinnvolles\nund Positives umzuwandeln.",[439,82779,82780,82781,82785],{},"Für die Daily ",[1002,82782,82784],{"href":13095,"rel":82783},[1006],"Scrums"," ist es wichtig, dass sie pünktlich\nanfangen, sprich, dass alle Beteiligten ohne Verspätung zum Meeting kommen. Für den ein oder anderen ist das nicht immer\nso leicht, auch wenn es sich in diesem Fall oftmals nur um eine Minute handelt. Die Gründe dafür sind vielfältig,\nspielen aber hier keine Rolle. Denn Ziel ist es, Verspätungen zu vermeiden. Folglich wurde die “Scrum-Fail-Kasse”\neingeführt. Wer also zu spät zum Daily Scrum erscheint, hat einen gewissen Betrag in eben diese Kasse zu zahlen. Im\nLaufe der Zeit kann sich da durchaus etwas ansammeln.",[439,82787,82788,82789,82794],{},"Und was ist daran jetzt sinnvoll? Ganz einfach: ",[1002,82790,82793],{"href":82791,"rel":82792},"http://www.kiva.org/",[1006],"Kiva",", eine wohltätige Organisation (NGO), bietet\ndie Möglichkeit, Mikrokredite direkt an einen selbst ausgesuchten Kreditnehmer in einem Entwicklungsland zu vergeben.\nMenschen auf der ganzen Welt können so mit kleinen Beträgen – schon ab 25 US-Dollar – anderen Menschen in Armut helfen,\neine Existenzgrundlage aufzubauen.",[439,82796,82797,82800,82801,82806],{},[1002,82798,28],{"href":71514,"rel":82799},[1006],", Scrum Master bei Synyx, hat sich in Absprache mit den Teams für\ndiesen Weg entschieden, um den Betrag der Scrum Fail Kasse sinnvoll einzusetzen. Inzwischen konnten bereits einige\nProjekte unterstützt werden, wie auf der ",[1002,82802,82805],{"href":82803,"rel":82804},"http://www.kiva.org/team/synyx",[1006],"Kiva Lending Team Seite"," von Synyx nachzulesen\nist.",[439,82808,82809],{},"Auch wenn sich der Scrum Master stets über Pünktlichkeit freut, kann er mit einem zwinkernden Auge, das Positive daran\nsehen 😉",{"title":469,"searchDepth":507,"depth":507,"links":82811},[],[1031],"2010-09-07T15:38:04","\\nNiemand ist perfekt, das ist allgemein bekannt. Manchmal muss man auch zu weniger makellosen Eigenschaften stehen und\\nversuchen das Beste daraus zu machen. So versuchen die Mitarbeiter von Synyx in erster Linie die Untugend des\\nZuspätkommens erst gar nicht aufkommen zu lassen. Sollte es aber doch mal nicht funktionieren, dann in etwas Sinnvolles\\nund Positives umzuwandeln.","https://synyx.de/blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse/",{},"/blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse",{"title":82760,"description":82777},"wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse","blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse",[82822,9557],"kiva","Niemand ist perfekt, das ist allgemein bekannt. Manchmal muss man auch zu weniger makellosen Eigenschaften stehen und versuchen das Beste daraus zu machen. So versuchen die Mitarbeiter von Synyx in…","3TqpC2e5_DI7u8FSV0lar3HJL-hqcXsf8Q5NPGdMuLA",{"id":82826,"title":82827,"author":82828,"body":82829,"category":83091,"date":83092,"description":83093,"extension":1034,"link":83094,"meta":83095,"navigation":916,"path":83096,"seo":83097,"slug":82833,"stem":83099,"tags":83100,"teaser":83101,"__hash__":83102},"blog/blog/on-cross-device-mobile-development-part-2.md","On cross-device mobile development – Part 2",[205],{"type":432,"value":82830,"toc":83084},[82831,82834,82846,82850,82873,82877,82908,82912,82936,82940,82950,82954,82957,82961,82965,82989,82993,83012,83016,83031,83035,83066,83070,83081],[435,82832,82827],{"id":82833},"on-cross-device-mobile-development-part-2",[439,82835,37673,82836,82841,82842,82845],{},[1002,82837,82840],{"href":82838,"rel":82839},"http://mobile.synyx.de/2010/08/on-cross-device-mobile-development-part-1/",[1006],"previous part"," of this series we took\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as ",[990,82843,82844],{},"the web\nstack","). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\nfor, shouldn’t it? Not quite …",[3938,82847,82849],{"id":82848},"what-does-native-have-what-web-stack-hasnt","What does native have, what web stack hasn’t?",[439,82851,82852,82853,82856,82857,82860,82861,82864,82865,82868,82869,82872],{},"If you want the answer to this question boiled down to one simple word: ",[448,82854,82855],{},"abstraction",". In an application you don’t\nwant to think in terms of ",[471,82858,82859],{},"\u003Cul>"," elements, to which you append ",[471,82862,82863],{},"\u003Cli>"," elements, styled adequately with CSS to harbor\nitems populated from SQLite statements and react to code which is painstakingly attached to every list item to react to\ndifferent user inputs. You would usually think about the problem in terms of — for example — a ",[471,82866,82867],{},"ListController"," and an\nattached ",[471,82870,82871],{},"DataSource",", freeing you from the task of layouting your list. And you don’t want to have to code all this\nboilerplate from scratch. It is not the problem you want to solve. It is something left for a framework.",[3938,82874,82876],{"id":82875},"competing-with-native-sdks","Competing with native SDKs",[439,82878,82879,82880,82884,82885,82890,82891,82893,82894,82899,82900,82903,82904,82907],{},"Even before the days of mobile applications, web application developers pushed hard to create frameworks to match up\nagainst their competitors from the native world. An increasing number of those are now also reconsidering deployment on\nsmall mobile devices and are joined by new JavaScript frameworks especially tailored to mobile\ndevices. ",[1002,82881,82883],{"href":76442,"rel":82882},[1006],"PhoneGap"," — which we refered to already in\nthe first part of the series — is\nnow ",[1002,82886,82889],{"href":82887,"rel":82888},"https://web.archive.org/web/20140122061413/http://phonegap.com/2010/07/19/it%e2%80%99s-easier-than-ever-for-symbian-developers-to-build-mobile-apps-with-phonegap/",[1006],"fully embraced","\nby Nokia for their Symbian smartphone operating system. Palm has his whole mobile user experience built around ",[990,82892,82844],{},", accordingly naming their operating system ",[1002,82895,82898],{"href":82896,"rel":82897},"http://developer.palm.com/",[1006],"WebOS",". The need for mobile ",[990,82901,82902],{},"web stack","\nframeworks is growing. The rest of this article will introduce some of those frameworks, but first a short recap of what\n",[448,82905,82906],{},"abstractions"," we would expect from such a framework, compared to their native siblings:",[1633,82909,82911],{"id":82910},"models","Models",[439,82913,82914,82915,82920,82921,82928,82929,1402],{},"The way the data is retrieved and stored on the device comprises our data model. Because of constraints in memory\nfootprint and also because of security considerations, mobile SDKs should try to provide abstraction layers for your\ndata storage. As an example, the iPhone SDK incorporates a subsystem\ncalled ",[1002,82916,82919],{"href":82917,"rel":82918},"http://developer.apple.com/iphone/library/documentation/DataManagement/Conceptual/iPhoneCoreData01/",[1006],"CoreData",".\nIt provides modelling tools and stub generation to easily integrate with ",[1002,82922,82925],{"href":82923,"rel":82924},"http://developer.apple.com/iphone/library/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html",[1006],[471,82926,82927],{},"UITableView","\ncontrols. Android, while staying more on the lower SQLite level, also provides similar integration to their native ",[1002,82930,82933],{"href":82931,"rel":82932},"http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html",[1006],[471,82934,82935],{},"ListView",[1633,82937,82939],{"id":82938},"views-user-interface-ui-elements","Views & user interface (UI) elements",[439,82941,82942,82949],{},[1002,82943,82946],{"href":82944,"rel":82945},"https://media.synyx.de/uploads//2010/07/iPhone-UI-Controls-e1280510112988.png",[1006],[2205,82947],{"alt":469,"src":82944,"title":82948},"iPhone UI Controls","\nEvery mobile SDK contains a selection of useful UI elements: buttons, sliders, text entry controls, switches and on a\nlarger scale prebuilt view elements for tabular data, images, display of rich text or maps for geolocative services.\nUsually every of those elements or views exhibits a callback interface tailored towards the particular item. These can\nbecome quite sophisticated: views for tabular data call back for touches on a particular row and/or column or map views\nrequesting to redraw the viewport according to new coordinates after scrolling occured.",[1633,82951,82953],{"id":82952},"controllers","Controllers",[439,82955,82956],{},"The backbone of every applications, they are the heavy lifters which glue your models and views together. Often the\ntasks to be done — for example managing the display of overviews and increasingly detailled item views or also an\neditable table view of data from an underlying data source — are abstract and common enough, that a selection of generic\ncontrollers can eliminate boilerplate code and provide a feature rich set of actions out of the box.",[1065,82958,82960],{"id":82959},"javascript-frameworks-the-small-the-huge-and-the-familiar","JavaScript frameworks … the small, the huge and the familiar",[1633,82962,82964],{"id":82963},"jqtouch","jQTouch",[439,82966,82967,82968,82972,82973,82977,82978,82985,82986,82988],{},"Our first framework is perhaps the one which contradicts our demands the most. ",[1002,82969,82964],{"href":82970,"rel":82971},"http://jqtouch.com",[1006]," is a\nlightweight layer on the ever-present ",[1002,82974,48083],{"href":82975,"rel":82976},"http://jquery.com",[1006]," library. And with lightweight we are talking about a\nmeager 577 source lines of code. Obviously one cannot expect a lot of the desired abstractions, yet still jQTouch has\nits own eligibility. It provides mainly an implementation of one of the most common view abstractions — a stack of menus\nand toolbars to navigate forward and backward — bundled with finely tuned themes for an iPhone-like or more generic\nmobile UI. While you still have to provide the code for more complex controllers and also data model abstraction, it can\nbe used for those cases, where an already deployed web application employing jQuery, should be quickly ported to a\nmobile device. Furthermore it features resource-preloading as also access to geolocation APIs in mobile WebKit\nimplementations. The missing model and controller code can be adapted from existing desktop browser jQuery extensions,\nwhich are numerous. It is now maintained by Jonathan Stark, whose book ",[1002,82979,82982],{"href":82980,"rel":82981},"http://building-iphone-apps.labs.oreilly.com/",[1006],[990,82983,82984],{},"Building iPhone Apps with HTML, CSS &\nJavaScript"," we already mentioned in part one of this article series and\nwhich is still a great resource for ",[990,82987,82902],{}," development on mobile devices.",[1633,82990,82992],{"id":82991},"sencha-touch","Sencha Touch",[439,82994,82995,83000,83001,83005,83006,83011],{},[1002,82996,82999],{"href":82997,"rel":82998},"http://www.sencha.com/",[1006],"Sencha Labs"," (formerly ExtJS) are already well-known for their desktop browser frameworks.\nWith ",[1002,83002,82992],{"href":83003,"rel":83004},"http://www.sencha.com/products/touch/",[1006]," they provide abstractions for many of the use cases we talked\nabout above. This includes controllers for tool- or tabbar navigation, data sources which can be attached to analogous\nviews — which then are updated automatically, common entry controls like textfields or date pickers, map views and media\nlike audio and video. Every control provided has an extensive list of event callbacks, that you can easily attach your\nfunctions to. From the ",[1002,83007,83010],{"href":83008,"rel":83009},"http://www.sencha.com/products/touch/demos.php",[1006],"demos"," it becomes clear that they also push hard\ntowards the emerging tablet devices by providing a full-stack framework for even sophisticated applications. It should\nbe noted though, that it is dual-licensed, with commercial use entailing a (small) fee.",[1633,83013,83015],{"id":83014},"jquery-mobile","jQuery mobile",[439,83017,83018,83019,83024,83025,83030],{},"For people, who in the past have worked with ",[1002,83020,83023],{"href":83021,"rel":83022},"http://jqueryui.com",[1006],"jQuery UI"," for web applications, John Resig recently\nannounced ",[1002,83026,83029],{"href":83027,"rel":83028},"http://jquerymobile.com/",[1006],"jQuery Mobile",". While it is still in planning & internal beta, it could prove as\nthe missing link from the formerly mentioned jQTouch. Projecting from the existing jQuery UI framework, this would\nprovide a good amount of UI controls and common view abstractions, while maintaining the slick feeling of traditional\njQuery programming. It will also come with themes, which match typical native mobile controls and a strong mission\nstatement to make it a real cross-platform alternative, beyond the iOS platform usually targeted by other frameworks.",[1633,83032,83034],{"id":83033},"google-web-toolkit","Google Web Toolkit",[439,83036,83037,83038,83042,83043,83048,83049,22274,83054,83059,83060,83065],{},"Last but definitely not least, we have to include an old familiar\nfriend: ",[1002,83039,83034],{"href":83040,"rel":83041},"http://code.google.com/webtoolkit/",[1006]," (GWT) has already matured for desktop browsers. It also\nhas something to provide, which other JavaScript frameworks lack: extensive IDE support with a strictly typed language\nunderneath. Especially Android developers will feel more at home, when developing with Java. While originally targeted\nat desktop browsers, its extensibility makes it easy to retarget mobile\nplatforms. ",[1002,83044,83047],{"href":83045,"rel":83046},"http://code.google.com/p/gwt-mobile-webkit/",[1006],"GWT Mobile WebKit"," extends GWT with support for touch\ninterfaces and also bundles libraries to\nabstract ",[1002,83050,83053],{"href":83051,"rel":83052},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Geolocation",[1006],"geolocation services",[1002,83055,83058],{"href":83056,"rel":83057},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Database",[1006],"SQLite"," database — something even\nmissing from the original Android SDK. Views can be created programmatically\nor ",[1002,83061,83064],{"href":83062,"rel":83063},"http://code.google.com/webtoolkit/doc/latest/DevGuideUiBinder.html",[1006],"declaratively"," and provide a rich API for\ncallbacks on user input.",[3938,83067,83069],{"id":83068},"what-is-left-to-be-said","What is left to be said",[439,83071,22944,83072,83074,83075,21201,83077,83080],{},[990,83073,82902],{}," mobile development community is certainly on the move to sophisticated frameworks. From small\nmicroframeworks to full-stack frameworks like ",[990,83076,82992],{},[990,83078,83079],{},"GWT",", a range of tastes for different development\nstyles is served. While we tried to give a bigger perspective on what is available, we still haven’t even touched topics\nlike the upcoming HTML5 — which together with CSS3 will bring fast animations and sophisticated graphics — or actual\ncross-device behaviour. What can be definitely said is, that these developments — wether or not one decides to jump on\nthis bandwagon — increase the diversity of mobile development and provide new insights in how we think about mobile\napplication development.",[439,83082,83083],{},"To be continued some time …",{"title":469,"searchDepth":507,"depth":507,"links":83085},[83086,83087,83090],{"id":82848,"depth":507,"text":82849},{"id":82875,"depth":507,"text":82876,"children":83088},[83089],{"id":82959,"depth":547,"text":82960},{"id":83068,"depth":507,"text":83069},[11122],"2010-09-07T12:00:39","In the previous part of this series we took\\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web\\nstack). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\\nfor, shouldn’t it? Not quite …","https://synyx.de/blog/on-cross-device-mobile-development-part-2/",{},"/blog/on-cross-device-mobile-development-part-2",{"title":82827,"description":83098},"In the previous part of this series we took\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web\nstack). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\nfor, shouldn’t it? Not quite …","blog/on-cross-device-mobile-development-part-2",[11132,22162,15041,78398,15201],"In the previous part of this series we took a look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web stack). A…","f8wWfNKcafOIQayEDy1zO2QCR9mwXDvyrHD-gzeplCU",{"id":83104,"title":83105,"author":83106,"body":83107,"category":83199,"date":83200,"description":83201,"extension":1034,"link":83202,"meta":83203,"navigation":916,"path":83204,"seo":83205,"slug":83111,"stem":83207,"tags":83208,"teaser":83209,"__hash__":83210},"blog/blog/testing-apps-with-in-app-purchases-in-simulator.md","Testing Apps with In App Purchases in Simulator",[223],{"type":432,"value":83108,"toc":83197},[83109,83112,83126,83131,83140,83192,83195],[435,83110,83105],{"id":83111},"testing-apps-with-in-app-purchases-in-simulator",[439,83113,83114,83115,83120,83121,6562],{},"If you add a store to your app and\nuse ",[1002,83116,83119],{"href":83117,"rel":83118},"http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008267-CH1-SW1",[1006],"In App Purchases","\nto collect your payments, there are a couple of limitations your have to live with. One of those limitations\nis ",[1002,83122,83125],{"href":83123,"rel":83124},"http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/DevelopingwithStoreKit/DevelopingwithStoreKit.html#//apple_ref/doc/uid/TP40008267-CH103-SW1",[1006],"not being able to fully test your App in the iPhone Simulator",[11947,83127,83128],{},[439,83129,83130],{},"Store Kit does not operate in iPhone Simulator. When running your application in iPhone Simulator, Store Kit logs a\nwarning if your application attempts to retrieve the payment queue. Testing the store must be done on actual devices.",[439,83132,83133,83134,83139],{},"Although, there is not way to test Store Kit itself, you can still test the parts of your App that use and build on the\ninformation retrieved from Store Kit. You can use\na ",[1002,83135,83138],{"href":83136,"rel":83137},"http://en.wikipedia.org/wiki/C_preprocessor#Macro_definition_and_expansion",[1006],"preprocessor conditional inclusion"," to\ndetermine, whether you are running on the simulator and then “mock” the Store Kit calls or don’t execute them at all.",[464,83141,83145],{"className":83142,"code":83143,"language":83144,"meta":469,"style":469},"language-objc shiki shiki-themes github-light github-dark","#if TARGET_IPHONE_SIMULATOR\n// mock product description\n#else\nSKProductsRequest *productRequest =\n[[SKProductsRequest alloc] initWithProductIdentifiers:productIds];\nproductRequest.delegate = self;\n[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;\n[productRequest start];\n#endif\n","objc",[471,83146,83147,83152,83157,83162,83167,83172,83177,83182,83187],{"__ignoreMap":469},[474,83148,83149],{"class":476,"line":477},[474,83150,83151],{},"#if TARGET_IPHONE_SIMULATOR\n",[474,83153,83154],{"class":476,"line":507},[474,83155,83156],{},"// mock product description\n",[474,83158,83159],{"class":476,"line":547},[474,83160,83161],{},"#else\n",[474,83163,83164],{"class":476,"line":584},[474,83165,83166],{},"SKProductsRequest *productRequest =\n",[474,83168,83169],{"class":476,"line":607},[474,83170,83171],{},"[[SKProductsRequest alloc] initWithProductIdentifiers:productIds];\n",[474,83173,83174],{"class":476,"line":642},[474,83175,83176],{},"productRequest.delegate = self;\n",[474,83178,83179],{"class":476,"line":663},[474,83180,83181],{},"[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;\n",[474,83183,83184],{"class":476,"line":694},[474,83185,83186],{},"[productRequest start];\n",[474,83188,83189],{"class":476,"line":700},[474,83190,83191],{},"#endif\n",[439,83193,83194],{},"Keep in mind: this might not work for your App, however, it did work for our Apps and it’s better than not testing your\ncode at all. The optimal solution would definitely be to connect to the In App Purchase Sandbox environment from the\nSimulator.",[1024,83196,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":83198},[],[11122,1413],"2010-08-30T06:58:00","If you add a store to your app and\\nuse In App Purchases\\nto collect your payments, there are a couple of limitations your have to live with. One of those limitations\\nis not being able to fully test your App in the iPhone Simulator:","https://synyx.de/blog/testing-apps-with-in-app-purchases-in-simulator/",{},"/blog/testing-apps-with-in-app-purchases-in-simulator",{"title":83105,"description":83206},"If you add a store to your app and\nuse In App Purchases\nto collect your payments, there are a couple of limitations your have to live with. One of those limitations\nis not being able to fully test your App in the iPhone Simulator:","blog/testing-apps-with-in-app-purchases-in-simulator",[63945,78398,21404],"If you add a store to your app and use In App Purchases to collect your payments, there are a couple of limitations your have to live with. One of…","7mAd46W8XK_I2XpLyq6_9JiT3PH3jNVW-hswC_i86eU",{"id":83212,"title":83213,"author":83214,"body":83215,"category":83317,"date":83318,"description":83319,"extension":1034,"link":83320,"meta":83321,"navigation":916,"path":83322,"seo":83323,"slug":83219,"stem":83325,"tags":83326,"teaser":83328,"__hash__":83329},"blog/blog/froscon-2010.md","FrOSCon 2010",[148],{"type":432,"value":83216,"toc":83315},[83217,83220,83229,83232,83247,83266,83274,83289,83303,83306],[435,83218,83213],{"id":83219},"froscon-2010",[439,83221,83222,83223,83228],{},"Am 21. und 22.08. fand für mich die 2. ",[1002,83224,83227],{"href":83225,"rel":83226},"http://froscon.de/",[1006],"Free and Open Source Software Conference FrOSCon"," in St.\nAugustin bei Bonn statt. Der Hauptgrund für meinen Besuch im letzten Jahr war ein ausgedehnter Java-Track, ich war\njedoch von der Atmosphäre und der Vielfalt der Themen so begeistert, dass die Konferenz für mich sicher ein regelmäßiges\nEreignis sein wird.",[439,83230,83231],{},"Die Gelegenheit soll natürlich genutzt werden, um ein paar interessante Vorträge aus diesem Jahr vorzustellen, um dem\nein oder anderen vielleicht auch Lust auf einen Besuch zu machen.",[439,83233,83234,83235,83240,83241,83246],{},"Ein recht verbreitetes Thema, das unter anderem gerne im leider\neingestellten ",[1002,83236,83239],{"href":83237,"rel":83238},"http://blog.stackoverflow.com/category/podcasts/",[1006],"Stackoverflow-Podcast"," oder auch in diversen Büchern\nangesprochen wird, ist der Nutzen für die eigene Karriere, den man sich durch ein offenes Arbeiten in einer Community\nerarbeiten kann. ",[1002,83242,83245],{"href":83243,"rel":83244},"http://www.lornajane.net/",[1006],"Lorna Jane Mitchell"," beschrieb in “Open Source Your Career” recht\nunterhaltsam von Ihren Anfängen in der PHP-Community und den ersten Auftritten auf Konferenzen. Eine Anreiz für mehr\nEngagement.",[439,83248,83249,83250,83253,83254,83259,83260,83265],{},"Obwohl ich mit den Grundlagen schon vertraut war, waren zwei Vorträge zu ",[1002,83251,71000],{"href":70998,"rel":83252},[1006]," interessant:\nEiner über MongoDB im generellen von einem Mitarbeiter von ",[1002,83255,83258],{"href":83256,"rel":83257},"http://www.10gen.com/",[1006],"10gen",", der Firma hinter der\nDatenbank, und einen zur Integration in Ruby on Rails von ",[1002,83261,83264],{"href":83262,"rel":83263},"http://jan.krutisch.de/",[1006],"Jan Krutisch",". Auch wenn die\nhorizontale Skalierung für mich momentan noch keine große Rolle spielen wird, kann ich mir vorstellen, dass der\ndokumentenorientierte Ansatz auch beim Einsatz auf einer Maschine Vorteile bringen kann. Ich plane das an einem\nkonkreten Problem einmal auszuprobieren, einfach nur um zu wissen, wie sich die Entwicklung anfühlt.",[439,83267,83268,83269,83273],{},"“Mobile Linux Development” von Christian Küster ging mit einem für mich neuen Blickwinkel auf\nein ",[1002,83270,83272],{"href":77932,"rel":83271},[1006],"bekanntes Thema"," zu: Wie unterscheiden sich die unterschiedlichen Linux-basierten mobilen\nBetriebssysteme in Bezug auf die Zugriffsmöglichkeiten auf die Kernkomponenten, welche erlauben beispielsweise ein\nAustauschen des Kernels oder den Zugriff auf Systemkomponenten. Android schnitt bei diesen Aspekten relativ schlecht ab,\nMaemo scheint die meisten Freiheitsgrade zu bieten. Für die Entwicklung auf den Geräten spielt die dies meiner Meinung\njedoch eine recht geringe Rolle, solange höherwertige Funktionen angeboten werden, mit denen die benötigten\nFunktionalitäten abgedeckt werden können.",[439,83275,83276,83277,83282,83283,83288],{},"Der Vortrag, wegen dem ich mich eigentlich zum Besuch der FrOSCon entschlossen hatte, Ruby on Rails 3\nvon ",[1002,83278,83281],{"href":83279,"rel":83280},"http://yehudakatz.com/",[1006],"Yehuda Katz",", war leider nicht so technisch, wie ich ihn mir gewünscht hatte. Gerade da er\nvon einem Mann aus dem ",[1002,83284,83287],{"href":83285,"rel":83286},"http://rubyonrails.org/merb",[1006],"Merb-Team"," gehalten wurde, hätte ich mir einen Überblick über die\nUnterschiede zwischen den Versionen gewünscht. Merb war ehemals ein Konkurrenz-Framework und geht mit Version 3 in Ruby\non Rails auf. Auch wenn mir die technischen Konzepte gefehlt haben war der Einblick in die Arbeit des Rails-Teams dann\ndoch interessant.",[439,83290,83291,83292,83297,83298,83302],{},"Kurzfristig eingeschoben wurde ein Talk von ",[1002,83293,83296],{"href":83294,"rel":83295},"http://kippdata.de/",[1006],"Rainer Jung"," zu den Neuerungen\nin ",[1002,83299,83301],{"href":80923,"rel":83300},[1006],"Apache httpd"," 2.4. Besonders die Anforderungen an einen Webserver, die sich durch die\nstark steigende Anzahl an Connections durch die Verbreitung von AJAX-Anwendungen und das Offenhalten der Connections\ndurch Techniken wie WebSockets/Comet ergeben, waren spannend. Beeindruckend, dass Rainer Jung es nicht nur schafft, in\nzwei so wichtigen Projekten wie dem in C geschriebenen httpd und dem in Java geschriebenen Servlet-Container Tomcat\nCore-Committer zu sein, sondern auch beide Projekte gleichwertig vertreten kann, indem er Samstags ein Tomcat-Shirt\nund Sonntags ein Apache-Shirt trägt :).",[439,83304,83305],{},"Alles in allem war die FrOSCon auch in diesem Jahr wieder außerordentlich lohnend. Die lockere Atmosphäre ist mit keiner\nanderen Konferenz, die ich kenne, zu vergleichen, was sicher einerseits an dem Termin am Wochenende liegt und\nandererseits daran, dass die Konferenz weniger businesslastig ist, als die mir bekannten Java-Konferenzen. Trotzdem\nkann man jede Menge neuen Input für die tägliche Arbeit bekommen, ein Besuch lohnt sich also nicht nur wegen des extrem\nniedrigen Eintrittspreises von 5€.",[439,83307,83308,83309,83314],{},"Und noch ein praktischer Tipp am Rande: Wer kein Problem mit einem kleinen Fußmarsch hat, ist beim sehr freundlichen\nWirt im ",[1002,83310,83313],{"href":83311,"rel":83312},"http://www.zum-laternchen.de/",[1006],"Laternchen"," bestens aufgehoben.",{"title":469,"searchDepth":507,"depth":507,"links":83316},[],[1412],"2010-08-24T06:16:08","Am 21. und 22.08. fand für mich die 2. Free and Open Source Software Conference FrOSCon in St.\\nAugustin bei Bonn statt. Der Hauptgrund für meinen Besuch im letzten Jahr war ein ausgedehnter Java-Track, ich war\\njedoch von der Atmosphäre und der Vielfalt der Themen so begeistert, dass die Konferenz für mich sicher ein regelmäßiges\\nEreignis sein wird.","https://synyx.de/blog/froscon-2010/",{},"/blog/froscon-2010",{"title":83213,"description":83324},"Am 21. und 22.08. fand für mich die 2. Free and Open Source Software Conference FrOSCon in St.\nAugustin bei Bonn statt. Der Hauptgrund für meinen Besuch im letzten Jahr war ein ausgedehnter Java-Track, ich war\njedoch von der Atmosphäre und der Vielfalt der Themen so begeistert, dass die Konferenz für mich sicher ein regelmäßiges\nEreignis sein wird.","blog/froscon-2010",[73058,65242,9556,18496,80841,26636,83327],"ruby-on-rails","Am 21. und 22.08. fand für mich die 2. Free and Open Source Software Conference FrOSCon in St. Augustin bei Bonn statt. Der Hauptgrund für meinen Besuch im letzten Jahr…","I5FF7sxuFa3dbVLJ3H-3GJIR6OfzFdL06uiY3HOqsj4",{"id":83331,"title":83332,"author":83333,"body":83334,"category":83383,"date":83384,"description":83385,"extension":1034,"link":83386,"meta":83387,"navigation":916,"path":83388,"seo":83389,"slug":83390,"stem":83391,"tags":83392,"teaser":83395,"__hash__":83396},"blog/blog/iphone-partner-und-spinnen-holl-the-woodfairy.md","iPhone, Partner und Spinnen – holla the woodfairy",[172],{"type":432,"value":83335,"toc":83381},[83336,83339,83349,83362,83371],[435,83337,83332],{"id":83338},"iphone-partner-und-spinnen-holla-the-woodfairy",[439,83340,83341,83348],{},[1002,83342,83345],{"href":83343,"rel":83344},"https://media.synyx.de/uploads//2010/08/ithinkispider.png",[1006],[2205,83346],{"alt":83347,"src":83343,"title":82560},"I think I spider app","\nWie passt das alles zusammen? Ganz einfach …",[439,83350,83351,83352,83356,83357,83361],{},"“Holla the woodfairy – holla die Waldfee”. Was haben wir gelacht über diese und noch viele weitere “Übersetzungen” auf\nder Website “",[1002,83353,77940],{"href":83354,"rel":83355,"title":77940},"http://ithinkispider.com/",[1006],"“. Und da lachen bekanntlich gesund ist,\nkamen wir schnell zu dem Entschluss noch mehr Menschen zum Lachen zu bringen. Was liegt da also näher, als eine iPhone\nApp zu diesem Thema zu entwickeln? David Linsin, Ansprechpartner für den\nGeschäftsbereich ",[1002,83358,83360],{"href":13089,"rel":83359,"title":83360},[1006],"Mobile Solutions",", war sofort begeistert von\ndieser Idee und nahm Kontakt mit dem Betreiber der Website auf. Mit seiner Erlaubnis fiel der Startschuss für die\nunkommerzielle Entwicklung der App.",[439,83363,83364,83365,83370],{},"Eine gut durchdachte und funktionierende App ist aber nicht alles. Das Auge isst bekanntlich mit. Also hieß es für uns\njemanden zu finden, der genauso begeistert von dieser Idee ist und das Ganze auch noch grafisch umsetzen kann. Dieser\nwar schnell gefunden: Lars von ",[1002,83366,77946],{"href":83367,"rel":83368,"title":83369},"http://www.upstruct.com",[1006],"upstuct berlin oslo",". Die Zusammenarbeit hat\nhervorragend geklappt und wir sehen auch für die Zukunft gemeinsame Projekte vor.",[439,83372,83373,83374,83380],{},"Entstanden ist eine feine, kleine, lustige App, welche sich zurzeit in der Betaphase befindet. Wir sind schon auf das\nFeedback gespannt und freuen uns auf die Version 1.0! Wer auf dem Laufenden bleiben will, soll einfach\nden ",[1002,83375,83379],{"href":83376,"rel":83377,"title":83378},"https://synyx.de/blog/kategorien/mobile-blog/",[1006],"Synyx Mobile Solution Blog","Mobile Solutions Blog"," im Auge\nbehalten. Dort werden alle Neuigkeiten und Infos zu dem Thema gepostet.",{"title":469,"searchDepth":507,"depth":507,"links":83382},[],[1031],"2010-08-17T15:03:53","\\nWie passt das alles zusammen? Ganz einfach …","https://synyx.de/blog/iphone-partner-und-spinnen-holl-the-woodfairy/",{},"/blog/iphone-partner-und-spinnen-holl-the-woodfairy",{"title":83332,"description":83348},"iphone-partner-und-spinnen-holl-the-woodfairy","blog/iphone-partner-und-spinnen-holl-the-woodfairy",[83393,82754,78398,18496,83394],"app","partner","Wie passt das alles zusammen? Ganz einfach … “Holla the woodfairy – holla die Waldfee”. Was haben wir gelacht über diese und noch viele weitere “Übersetzungen” auf der Website “I…","DFJ5251aaMKOTAGo0Y2SgGvHnW9DdfdTcakwCeGfggQ",{"id":83398,"title":83399,"author":83400,"body":83401,"category":83523,"date":83524,"description":469,"extension":1034,"link":83525,"meta":83526,"navigation":916,"path":83527,"seo":83528,"slug":83405,"stem":83529,"tags":83530,"teaser":83531,"__hash__":83532},"blog/blog/settings-bundle-and-default-values.md","Settings Bundle and Default Values",[223],{"type":432,"value":83402,"toc":83521},[83403,83406,83410,83459,83472,83505,83516,83519],[435,83404,83399],{"id":83405},"settings-bundle-and-default-values",[439,83407,83408],{},[2205,83409],{"alt":78385,"src":82521},[464,83411,83413],{"className":6253,"code":83412,"language":6255,"meta":469,"style":469},"\n\u003Ckey>Type\u003C/key>\n\u003Cstring>PSToggleSwitchSpecifier\u003C/string>\n\u003Ckey>Title\u003C/key>\n\u003Cstring>Sound\u003C/string>\n\u003Ckey>Key\u003C/key>\n\u003Cstring>sound_enabled\u003C/string>\n\u003Ckey>DefaultValue\u003C/key>\n\u003Ctrue/>\n\n",[471,83414,83415,83419,83424,83429,83434,83439,83444,83449,83454],{"__ignoreMap":469},[474,83416,83417],{"class":476,"line":477},[474,83418,917],{"emptyLinePlaceholder":916},[474,83420,83421],{"class":476,"line":507},[474,83422,83423],{},"\u003Ckey>Type\u003C/key>\n",[474,83425,83426],{"class":476,"line":547},[474,83427,83428],{},"\u003Cstring>PSToggleSwitchSpecifier\u003C/string>\n",[474,83430,83431],{"class":476,"line":584},[474,83432,83433],{},"\u003Ckey>Title\u003C/key>\n",[474,83435,83436],{"class":476,"line":607},[474,83437,83438],{},"\u003Cstring>Sound\u003C/string>\n",[474,83440,83441],{"class":476,"line":642},[474,83442,83443],{},"\u003Ckey>Key\u003C/key>\n",[474,83445,83446],{"class":476,"line":663},[474,83447,83448],{},"\u003Cstring>sound_enabled\u003C/string>\n",[474,83450,83451],{"class":476,"line":694},[474,83452,83453],{},"\u003Ckey>DefaultValue\u003C/key>\n",[474,83455,83456],{"class":476,"line":700},[474,83457,83458],{},"\u003Ctrue/>\n",[439,83460,83461,83462,83467,83468,83471],{},"Unfortuntately, the default value is only applied the first time you access the Settings Application. That means your\ncode cannot rely on the default values and rather has to check manually, if the value has been set in the Settings\nApplication. In case of “I think I spider”, that meant no sound until you accessed the Settings Application. After\npoking around for a couple of minutes,\nwe ",[1002,83463,83466],{"href":83464,"rel":83465},"http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the-s/510329#510329",[1006],"found a workaround",",\nwhich we implemented in our AppDelegate’s ",[990,83469,83470],{},"didFinishLaunchingWithOptions"," method:",[464,83473,83475],{"className":83142,"code":83474,"language":83144,"meta":469,"style":469},"\nid test = [[NSUserDefaults standardUserDefaults] objectForKey:@\"sound_enabled\"];\nif (test == NULL) {\n [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@\"sound_enabled\"];\n}\nreturn YES;\n",[471,83476,83477,83481,83486,83491,83496,83500],{"__ignoreMap":469},[474,83478,83479],{"class":476,"line":477},[474,83480,917],{"emptyLinePlaceholder":916},[474,83482,83483],{"class":476,"line":507},[474,83484,83485],{},"id test = [[NSUserDefaults standardUserDefaults] objectForKey:@\"sound_enabled\"];\n",[474,83487,83488],{"class":476,"line":547},[474,83489,83490],{},"if (test == NULL) {\n",[474,83492,83493],{"class":476,"line":584},[474,83494,83495],{}," [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@\"sound_enabled\"];\n",[474,83497,83498],{"class":476,"line":607},[474,83499,703],{},[474,83501,83502],{"class":476,"line":642},[474,83503,83504],{},"return YES;\n",[439,83506,83507,83508,83511,83512,83515],{},"This checks if no value was set, which means the returned value is ",[990,83509,83510],{},"NULL",", and in that case sets our default value. Note\nthat we are duplicating the default value definition here. You could also access the ",[990,83513,83514],{},"Settings Bundle"," programmatically\nand read the default value from there.",[439,83517,83518],{},"This gives us an easy workaround, so that you can enjoy the awesome sound effects in “I think I spider”.",[1024,83520,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":83522},[],[11122,3799,1413],"2010-08-16T06:38:08","https://synyx.de/blog/settings-bundle-and-default-values/",{},"/blog/settings-bundle-and-default-values",{"title":83399,"description":469},"blog/settings-bundle-and-default-values",[63945,78398],"We improved our “I think I spider” App quite a bit since Beta 1. Among other stuff we added some nice sound effects. If you want to check it out,…","2u74Bt9iJYUS2yCQZEziD6_UMO-mCCEs4rBEu1KVQJQ",{"id":83534,"title":83535,"author":83536,"body":83537,"category":85043,"date":85044,"description":85045,"extension":1034,"link":85046,"meta":85047,"navigation":916,"path":85048,"seo":85049,"slug":83541,"stem":85051,"tags":85052,"teaser":85053,"__hash__":85054},"blog/blog/on-cross-device-mobile-development-part-1.md","On cross-device mobile development – Part 1",[205],{"type":432,"value":83538,"toc":85037},[83539,83542,83549,83553,83556,83560,83568,83870,83873,84561,84564,84760,84771,84818,84821,84986,84988,85000,85003,85010,85014,85017,85031,85034],[435,83540,83535],{"id":83541},"on-cross-device-mobile-development-part-1",[439,83543,83544,83545,83548],{},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. ",[990,83546,83547],{},"That’s the theory at least."," In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.",[3938,83550,83552],{"id":83551},"its-not-in-the-browser","It’s (not) in the browser",[439,83554,83555],{},"Developing applications with HTML, CSS & Javascript is a fundamentally different experience. It already is on the\nnormal desktop with its widescreen browsers. On a mobile device it becomes a game changer. We are accustomed to\ngraphical user interface (GUI) toolkits with their more direct manipulation of data through on-screen elements like\nbuttons. While browsers and HTML provide their own set of UI elements and the ability to wire events like clicks to\nJavascript callbacks, they lack a lot of the bells and whistles. Especially in the handling of client-side data — the\nmodel in model-view-controller (MVC) so to say — and the controller abstraction even modern browsers lack the feature\nrichness of common native SDKs. In the rest of this article we will give a short overview on which web technologies map\nto which concept in the MVC approach and how you would basically structure an application based on such technologies.",[3938,83557,83559],{"id":83558},"of-models-views-controllers","Of models, views & controllers",[439,83561,43313,83562,83567],{},[1002,83563,83566],{"href":83564,"rel":83565},"http://building-iphone-apps.labs.oreilly.com/ch04.html",[1006],"“Building iPhone Apps with HTML, CSS and JavaScript”","\nJonathan Stark describes how to turn websites into single page iPhone applications. Views are implemented straight\nforward with plain HTML and CSS, throwing in some bits and pieces to make browser standard behavior of HTML elements\nmore native like. To give an example, we will show how to create a graphical navigation bar and switch between different\nscreens. First we need some HTML and CSS snippets to create the layout:",[464,83569,83571],{"className":15039,"code":83570,"language":15041,"meta":469,"style":469},"\u003Cul id=\"navigation\">\n \u003Cli>\u003Ca class=\"current\" href=\"#home\">Home\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#new\">New things\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#favorites\">I like those\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#more\">More content\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#info\">About\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"view\" id=\"home\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"new\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"favorites\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"more\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"info\">...\u003C/div>\n",[471,83572,83573,83588,83625,83653,83681,83709,83737,83745,83771,83795,83820,83845],{"__ignoreMap":469},[474,83574,83575,83577,83579,83581,83583,83586],{"class":476,"line":477},[474,83576,15048],{"class":503},[474,83578,994],{"class":15051},[474,83580,15185],{"class":480},[474,83582,811],{"class":503},[474,83584,83585],{"class":484},"\"navigation\"",[474,83587,15070],{"class":503},[474,83589,83590,83592,83594,83597,83599,83601,83603,83606,83609,83611,83614,83617,83619,83621,83623],{"class":476,"line":507},[474,83591,15075],{"class":503},[474,83593,997],{"class":15051},[474,83595,83596],{"class":503},">\u003C",[474,83598,1002],{"class":15051},[474,83600,61709],{"class":480},[474,83602,811],{"class":503},[474,83604,83605],{"class":484},"\"current\"",[474,83607,83608],{"class":480}," href",[474,83610,811],{"class":503},[474,83612,83613],{"class":484},"\"#home\"",[474,83615,83616],{"class":503},">Home\u003C/",[474,83618,1002],{"class":15051},[474,83620,15192],{"class":503},[474,83622,997],{"class":15051},[474,83624,15070],{"class":503},[474,83626,83627,83629,83631,83633,83635,83637,83639,83642,83645,83647,83649,83651],{"class":476,"line":547},[474,83628,15075],{"class":503},[474,83630,997],{"class":15051},[474,83632,83596],{"class":503},[474,83634,1002],{"class":15051},[474,83636,83608],{"class":480},[474,83638,811],{"class":503},[474,83640,83641],{"class":484},"\"#new\"",[474,83643,83644],{"class":503},">New things\u003C/",[474,83646,1002],{"class":15051},[474,83648,15192],{"class":503},[474,83650,997],{"class":15051},[474,83652,15070],{"class":503},[474,83654,83655,83657,83659,83661,83663,83665,83667,83670,83673,83675,83677,83679],{"class":476,"line":584},[474,83656,15075],{"class":503},[474,83658,997],{"class":15051},[474,83660,83596],{"class":503},[474,83662,1002],{"class":15051},[474,83664,83608],{"class":480},[474,83666,811],{"class":503},[474,83668,83669],{"class":484},"\"#favorites\"",[474,83671,83672],{"class":503},">I like those\u003C/",[474,83674,1002],{"class":15051},[474,83676,15192],{"class":503},[474,83678,997],{"class":15051},[474,83680,15070],{"class":503},[474,83682,83683,83685,83687,83689,83691,83693,83695,83698,83701,83703,83705,83707],{"class":476,"line":607},[474,83684,15075],{"class":503},[474,83686,997],{"class":15051},[474,83688,83596],{"class":503},[474,83690,1002],{"class":15051},[474,83692,83608],{"class":480},[474,83694,811],{"class":503},[474,83696,83697],{"class":484},"\"#more\"",[474,83699,83700],{"class":503},">More content\u003C/",[474,83702,1002],{"class":15051},[474,83704,15192],{"class":503},[474,83706,997],{"class":15051},[474,83708,15070],{"class":503},[474,83710,83711,83713,83715,83717,83719,83721,83723,83726,83729,83731,83733,83735],{"class":476,"line":642},[474,83712,15075],{"class":503},[474,83714,997],{"class":15051},[474,83716,83596],{"class":503},[474,83718,1002],{"class":15051},[474,83720,83608],{"class":480},[474,83722,811],{"class":503},[474,83724,83725],{"class":484},"\"#info\"",[474,83727,83728],{"class":503},">About\u003C/",[474,83730,1002],{"class":15051},[474,83732,15192],{"class":503},[474,83734,997],{"class":15051},[474,83736,15070],{"class":503},[474,83738,83739,83741,83743],{"class":476,"line":663},[474,83740,15168],{"class":503},[474,83742,994],{"class":15051},[474,83744,15070],{"class":503},[474,83746,83747,83749,83751,83753,83755,83758,83760,83762,83765,83767,83769],{"class":476,"line":694},[474,83748,15048],{"class":503},[474,83750,15027],{"class":15051},[474,83752,61709],{"class":480},[474,83754,811],{"class":503},[474,83756,83757],{"class":484},"\"view\"",[474,83759,15185],{"class":480},[474,83761,811],{"class":503},[474,83763,83764],{"class":484},"\"home\"",[474,83766,76511],{"class":503},[474,83768,15027],{"class":15051},[474,83770,15070],{"class":503},[474,83772,83773,83775,83777,83779,83781,83783,83785,83787,83789,83791,83793],{"class":476,"line":700},[474,83774,15048],{"class":503},[474,83776,15027],{"class":15051},[474,83778,61709],{"class":480},[474,83780,811],{"class":503},[474,83782,83757],{"class":484},[474,83784,15185],{"class":480},[474,83786,811],{"class":503},[474,83788,68124],{"class":484},[474,83790,76511],{"class":503},[474,83792,15027],{"class":15051},[474,83794,15070],{"class":503},[474,83796,83797,83799,83801,83803,83805,83807,83809,83811,83814,83816,83818],{"class":476,"line":913},[474,83798,15048],{"class":503},[474,83800,15027],{"class":15051},[474,83802,61709],{"class":480},[474,83804,811],{"class":503},[474,83806,83757],{"class":484},[474,83808,15185],{"class":480},[474,83810,811],{"class":503},[474,83812,83813],{"class":484},"\"favorites\"",[474,83815,76511],{"class":503},[474,83817,15027],{"class":15051},[474,83819,15070],{"class":503},[474,83821,83822,83824,83826,83828,83830,83832,83834,83836,83839,83841,83843],{"class":476,"line":920},[474,83823,15048],{"class":503},[474,83825,15027],{"class":15051},[474,83827,61709],{"class":480},[474,83829,811],{"class":503},[474,83831,83757],{"class":484},[474,83833,15185],{"class":480},[474,83835,811],{"class":503},[474,83837,83838],{"class":484},"\"more\"",[474,83840,76511],{"class":503},[474,83842,15027],{"class":15051},[474,83844,15070],{"class":503},[474,83846,83847,83849,83851,83853,83855,83857,83859,83861,83864,83866,83868],{"class":476,"line":926},[474,83848,15048],{"class":503},[474,83850,15027],{"class":15051},[474,83852,61709],{"class":480},[474,83854,811],{"class":503},[474,83856,83757],{"class":484},[474,83858,15185],{"class":480},[474,83860,811],{"class":503},[474,83862,83863],{"class":484},"\"info\"",[474,83865,76511],{"class":503},[474,83867,15027],{"class":15051},[474,83869,15070],{"class":503},[439,83871,83872],{},"The navigational items are then replaced with nice button graphics via CSS and positioned on the page.",[464,83874,83877],{"className":83875,"code":83876,"language":22162,"meta":469,"style":469},"language-css shiki shiki-themes github-light github-dark","/* navigation is a fixed block */\n#navigation {\n position: fixed;\n top: 0px;\n left: 0px;\n width: 320px;\n margin: 0;\n padding: 0;\n}\n#navigation li {\n display: block;\n position: absolute;\n}\n#navigation li a {\n display: block;\n position: absolute;\n height: 48px;\n width: 80px;\n top: 0px;\n text-indent: -9999px;\n}\na[href=\"#home\"] {\n background: url(home.png);\n left: 0px;\n}\na[href=\"#home\"].current {\n background: url(home-current.png);\n}\na[href=\"#new\"] {\n background: url(new.png);\n left: 80px;\n}\na[href=\"#new\"].current {\n background: url(new-current.png);\n}\na[href=\"#favorites\"] {\n background: url(favorites.png);\n left: 160px;\n}\na[href=\"#favorites\"].current {\n background: url(favorites-current.png);\n}\na[href=\"#more\"] {\n background: url(more.png);\n left: 240px;\n}\na[href=\"#info\"] {\n background: url(info.png);\n position: fixed;\n width: 48px;\n height: 48px;\n bottom: 16px;\n right: 16px;\n}\n/* finally position the views themselves */\ndiv.view {\n position: absolute;\n top: 48px;\n left: 0px;\n width: 320px;\n height: 396px;\n}\n",[471,83878,83879,83884,83891,83903,83916,83929,83943,83954,83965,83969,83978,83990,84001,84005,84015,84025,84035,84049,84062,84074,84088,84092,84109,84125,84137,84141,84160,84175,84179,84193,84208,84220,84224,84242,84257,84261,84275,84290,84303,84307,84325,84340,84344,84358,84373,84386,84390,84404,84419,84429,84441,84453,84467,84480,84484,84489,84498,84508,84520,84532,84544,84557],{"__ignoreMap":469},[474,83880,83881],{"class":476,"line":477},[474,83882,83883],{"class":2277},"/* navigation is a fixed block */\n",[474,83885,83886,83889],{"class":476,"line":507},[474,83887,83888],{"class":480},"#navigation",[474,83890,14027],{"class":503},[474,83892,83893,83896,83898,83901],{"class":476,"line":547},[474,83894,83895],{"class":510}," position",[474,83897,4709],{"class":503},[474,83899,83900],{"class":510},"fixed",[474,83902,2139],{"class":503},[474,83904,83905,83908,83910,83912,83914],{"class":476,"line":584},[474,83906,83907],{"class":510}," top",[474,83909,4709],{"class":503},[474,83911,4745],{"class":510},[474,83913,57867],{"class":810},[474,83915,2139],{"class":503},[474,83917,83918,83921,83923,83925,83927],{"class":476,"line":607},[474,83919,83920],{"class":510}," left",[474,83922,4709],{"class":503},[474,83924,4745],{"class":510},[474,83926,57867],{"class":810},[474,83928,2139],{"class":503},[474,83930,83931,83934,83936,83939,83941],{"class":476,"line":642},[474,83932,83933],{"class":510}," width",[474,83935,4709],{"class":503},[474,83937,83938],{"class":510},"320",[474,83940,57867],{"class":810},[474,83942,2139],{"class":503},[474,83944,83945,83948,83950,83952],{"class":476,"line":663},[474,83946,83947],{"class":510}," margin",[474,83949,4709],{"class":503},[474,83951,4745],{"class":510},[474,83953,2139],{"class":503},[474,83955,83956,83959,83961,83963],{"class":476,"line":694},[474,83957,83958],{"class":510}," padding",[474,83960,4709],{"class":503},[474,83962,4745],{"class":510},[474,83964,2139],{"class":503},[474,83966,83967],{"class":476,"line":700},[474,83968,703],{"class":503},[474,83970,83971,83973,83976],{"class":476,"line":913},[474,83972,83888],{"class":480},[474,83974,83975],{"class":15051}," li",[474,83977,14027],{"class":503},[474,83979,83980,83983,83985,83988],{"class":476,"line":920},[474,83981,83982],{"class":510}," display",[474,83984,4709],{"class":503},[474,83986,83987],{"class":510},"block",[474,83989,2139],{"class":503},[474,83991,83992,83994,83996,83999],{"class":476,"line":926},[474,83993,83895],{"class":510},[474,83995,4709],{"class":503},[474,83997,83998],{"class":510},"absolute",[474,84000,2139],{"class":503},[474,84002,84003],{"class":476,"line":932},[474,84004,703],{"class":503},[474,84006,84007,84009,84011,84013],{"class":476,"line":938},[474,84008,83888],{"class":480},[474,84010,83975],{"class":15051},[474,84012,20159],{"class":15051},[474,84014,14027],{"class":503},[474,84016,84017,84019,84021,84023],{"class":476,"line":944},[474,84018,83982],{"class":510},[474,84020,4709],{"class":503},[474,84022,83987],{"class":510},[474,84024,2139],{"class":503},[474,84026,84027,84029,84031,84033],{"class":476,"line":950},[474,84028,83895],{"class":510},[474,84030,4709],{"class":503},[474,84032,83998],{"class":510},[474,84034,2139],{"class":503},[474,84036,84037,84040,84042,84045,84047],{"class":476,"line":956},[474,84038,84039],{"class":510}," height",[474,84041,4709],{"class":503},[474,84043,84044],{"class":510},"48",[474,84046,57867],{"class":810},[474,84048,2139],{"class":503},[474,84050,84051,84053,84055,84058,84060],{"class":476,"line":962},[474,84052,83933],{"class":510},[474,84054,4709],{"class":503},[474,84056,84057],{"class":510},"80",[474,84059,57867],{"class":810},[474,84061,2139],{"class":503},[474,84063,84064,84066,84068,84070,84072],{"class":476,"line":4876},[474,84065,83907],{"class":510},[474,84067,4709],{"class":503},[474,84069,4745],{"class":510},[474,84071,57867],{"class":810},[474,84073,2139],{"class":503},[474,84075,84076,84079,84081,84084,84086],{"class":476,"line":4888},[474,84077,84078],{"class":510}," text-indent",[474,84080,4709],{"class":503},[474,84082,84083],{"class":510},"-9999",[474,84085,57867],{"class":810},[474,84087,2139],{"class":503},[474,84089,84090],{"class":476,"line":4900},[474,84091,703],{"class":503},[474,84093,84094,84096,84099,84102,84104,84106],{"class":476,"line":4913},[474,84095,1002],{"class":15051},[474,84097,84098],{"class":503},"[",[474,84100,84101],{"class":480},"href",[474,84103,811],{"class":810},[474,84105,83613],{"class":484},[474,84107,84108],{"class":503},"] {\n",[474,84110,84111,84114,84116,84118,84120,84123],{"class":476,"line":4921},[474,84112,84113],{"class":510}," background",[474,84115,4709],{"class":503},[474,84117,81259],{"class":510},[474,84119,1483],{"class":503},[474,84121,84122],{"class":14037},"home.png",[474,84124,1495],{"class":503},[474,84126,84127,84129,84131,84133,84135],{"class":476,"line":4932},[474,84128,83920],{"class":510},[474,84130,4709],{"class":503},[474,84132,4745],{"class":510},[474,84134,57867],{"class":810},[474,84136,2139],{"class":503},[474,84138,84139],{"class":476,"line":4938},[474,84140,703],{"class":503},[474,84142,84143,84145,84147,84149,84151,84153,84155,84158],{"class":476,"line":4946},[474,84144,1002],{"class":15051},[474,84146,84098],{"class":503},[474,84148,84101],{"class":480},[474,84150,811],{"class":810},[474,84152,83613],{"class":484},[474,84154,15931],{"class":503},[474,84156,84157],{"class":480},".current",[474,84159,14027],{"class":503},[474,84161,84162,84164,84166,84168,84170,84173],{"class":476,"line":4952},[474,84163,84113],{"class":510},[474,84165,4709],{"class":503},[474,84167,81259],{"class":510},[474,84169,1483],{"class":503},[474,84171,84172],{"class":14037},"home-current.png",[474,84174,1495],{"class":503},[474,84176,84177],{"class":476,"line":4957},[474,84178,703],{"class":503},[474,84180,84181,84183,84185,84187,84189,84191],{"class":476,"line":4969},[474,84182,1002],{"class":15051},[474,84184,84098],{"class":503},[474,84186,84101],{"class":480},[474,84188,811],{"class":810},[474,84190,83641],{"class":484},[474,84192,84108],{"class":503},[474,84194,84195,84197,84199,84201,84203,84206],{"class":476,"line":4990},[474,84196,84113],{"class":510},[474,84198,4709],{"class":503},[474,84200,81259],{"class":510},[474,84202,1483],{"class":503},[474,84204,84205],{"class":14037},"new.png",[474,84207,1495],{"class":503},[474,84209,84210,84212,84214,84216,84218],{"class":476,"line":5001},[474,84211,83920],{"class":510},[474,84213,4709],{"class":503},[474,84215,84057],{"class":510},[474,84217,57867],{"class":810},[474,84219,2139],{"class":503},[474,84221,84222],{"class":476,"line":5013},[474,84223,703],{"class":503},[474,84225,84226,84228,84230,84232,84234,84236,84238,84240],{"class":476,"line":5024},[474,84227,1002],{"class":15051},[474,84229,84098],{"class":503},[474,84231,84101],{"class":480},[474,84233,811],{"class":810},[474,84235,83641],{"class":484},[474,84237,15931],{"class":503},[474,84239,84157],{"class":480},[474,84241,14027],{"class":503},[474,84243,84244,84246,84248,84250,84252,84255],{"class":476,"line":5035},[474,84245,84113],{"class":510},[474,84247,4709],{"class":503},[474,84249,81259],{"class":510},[474,84251,1483],{"class":503},[474,84253,84254],{"class":14037},"new-current.png",[474,84256,1495],{"class":503},[474,84258,84259],{"class":476,"line":5047},[474,84260,703],{"class":503},[474,84262,84263,84265,84267,84269,84271,84273],{"class":476,"line":5055},[474,84264,1002],{"class":15051},[474,84266,84098],{"class":503},[474,84268,84101],{"class":480},[474,84270,811],{"class":810},[474,84272,83669],{"class":484},[474,84274,84108],{"class":503},[474,84276,84277,84279,84281,84283,84285,84288],{"class":476,"line":5062},[474,84278,84113],{"class":510},[474,84280,4709],{"class":503},[474,84282,81259],{"class":510},[474,84284,1483],{"class":503},[474,84286,84287],{"class":14037},"favorites.png",[474,84289,1495],{"class":503},[474,84291,84292,84294,84296,84299,84301],{"class":476,"line":5067},[474,84293,83920],{"class":510},[474,84295,4709],{"class":503},[474,84297,84298],{"class":510},"160",[474,84300,57867],{"class":810},[474,84302,2139],{"class":503},[474,84304,84305],{"class":476,"line":5072},[474,84306,703],{"class":503},[474,84308,84309,84311,84313,84315,84317,84319,84321,84323],{"class":476,"line":5084},[474,84310,1002],{"class":15051},[474,84312,84098],{"class":503},[474,84314,84101],{"class":480},[474,84316,811],{"class":810},[474,84318,83669],{"class":484},[474,84320,15931],{"class":503},[474,84322,84157],{"class":480},[474,84324,14027],{"class":503},[474,84326,84327,84329,84331,84333,84335,84338],{"class":476,"line":5100},[474,84328,84113],{"class":510},[474,84330,4709],{"class":503},[474,84332,81259],{"class":510},[474,84334,1483],{"class":503},[474,84336,84337],{"class":14037},"favorites-current.png",[474,84339,1495],{"class":503},[474,84341,84342],{"class":476,"line":5111},[474,84343,703],{"class":503},[474,84345,84346,84348,84350,84352,84354,84356],{"class":476,"line":5122},[474,84347,1002],{"class":15051},[474,84349,84098],{"class":503},[474,84351,84101],{"class":480},[474,84353,811],{"class":810},[474,84355,83697],{"class":484},[474,84357,84108],{"class":503},[474,84359,84360,84362,84364,84366,84368,84371],{"class":476,"line":5134},[474,84361,84113],{"class":510},[474,84363,4709],{"class":503},[474,84365,81259],{"class":510},[474,84367,1483],{"class":503},[474,84369,84370],{"class":14037},"more.png",[474,84372,1495],{"class":503},[474,84374,84375,84377,84379,84382,84384],{"class":476,"line":5145},[474,84376,83920],{"class":510},[474,84378,4709],{"class":503},[474,84380,84381],{"class":510},"240",[474,84383,57867],{"class":810},[474,84385,2139],{"class":503},[474,84387,84388],{"class":476,"line":5156},[474,84389,703],{"class":503},[474,84391,84392,84394,84396,84398,84400,84402],{"class":476,"line":5163},[474,84393,1002],{"class":15051},[474,84395,84098],{"class":503},[474,84397,84101],{"class":480},[474,84399,811],{"class":810},[474,84401,83725],{"class":484},[474,84403,84108],{"class":503},[474,84405,84406,84408,84410,84412,84414,84417],{"class":476,"line":5170},[474,84407,84113],{"class":510},[474,84409,4709],{"class":503},[474,84411,81259],{"class":510},[474,84413,1483],{"class":503},[474,84415,84416],{"class":14037},"info.png",[474,84418,1495],{"class":503},[474,84420,84421,84423,84425,84427],{"class":476,"line":5175},[474,84422,83895],{"class":510},[474,84424,4709],{"class":503},[474,84426,83900],{"class":510},[474,84428,2139],{"class":503},[474,84430,84431,84433,84435,84437,84439],{"class":476,"line":5180},[474,84432,83933],{"class":510},[474,84434,4709],{"class":503},[474,84436,84044],{"class":510},[474,84438,57867],{"class":810},[474,84440,2139],{"class":503},[474,84442,84443,84445,84447,84449,84451],{"class":476,"line":5192},[474,84444,84039],{"class":510},[474,84446,4709],{"class":503},[474,84448,84044],{"class":510},[474,84450,57867],{"class":810},[474,84452,2139],{"class":503},[474,84454,84455,84458,84460,84463,84465],{"class":476,"line":5217},[474,84456,84457],{"class":510}," bottom",[474,84459,4709],{"class":503},[474,84461,84462],{"class":510},"16",[474,84464,57867],{"class":810},[474,84466,2139],{"class":503},[474,84468,84469,84472,84474,84476,84478],{"class":476,"line":5229},[474,84470,84471],{"class":510}," right",[474,84473,4709],{"class":503},[474,84475,84462],{"class":510},[474,84477,57867],{"class":810},[474,84479,2139],{"class":503},[474,84481,84482],{"class":476,"line":5240},[474,84483,703],{"class":503},[474,84485,84486],{"class":476,"line":5251},[474,84487,84488],{"class":2277},"/* finally position the views themselves */\n",[474,84490,84491,84493,84496],{"class":476,"line":5262},[474,84492,15027],{"class":15051},[474,84494,84495],{"class":480},".view",[474,84497,14027],{"class":503},[474,84499,84500,84502,84504,84506],{"class":476,"line":5274},[474,84501,83895],{"class":510},[474,84503,4709],{"class":503},[474,84505,83998],{"class":510},[474,84507,2139],{"class":503},[474,84509,84510,84512,84514,84516,84518],{"class":476,"line":5281},[474,84511,83907],{"class":510},[474,84513,4709],{"class":503},[474,84515,84044],{"class":510},[474,84517,57867],{"class":810},[474,84519,2139],{"class":503},[474,84521,84522,84524,84526,84528,84530],{"class":476,"line":5294},[474,84523,83920],{"class":510},[474,84525,4709],{"class":503},[474,84527,4745],{"class":510},[474,84529,57867],{"class":810},[474,84531,2139],{"class":503},[474,84533,84534,84536,84538,84540,84542],{"class":476,"line":5307},[474,84535,83933],{"class":510},[474,84537,4709],{"class":503},[474,84539,83938],{"class":510},[474,84541,57867],{"class":810},[474,84543,2139],{"class":503},[474,84545,84546,84548,84550,84553,84555],{"class":476,"line":5318},[474,84547,84039],{"class":510},[474,84549,4709],{"class":503},[474,84551,84552],{"class":510},"396",[474,84554,57867],{"class":810},[474,84556,2139],{"class":503},[474,84558,84559],{"class":476,"line":5323},[474,84560,703],{"class":503},[439,84562,84563],{},"This can act as a simple framework for an application with multiple views. Now we still need to make our navigation\ncontroller to switch between the different views. jQuery to the rescue — a small snippet initializes our view hierarchy\nand provides switching capabilities:",[464,84565,84567],{"className":15199,"code":84566,"language":15201,"meta":469,"style":469},"$(document).ready(function () {\n var navitems = $(\"#navigation li a\");\n navitems.click(function () {\n navitems.removeClass(\"current\");\n var ref = $(this).addClass(\"current\").attr(\"href\");\n /* hide the other views, show the one navigated to\n and trigger a custom event */\n $(\"div.view\").hide();\n $(ref).show().trigger(\"becameActive\");\n });\n $(\"div.view\").hide();\n $(\"div.view.current\").show().trigger(\"becameActive\");\n});\n",[471,84568,84569,84585,84604,84618,84632,84668,84673,84678,84694,84715,84719,84733,84756],{"__ignoreMap":469},[474,84570,84571,84573,84576,84579,84581,84583],{"class":476,"line":477},[474,84572,59935],{"class":480},[474,84574,84575],{"class":503},"(document).",[474,84577,84578],{"class":480},"ready",[474,84580,1483],{"class":503},[474,84582,14021],{"class":810},[474,84584,29622],{"class":503},[474,84586,84587,84589,84592,84594,84597,84599,84602],{"class":476,"line":507},[474,84588,29627],{"class":810},[474,84590,84591],{"class":503}," navitems ",[474,84593,811],{"class":810},[474,84595,84596],{"class":480}," $",[474,84598,1483],{"class":503},[474,84600,84601],{"class":484},"\"#navigation li a\"",[474,84603,1495],{"class":503},[474,84605,84606,84609,84612,84614,84616],{"class":476,"line":547},[474,84607,84608],{"class":503}," navitems.",[474,84610,84611],{"class":480},"click",[474,84613,1483],{"class":503},[474,84615,14021],{"class":810},[474,84617,29622],{"class":503},[474,84619,84620,84623,84626,84628,84630],{"class":476,"line":584},[474,84621,84622],{"class":503}," navitems.",[474,84624,84625],{"class":480},"removeClass",[474,84627,1483],{"class":503},[474,84629,83605],{"class":484},[474,84631,1495],{"class":503},[474,84633,84634,84636,84639,84641,84643,84645,84647,84649,84652,84654,84656,84658,84661,84663,84666],{"class":476,"line":607},[474,84635,46473],{"class":810},[474,84637,84638],{"class":503}," ref ",[474,84640,811],{"class":810},[474,84642,84596],{"class":480},[474,84644,1483],{"class":503},[474,84646,8394],{"class":510},[474,84648,11288],{"class":503},[474,84650,84651],{"class":480},"addClass",[474,84653,1483],{"class":503},[474,84655,83605],{"class":484},[474,84657,11288],{"class":503},[474,84659,84660],{"class":480},"attr",[474,84662,1483],{"class":503},[474,84664,84665],{"class":484},"\"href\"",[474,84667,1495],{"class":503},[474,84669,84670],{"class":476,"line":642},[474,84671,84672],{"class":2277}," /* hide the other views, show the one navigated to\n",[474,84674,84675],{"class":476,"line":663},[474,84676,84677],{"class":2277}," and trigger a custom event */\n",[474,84679,84680,84683,84685,84688,84690,84692],{"class":476,"line":694},[474,84681,84682],{"class":480}," $",[474,84684,1483],{"class":503},[474,84686,84687],{"class":484},"\"div.view\"",[474,84689,11288],{"class":503},[474,84691,76601],{"class":480},[474,84693,15271],{"class":503},[474,84695,84696,84698,84701,84703,84705,84708,84710,84713],{"class":476,"line":700},[474,84697,84682],{"class":480},[474,84699,84700],{"class":503},"(ref).",[474,84702,76652],{"class":480},[474,84704,78779],{"class":503},[474,84706,84707],{"class":480},"trigger",[474,84709,1483],{"class":503},[474,84711,84712],{"class":484},"\"becameActive\"",[474,84714,1495],{"class":503},[474,84716,84717],{"class":476,"line":913},[474,84718,29665],{"class":503},[474,84720,84721,84723,84725,84727,84729,84731],{"class":476,"line":920},[474,84722,76588],{"class":480},[474,84724,1483],{"class":503},[474,84726,84687],{"class":484},[474,84728,11288],{"class":503},[474,84730,76601],{"class":480},[474,84732,15271],{"class":503},[474,84734,84735,84737,84739,84742,84744,84746,84748,84750,84752,84754],{"class":476,"line":926},[474,84736,76588],{"class":480},[474,84738,1483],{"class":503},[474,84740,84741],{"class":484},"\"div.view.current\"",[474,84743,11288],{"class":503},[474,84745,76652],{"class":480},[474,84747,78779],{"class":503},[474,84749,84707],{"class":480},[474,84751,1483],{"class":503},[474,84753,84712],{"class":484},[474,84755,1495],{"class":503},[474,84757,84758],{"class":476,"line":932},[474,84759,15357],{"class":503},[439,84761,84762,84763,84766,84767,84770],{},"With custom events like ",[471,84764,84765],{},"becameActive"," it is easy to create new callbacks that are invoked when state changes occur — in\nthis example when switching the active view through our navigation controller. For brevity we will omit a elaborate\nexample of controller code, but if for example you want code to run when switching to the ",[471,84768,84769],{},"#new"," view, you could simply\nwrite:",[464,84772,84774],{"className":15199,"code":84773,"language":15201,"meta":469,"style":469},"$(\"#new\").bind(\"becameActive\", function (event) {\n $(event.target).doSomething();\n});\n",[471,84775,84776,84802,84814],{"__ignoreMap":469},[474,84777,84778,84780,84782,84784,84786,84788,84790,84792,84794,84796,84798,84800],{"class":476,"line":477},[474,84779,59935],{"class":480},[474,84781,1483],{"class":503},[474,84783,83641],{"class":484},[474,84785,11288],{"class":503},[474,84787,46239],{"class":480},[474,84789,1483],{"class":503},[474,84791,84712],{"class":484},[474,84793,520],{"class":503},[474,84795,14021],{"class":810},[474,84797,15250],{"class":503},[474,84799,15253],{"class":14037},[474,84801,23418],{"class":503},[474,84803,84804,84806,84809,84812],{"class":476,"line":507},[474,84805,76588],{"class":480},[474,84807,84808],{"class":503},"(event.target).",[474,84810,84811],{"class":480},"doSomething",[474,84813,15271],{"class":503},[474,84815,84816],{"class":476,"line":547},[474,84817,15357],{"class":503},[439,84819,84820],{},"So what is still missing now? We still haven’t provided a data model to operate on. While data provided via web services\nis not a big deal by the use of JSON-APIs, we would certainly want to have a local data storage too. For quite some\ntime already, web engines provide a local SQLite-based storage system. On application initialization we can simply\nsetup some tables to hold our data and send statements from anywhere in the application later on:",[464,84822,84824],{"className":15199,"code":84823,"language":15201,"meta":469,"style":469},"// open a database, providing it's name, version,\n// maximum size and display name\nvar database = openDatabase(\"Example\", \"1.0\", 1048576, \"Example Database\");\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\n \"CREATE TABLE IF NOT EXISTS data\" +\n \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\" +\n \"... more declarations ...);\",\n );\n});\n// other statements are issued the same way\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\"SELECT * FROM data;\", function (transaction, result) {\n // do something with 'result'\n });\n});\n",[471,84825,84826,84831,84836,84870,84887,84897,84904,84911,84918,84922,84926,84931,84947,84973,84978,84982],{"__ignoreMap":469},[474,84827,84828],{"class":476,"line":477},[474,84829,84830],{"class":2277},"// open a database, providing it's name, version,\n",[474,84832,84833],{"class":476,"line":507},[474,84834,84835],{"class":2277},"// maximum size and display name\n",[474,84837,84838,84840,84843,84845,84848,84850,84853,84855,84858,84860,84863,84865,84868],{"class":476,"line":547},[474,84839,46757],{"class":810},[474,84841,84842],{"class":503}," database ",[474,84844,811],{"class":810},[474,84846,84847],{"class":480}," openDatabase",[474,84849,1483],{"class":503},[474,84851,84852],{"class":484},"\"Example\"",[474,84854,520],{"class":503},[474,84856,84857],{"class":484},"\"1.0\"",[474,84859,520],{"class":503},[474,84861,84862],{"class":510},"1048576",[474,84864,520],{"class":503},[474,84866,84867],{"class":484},"\"Example Database\"",[474,84869,1495],{"class":503},[474,84871,84872,84875,84877,84879,84881,84883,84885],{"class":476,"line":584},[474,84873,84874],{"class":503},"database.",[474,84876,47923],{"class":480},[474,84878,1483],{"class":503},[474,84880,14021],{"class":810},[474,84882,15250],{"class":503},[474,84884,47923],{"class":14037},[474,84886,23418],{"class":503},[474,84888,84889,84892,84895],{"class":476,"line":607},[474,84890,84891],{"class":503}," transaction.",[474,84893,84894],{"class":480},"executeSQL",[474,84896,1555],{"class":503},[474,84898,84899,84902],{"class":476,"line":642},[474,84900,84901],{"class":484}," \"CREATE TABLE IF NOT EXISTS data\"",[474,84903,58479],{"class":810},[474,84905,84906,84909],{"class":476,"line":663},[474,84907,84908],{"class":484}," \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"",[474,84910,58479],{"class":810},[474,84912,84913,84916],{"class":476,"line":694},[474,84914,84915],{"class":484}," \"... more declarations ...);\"",[474,84917,4715],{"class":503},[474,84919,84920],{"class":476,"line":700},[474,84921,76636],{"class":503},[474,84923,84924],{"class":476,"line":913},[474,84925,15357],{"class":503},[474,84927,84928],{"class":476,"line":920},[474,84929,84930],{"class":2277},"// other statements are issued the same way\n",[474,84932,84933,84935,84937,84939,84941,84943,84945],{"class":476,"line":926},[474,84934,84874],{"class":503},[474,84936,47923],{"class":480},[474,84938,1483],{"class":503},[474,84940,14021],{"class":810},[474,84942,15250],{"class":503},[474,84944,47923],{"class":14037},[474,84946,23418],{"class":503},[474,84948,84949,84951,84953,84955,84958,84960,84962,84964,84966,84968,84971],{"class":476,"line":932},[474,84950,84891],{"class":503},[474,84952,84894],{"class":480},[474,84954,1483],{"class":503},[474,84956,84957],{"class":484},"\"SELECT * FROM data;\"",[474,84959,520],{"class":503},[474,84961,14021],{"class":810},[474,84963,15250],{"class":503},[474,84965,47923],{"class":14037},[474,84967,520],{"class":503},[474,84969,84970],{"class":14037},"result",[474,84972,23418],{"class":503},[474,84974,84975],{"class":476,"line":938},[474,84976,84977],{"class":2277}," // do something with 'result'\n",[474,84979,84980],{"class":476,"line":944},[474,84981,29665],{"class":503},[474,84983,84984],{"class":476,"line":950},[474,84985,15357],{"class":503},[3938,84987,22526],{"id":22525},[439,84989,84990,84991,84994],{},"A question remains: ",[990,84992,84993],{},"when this is the basic skeleton of an application, how do i put this on an actual\ndevice?",[1002,84995,84998],{"href":84996,"rel":84997},"https://media.synyx.de/uploads//2010/08/jquery-html-css-example-e1281692547570.png",[1006],[2205,84999],{"alt":469,"src":84996},[439,85001,85002],{},"Example in Simulator",[439,85004,85005,85006,85009],{},"While this could be simply served by a web server to a mobile device, the main reason for developing applications\ninstead of websites is the ability to expose them through an application store (e.g. Apples AppStore or the Android\nMarket). We won’t go into much detail here now — this is something to be discussed in the following blog posts in this\nseries — a simple answer is provided by a small framework\nnamed ",[1002,85007,82883],{"href":76442,"rel":85008},[1006],". PhoneGap provides the developer with\na cross-platform deployment environment, which makes use of the web engines on the different mobile devices. It\nbasically is a fullscreen web browser view, without any UI elements and other decorations but with added support for\naccessing hardware features of the actual device. Next to the already mentioned features, which are part of the modern\nweb engines, it gives access to features like cameras, accelerometer or access to the native phonebook of your mobile\nphone. You simply drop all your HTML, CSS & JavaScript into a PhoneGap application package which is built for your\nparticular device and are ready to deploy through any application store or similar channel you like.",[3938,85011,85013],{"id":85012},"whats-next","What’s next?",[439,85015,85016],{},"So developing mobile applications with HTML, CSS & JavaScript is actually easy, isn’t it? Well, with the basic ideas\noutlined above it is — until it isn’t anymore. In the parts of this series which are still to come, we will answer some\nquestions, that might be crucial to your development cycle:",[994,85018,85019,85022,85025,85028],{},[997,85020,85021],{},"What problems might arise with JavaScript as a development language? Is there enough tool support for ease of\ndevelopment?",[997,85023,85024],{},"What do i have to program from scratch and which frameworks are available, that erase my need of boilerplate code?",[997,85026,85027],{},"What about the performance, for example when sophisticated animations are desired?",[997,85029,85030],{},"Is my application really cross-platform, when using these technologies, and does it behave exactly the same on every\ndevice?",[439,85032,85033],{},"Stay tuned for more.",[1024,85035,85036],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":469,"searchDepth":507,"depth":507,"links":85038},[85039,85040,85041,85042],{"id":83551,"depth":507,"text":83552},{"id":83558,"depth":507,"text":83559},{"id":22525,"depth":507,"text":22526},{"id":85012,"depth":507,"text":85013},[11122,1413],"2010-08-13T12:17:29","Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\\nseveral mobile platforms at once.","https://synyx.de/blog/on-cross-device-mobile-development-part-1/",{},"/blog/on-cross-device-mobile-development-part-1",{"title":83535,"description":85050},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.","blog/on-cross-device-mobile-development-part-1",[11132,22162,15041,78398,15201],"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team of developers might be small or knowledge about…","0fOf_YT_6c5o3h5pq67dvEk7owDHif1mmTz-IjeZrLI",{"id":85056,"title":85057,"author":85058,"body":85059,"category":85070,"date":85071,"description":469,"extension":1034,"link":85072,"meta":85073,"navigation":916,"path":85074,"seo":85075,"slug":85063,"stem":85076,"tags":85077,"teaser":85078,"__hash__":85079},"blog/blog/human-maier-we-are-in-beta.md","Human Maier! We are in Beta!",[223],{"type":432,"value":85060,"toc":85068},[85061,85064],[435,85062,85057],{"id":85063},"human-maier-we-are-in-beta",[439,85065,85066],{},[2205,85067],{"alt":78385,"src":82521},{"title":469,"searchDepth":507,"depth":507,"links":85069},[],[11122,3799],"2010-07-30T14:30:44","https://synyx.de/blog/human-maier-we-are-in-beta/",{},"/blog/human-maier-we-are-in-beta",{"title":85057,"description":469},"blog/human-maier-we-are-in-beta",[11132,63945,78398],"Our little pet project “I think I spider” is almost ready to be thrown out into the wild! That means we are running a closed beta for iOS devices for…","Tv3D01DCynO0dlp6K4oLCcEF6qiHcJkQCehI25ZSfc0",{"id":85081,"title":85082,"author":85083,"body":85084,"category":85392,"date":85393,"description":85394,"extension":1034,"link":85395,"meta":85396,"navigation":916,"path":85397,"seo":85398,"slug":85088,"stem":85400,"tags":85401,"teaser":85402,"__hash__":85403},"blog/blog/sending-apple-push-notifications-with-notnoops-java-apns-library.md","Sending Apple Push Notifications with notnoop's java-apns library",[190],{"type":432,"value":85085,"toc":85390},[85086,85089,85103,85111,85114,85121,85124,85385,85388],[435,85087,85082],{"id":85088},"sending-apple-push-notifications-with-notnoops-java-apns-library",[439,85090,85091,85092,85096,85097,85102],{},"If you need to send apple push notifications to your users, like we do in\na ",[1002,85093,85095],{"href":82591,"rel":85094},[1006],"secret project"," mentioned earlier this\nweek, ",[1002,85098,85101],{"href":85099,"rel":85100},"http://github.com/notnoop/java-apns",[1006],"notnoop’s java-apns library"," is a good choice, because its really simple to\nuse and saves you a lot of work.",[439,85104,85105,85106,17548],{},"(I presume that you already know how to get the Tokens of your users and already have a certificate for the push\nnotifications, I only show you how easy the server part can be with this library. If you don’t know, read this\nfirst: ",[1002,85107,85110],{"href":85108,"rel":85109},"http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html",[1006],"Apple Push Service",[439,85112,85113],{},"First you have to download the library (or add it in your maven dependencies):",[439,85115,85116],{},[1002,85117,85120],{"href":85118,"rel":85119},"http://github.com/notnoop/java-apns/tree/",[1006],"java-apns",[439,85122,85123],{},"The programming part isn’t that much so here’s how you do it:",[464,85125,85127],{"className":709,"code":85126,"language":711,"meta":469,"style":469},"\npublic void pushMessage() {\n ApnsService service = null;\n try {\n // get the certificate\n InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(\"your_certificate.p12\");\n service = APNS.newService().withCert(certStream, \"your_cert_password\").withSandboxDestination().build();\n // or\n // service = APNS.newService().withCert(certStream,\n // \"your_cert_password\").withProductionDestination().build();\n service.start();\n // You have to delete the devices from you list that no longer\n //have the app installed, see method below\n deleteInactiveDevices(service);\n // read your user list\n List\u003CUser> userList = userDao.readUsers();\n for (User user : userList) {\n try {\n // we had a daily update here, so we need to know how many\n //days the user hasn't started the app\n // so that we get the number of updates to display it as the badge.\n int days = (int) ((System.currentTimeMillis() - user.getLastUpdate()) / 1000 / 60 / 60 / 24);\n PayloadBuilder payloadBuilder = APNS.newPayload();\n payloadBuilder = payloadBuilder.badge(days).alertBody(\"some message you want to send here\");\n // check if the message is too long (it won't be sent if it is)\n //and trim it if it is.\n if (payloadBuilder.isTooLong()) {\n payloadBuilder = payloadBuilder.shrinkBody();\n }\n String payload = payloadBuilder.build();\n String token = user.getToken();\n service.push(token, payload);\n } catch (Exception ex) {\n // some logging stuff\n }\n }\n } catch (Exception ex) {\n // more logging\n } finally {\n // check if the service was successfull initialized and stop it here, if it was\n if (service != null) {\n service.stop();\n }\n }\n }\n private void deleteInactiveDevices(ApnsService service) {\n // get the list of the devices that no longer have your app installed from apple\n //ignore the =\"\" after Date here, it's a bug...\n Map\u003CString, Date> inactiveDevices = service.getInactiveDevices();\n for (String deviceToken : inactiveDevices.keySet()) {\n userDao.deleteByDeviceId(deviceToken);\n }\n }\n\n",[471,85128,85129,85133,85138,85143,85147,85152,85157,85162,85167,85172,85177,85182,85187,85192,85197,85202,85207,85212,85217,85222,85227,85232,85237,85242,85247,85252,85257,85262,85267,85272,85277,85282,85287,85292,85297,85302,85306,85311,85316,85320,85325,85330,85335,85339,85343,85347,85352,85357,85362,85367,85372,85377,85381],{"__ignoreMap":469},[474,85130,85131],{"class":476,"line":477},[474,85132,917],{"emptyLinePlaceholder":916},[474,85134,85135],{"class":476,"line":507},[474,85136,85137],{},"public void pushMessage() {\n",[474,85139,85140],{"class":476,"line":547},[474,85141,85142],{}," ApnsService service = null;\n",[474,85144,85145],{"class":476,"line":584},[474,85146,61985],{},[474,85148,85149],{"class":476,"line":607},[474,85150,85151],{}," // get the certificate\n",[474,85153,85154],{"class":476,"line":642},[474,85155,85156],{}," InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(\"your_certificate.p12\");\n",[474,85158,85159],{"class":476,"line":663},[474,85160,85161],{}," service = APNS.newService().withCert(certStream, \"your_cert_password\").withSandboxDestination().build();\n",[474,85163,85164],{"class":476,"line":694},[474,85165,85166],{}," // or\n",[474,85168,85169],{"class":476,"line":700},[474,85170,85171],{}," // service = APNS.newService().withCert(certStream,\n",[474,85173,85174],{"class":476,"line":913},[474,85175,85176],{}," // \"your_cert_password\").withProductionDestination().build();\n",[474,85178,85179],{"class":476,"line":920},[474,85180,85181],{}," service.start();\n",[474,85183,85184],{"class":476,"line":926},[474,85185,85186],{}," // You have to delete the devices from you list that no longer\n",[474,85188,85189],{"class":476,"line":932},[474,85190,85191],{}," //have the app installed, see method below\n",[474,85193,85194],{"class":476,"line":938},[474,85195,85196],{}," deleteInactiveDevices(service);\n",[474,85198,85199],{"class":476,"line":944},[474,85200,85201],{}," // read your user list\n",[474,85203,85204],{"class":476,"line":950},[474,85205,85206],{}," List\u003CUser> userList = userDao.readUsers();\n",[474,85208,85209],{"class":476,"line":956},[474,85210,85211],{}," for (User user : userList) {\n",[474,85213,85214],{"class":476,"line":962},[474,85215,85216],{}," try {\n",[474,85218,85219],{"class":476,"line":4876},[474,85220,85221],{}," // we had a daily update here, so we need to know how many\n",[474,85223,85224],{"class":476,"line":4888},[474,85225,85226],{}," //days the user hasn't started the app\n",[474,85228,85229],{"class":476,"line":4900},[474,85230,85231],{}," // so that we get the number of updates to display it as the badge.\n",[474,85233,85234],{"class":476,"line":4913},[474,85235,85236],{}," int days = (int) ((System.currentTimeMillis() - user.getLastUpdate()) / 1000 / 60 / 60 / 24);\n",[474,85238,85239],{"class":476,"line":4921},[474,85240,85241],{}," PayloadBuilder payloadBuilder = APNS.newPayload();\n",[474,85243,85244],{"class":476,"line":4932},[474,85245,85246],{}," payloadBuilder = payloadBuilder.badge(days).alertBody(\"some message you want to send here\");\n",[474,85248,85249],{"class":476,"line":4938},[474,85250,85251],{}," // check if the message is too long (it won't be sent if it is)\n",[474,85253,85254],{"class":476,"line":4946},[474,85255,85256],{}," //and trim it if it is.\n",[474,85258,85259],{"class":476,"line":4952},[474,85260,85261],{}," if (payloadBuilder.isTooLong()) {\n",[474,85263,85264],{"class":476,"line":4957},[474,85265,85266],{}," payloadBuilder = payloadBuilder.shrinkBody();\n",[474,85268,85269],{"class":476,"line":4969},[474,85270,85271],{}," }\n",[474,85273,85274],{"class":476,"line":4990},[474,85275,85276],{}," String payload = payloadBuilder.build();\n",[474,85278,85279],{"class":476,"line":5001},[474,85280,85281],{}," String token = user.getToken();\n",[474,85283,85284],{"class":476,"line":5013},[474,85285,85286],{}," service.push(token, payload);\n",[474,85288,85289],{"class":476,"line":5024},[474,85290,85291],{}," } catch (Exception ex) {\n",[474,85293,85294],{"class":476,"line":5035},[474,85295,85296],{}," // some logging stuff\n",[474,85298,85299],{"class":476,"line":5047},[474,85300,85301],{}," }\n",[474,85303,85304],{"class":476,"line":5055},[474,85305,42564],{},[474,85307,85308],{"class":476,"line":5062},[474,85309,85310],{}," } catch (Exception ex) {\n",[474,85312,85313],{"class":476,"line":5067},[474,85314,85315],{}," // more logging\n",[474,85317,85318],{"class":476,"line":5072},[474,85319,73459],{},[474,85321,85322],{"class":476,"line":5084},[474,85323,85324],{}," // check if the service was successfull initialized and stop it here, if it was\n",[474,85326,85327],{"class":476,"line":5100},[474,85328,85329],{}," if (service != null) {\n",[474,85331,85332],{"class":476,"line":5111},[474,85333,85334],{}," service.stop();\n",[474,85336,85337],{"class":476,"line":5122},[474,85338,42564],{},[474,85340,85341],{"class":476,"line":5134},[474,85342,5704],{},[474,85344,85345],{"class":476,"line":5145},[474,85346,1276],{},[474,85348,85349],{"class":476,"line":5156},[474,85350,85351],{}," private void deleteInactiveDevices(ApnsService service) {\n",[474,85353,85354],{"class":476,"line":5163},[474,85355,85356],{}," // get the list of the devices that no longer have your app installed from apple\n",[474,85358,85359],{"class":476,"line":5170},[474,85360,85361],{}," //ignore the =\"\" after Date here, it's a bug...\n",[474,85363,85364],{"class":476,"line":5175},[474,85365,85366],{}," Map\u003CString, Date> inactiveDevices = service.getInactiveDevices();\n",[474,85368,85369],{"class":476,"line":5180},[474,85370,85371],{}," for (String deviceToken : inactiveDevices.keySet()) {\n",[474,85373,85374],{"class":476,"line":5192},[474,85375,85376],{}," userDao.deleteByDeviceId(deviceToken);\n",[474,85378,85379],{"class":476,"line":5217},[474,85380,5704],{},[474,85382,85383],{"class":476,"line":5229},[474,85384,1276],{},[439,85386,85387],{},"Now wasn’t that an easy one this time?",[1024,85389,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":85391},[],[11122],"2010-07-27T07:00:38","If you need to send apple push notifications to your users, like we do in\\na secret project mentioned earlier this\\nweek, notnoop’s java-apns library is a good choice, because its really simple to\\nuse and saves you a lot of work.","https://synyx.de/blog/sending-apple-push-notifications-with-notnoops-java-apns-library/",{},"/blog/sending-apple-push-notifications-with-notnoops-java-apns-library",{"title":85082,"description":85399},"If you need to send apple push notifications to your users, like we do in\na secret project mentioned earlier this\nweek, notnoop’s java-apns library is a good choice, because its really simple to\nuse and saves you a lot of work.","blog/sending-apple-push-notifications-with-notnoops-java-apns-library",[82532,63945,78398,62094],"If you need to send apple push notifications to your users, like we do in a secret project mentioned earlier this week, notnoop’s java-apns library is a good choice, because…","mqOSmpufU8Sqpzy9fxCtmH5D6V8CLhPDD5kkCRFFWO8",{"id":85405,"title":77940,"author":85406,"body":85407,"category":85417,"date":85418,"description":469,"extension":1034,"link":85419,"meta":85420,"navigation":916,"path":85421,"seo":85422,"slug":82754,"stem":85423,"tags":85424,"teaser":85425,"__hash__":85426},"blog/blog/i-think-i-spider.md",[223],{"type":432,"value":85408,"toc":85415},[85409,85411],[435,85410,77940],{"id":82754},[439,85412,85413],{},[2205,85414],{"alt":78385,"src":82521},{"title":469,"searchDepth":507,"depth":507,"links":85416},[],[11122,3799],"2010-07-26T06:56:04","https://synyx.de/blog/i-think-i-spider/",{},"/blog/i-think-i-spider",{"title":77940,"description":469},"blog/i-think-i-spider",[11132,78398],"We are working on a fair number of Apps here at our mobile team and today we are proud to announce one of them: I think I spider! The “I…","Q4uy3XPVMpWwUGobhts0mEDqawiYKLMPBPP9qYTF_us",{"id":85428,"title":85429,"author":85430,"body":85431,"category":85539,"date":85540,"description":85541,"extension":1034,"link":85542,"meta":85543,"navigation":916,"path":85544,"seo":85545,"slug":85435,"stem":85546,"tags":85547,"teaser":85549,"__hash__":85550},"blog/blog/debugging-on-your-n900.md","Debugging on your N900",[109],{"type":432,"value":85432,"toc":85537},[85433,85436,85439,85442,85445,85448,85455,85458,85461,85464,85467,85470,85473,85478,85481,85484,85487,85490,85493,85496,85499,85502,85505,85508,85511,85514,85517,85527,85529],[435,85434,85429],{"id":85435},"debugging-on-your-n900",[439,85437,85438],{},"During development I realized that many device specific features are not available in the emulator. So I decided to find\na way to debug software in realtime on the device. First of all I had to upgrade my Device to version PR 1.2. So my\nSDK and the device libraries had the same version numbers.",[439,85440,85441],{},"I had to install the „maemo pc connectivity“ Package on my N900 and on my host pc.",[439,85443,85444],{},"First I enabled the developer repository on my N900:",[439,85446,85447],{},"`catalog name: extras-devel",[439,85449,85450,85451],{},"web address: ",[1002,85452,85453],{"href":85453,"rel":85454},"http://repository.maemo.org/extras-devel",[1006],[439,85456,85457],{},"distribution: fremantle",[439,85459,85460],{},"components: free non-free`",[439,85462,85463],{},"in a shell on the N900:",[439,85465,85466],{},"`sudo gainroot",[439,85468,85469],{},"apt-get install maemo-pc-connectivity gdb`",[439,85471,85472],{},"On my Host System I added the repository:",[439,85474,85475],{},[471,85476,85477],{},"deb http://pc-connectivity.garage.maemo.org/repository intrepid main",[439,85479,85480],{},"and installed host-pc-connectivity by typing:",[439,85482,85483],{},"`sudo apt-get update",[439,85485,85486],{},"sudo apt-get instsall host-pc-connectivity`",[439,85488,85489],{},"I had a new usb device with an IP address 192.168.2.14.",[439,85491,85492],{},"On my N900 I configured in the system-settings->pc connectivity manager the “default” environment:",[439,85494,85495],{},"`connection type: usb",[439,85497,85498],{},"ipadress: 192.168.2.15",[439,85500,85501],{},"gateway: 192.168.2.14`",[439,85503,85504],{},"After applying and saving the changes the N900 was ready. I connected it to my pc and opened an ssh shell to the device.\nI used the username and password I have been asked for while installation on the n900.",[439,85506,85507],{},"In ESBox i had to open the Debug Configurations Dialog and created a new Maemo Remote Application. Most points should be\nclear. The point Download is interesting: I used",[439,85509,85510],{},"`Download Method: ssh",[439,85512,85513],{},"RemoteConnection: 192.168.2.15`",[439,85515,85516],{},"Verify the target path. It must be a path that is writeable by the user you have used for the ssh session above. Klick\non „Edit“->the link SSH panel and configure the destination folder if necessary.",[439,85518,85519],{},[1002,85520,85523],{"href":85521,"rel":85522},"https://media.synyx.de/uploads//2010/07/maemo.jpeg",[1006],[2205,85524],{"alt":85525,"src":85526,"title":85525},"Debug Configuration - Download Method","https://media.synyx.de/uploads//2010/07/maemo-300x188.jpg",[439,85528,85525],{},[439,85530,85531,85532,85536],{},"That was all. The complete documentation of “pc connectivity” can be found here ",[1002,85533,85534],{"href":85534,"rel":85535},"http://pc",[1006],"\n-connectivity.garage.maemo.org/2nd_edition/index.html. This documentation describes howto use certificates to avoid\npassword fields during the launch process. This is not necessary, simply type the username and password once and check\nremember.",{"title":469,"searchDepth":507,"depth":507,"links":85538},[],[11122,1413],"2010-07-23T18:16:02","During development I realized that many device specific features are not available in the emulator. So I decided to find\\na way to debug software in realtime on the device. First of all I had to upgrade my Device to version PR 1.2. So my\\nSDK and the device libraries had the same version numbers.","https://synyx.de/blog/debugging-on-your-n900/",{},"/blog/debugging-on-your-n900",{"title":85429,"description":85438},"blog/debugging-on-your-n900",[85548],"maemo-5","During development I realized that many device specific features are not available in the emulator. So I decided to find a way to debug software in realtime on the device.…","1FB_9DCeDtAvd2O3wxpb16p_y5JWpsZOFXGzI6vAlJ4",{"id":85552,"title":77269,"author":85553,"body":85554,"category":85753,"date":85754,"description":85755,"extension":1034,"link":85756,"meta":85757,"navigation":916,"path":85758,"seo":85759,"slug":85558,"stem":85761,"tags":85762,"teaser":85766,"__hash__":85767},"blog/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app.md",[15],{"type":432,"value":85555,"toc":85751},[85556,85559,85566,85574,85577,85720,85727,85730,85733,85736,85739,85746,85749],[435,85557,77269],{"id":85558},"dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[439,85560,85561,85562,17548],{},"Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\nan OR-Mapper. (",[1002,85563,85564],{"href":85564,"rel":85565},"http://redmine.synyx.org/projects/show/hades",[1006],[439,85567,85568,85569,17548],{},"First I just followed the Spring tutorial which is quite simple and straight\nforward. (",[1002,85570,85573],{"href":85571,"rel":85572},"http://static.springsource.org/spring/docs/3%5C.0%5C.x/spring%5C-framework%5C-reference/html/validation.html%5C#validation%5C-beanvalidation%5C-spring",[1006],"http://static.springsource.org/spring/docs/3\\.0\\.x/spring\\-framework\\-reference/html/validation.html\\#validation\\-beanvalidation\\-spring",[439,85575,85576],{},"After a redeploy and writing an example bean and a test for it, it was just disappointing because nothing worked and the\nstacktrace was not really helping at a first look.",[464,85578,85580],{"className":16895,"code":85579,"language":16897,"meta":469,"style":469},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\njava.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\nat org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\nat org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\nat org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\nat org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\nat org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\nat org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\nat org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\nat org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\nat org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\nat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\nat java.lang.reflect.Method.invoke(Method.java:597)\nat org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\nat org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\nat org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\nat org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\nat org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\nat org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\nat org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\nat org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\nat org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\nat org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\nat org.apache.maven.surefire.Surefire.run(Surefire.java:177)\nat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\nat java.lang.reflect.Method.invoke(Method.java:597)\nat org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\nat org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n\n",[471,85581,85582,85587,85592,85597,85602,85607,85612,85617,85622,85627,85632,85637,85642,85647,85652,85657,85662,85667,85672,85677,85682,85687,85692,85697,85702,85706,85710,85715],{"__ignoreMap":469},[474,85583,85584],{"class":476,"line":477},[474,85585,85586],{},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\n",[474,85588,85589],{"class":476,"line":507},[474,85590,85591],{},"java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\n",[474,85593,85594],{"class":476,"line":547},[474,85595,85596],{},"at org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\n",[474,85598,85599],{"class":476,"line":584},[474,85600,85601],{},"at org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\n",[474,85603,85604],{"class":476,"line":607},[474,85605,85606],{},"at org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\n",[474,85608,85609],{"class":476,"line":642},[474,85610,85611],{},"at org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\n",[474,85613,85614],{"class":476,"line":663},[474,85615,85616],{},"at org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\n",[474,85618,85619],{"class":476,"line":694},[474,85620,85621],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\n",[474,85623,85624],{"class":476,"line":700},[474,85625,85626],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\n",[474,85628,85629],{"class":476,"line":913},[474,85630,85631],{},"at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\n",[474,85633,85634],{"class":476,"line":920},[474,85635,85636],{},"at org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\n",[474,85638,85639],{"class":476,"line":926},[474,85640,85641],{},"at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n",[474,85643,85644],{"class":476,"line":932},[474,85645,85646],{},"at java.lang.reflect.Method.invoke(Method.java:597)\n",[474,85648,85649],{"class":476,"line":938},[474,85650,85651],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\n",[474,85653,85654],{"class":476,"line":944},[474,85655,85656],{},"at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\n",[474,85658,85659],{"class":476,"line":950},[474,85660,85661],{},"at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\n",[474,85663,85664],{"class":476,"line":956},[474,85665,85666],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\n",[474,85668,85669],{"class":476,"line":962},[474,85670,85671],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\n",[474,85673,85674],{"class":476,"line":4876},[474,85675,85676],{},"at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\n",[474,85678,85679],{"class":476,"line":4888},[474,85680,85681],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\n",[474,85683,85684],{"class":476,"line":4900},[474,85685,85686],{},"at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\n",[474,85688,85689],{"class":476,"line":4913},[474,85690,85691],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\n",[474,85693,85694],{"class":476,"line":4921},[474,85695,85696],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\n",[474,85698,85699],{"class":476,"line":4932},[474,85700,85701],{},"at org.apache.maven.surefire.Surefire.run(Surefire.java:177)\n",[474,85703,85704],{"class":476,"line":4938},[474,85705,85641],{},[474,85707,85708],{"class":476,"line":4946},[474,85709,85646],{},[474,85711,85712],{"class":476,"line":4952},[474,85713,85714],{},"at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\n",[474,85716,85717],{"class":476,"line":4957},[474,85718,85719],{},"at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n",[439,85721,85722,85723,85726],{},"Why did I get an exception from a class called PersistenceUtil while just doing some validations via JSR303? Well, after\nreading the specification for JSR303 and some related blog entries, the solution is quite simple. JSR303 specification\nmanifests, that if a class PersistenceUtil is in the classpath, the JPATraversalResolver has to be integrated into the\nvalidation as well. If afterwards the method ",[990,85724,85725],{},"getPersistenceUtil()"," is getting called, its obvious that this method is\nnot available due to the fact that it is just a simple name matching.",[439,85728,85729],{},"Now I had THREE questions instead of one 🙂:",[439,85731,85732],{},"* Why is there a class called PersistenceUtil in my classpath while I DON’T use any JPA2 library (And this class is\nonly relevant for JPA2) – well this is surely just because the Hibernate guys had chosen the same name. Anyway, why the\nJPA2 guys used names like “PersistenceUtil” ????",[439,85734,85735],{},"* Why the specification manifests that if a class called PersistenceUtil is in classpath, there must be also a JPA\nvalidation?",[439,85737,85738],{},"* Why they do not additionally check against the needed method getPersistenceUtil() as well?",[439,85740,85741,85742,85745],{},"Anyway, as I needed a workaround, I checked the projects classpath… and I found my class ",[990,85743,85744],{},"PersistenceUtil"," but it was in\na jar called “ejb3-persistence-1.0.1GA.jar” – don’t ask me, why there also is a class called PersistenceUtil (btw. I\nhate ANY classes with *UTIL* in its name). I googled around a bit and found more people, who had the same problem and\nthe fix is really really easy. The guys who maintained the jar, fixed the problem due a little refactoring in version\n1.0.2GA.",[439,85747,85748],{},"So the only thing to do was to update this dependency in pom.xml to 1.0.2GA and everything went fine… Took me three\nhours to find out 🙁",[1024,85750,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":85752},[],[1031],"2010-07-20T16:10:45","Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\\nan OR-Mapper. (http://redmine.synyx.org/projects/show/hades)","https://synyx.de/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app/",{},"/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",{"title":77269,"description":85760},"Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\nan OR-Mapper. (http://redmine.synyx.org/projects/show/hades)","blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[85763,85764,85765,711,22988,389],"architecture","dependencyhell","dependencymanagement","Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE application. The application is built on Spring 3.0 and uses our Synyx Hades project, which…","XuDQA8M06fVBKiFLlJKRAWpz8k270QHpGRxVT7f354A",{"id":85769,"title":82565,"author":85770,"body":85771,"category":85851,"date":85852,"description":85853,"extension":1034,"link":85854,"meta":85855,"navigation":916,"path":85856,"seo":85857,"slug":85859,"stem":85860,"tags":85861,"teaser":85862,"__hash__":85863},"blog/blog/mobile-solutions-summary-4.md",[223],{"type":432,"value":85772,"toc":85849},[85773,85775,85787,85807,85815,85830,85835],[435,85774,82565],{"id":82571},[439,85776,85777,85778,85781,85782,85786],{},"There’s a lot going on over at the ",[1002,85779,82585],{"href":77932,"rel":85780},[1006],", so in case you are not subscribed to\nour feed, which I hope you are, you can grab it ",[1002,85783,22916],{"href":85784,"rel":85785},"http://mobile.synyx.de/feed/",[1006],". In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.",[439,85788,85789,85794,85795,85800,85801,85806],{},[1002,85790,85793],{"href":85791,"rel":85792},"http://mobile.synyx.de/authors/?uid=3",[1006],"Tobias’","\npost ",[1002,85796,85799],{"href":85797,"rel":85798},"http://mobile.synyx.de/2010/06/android-and-self-signed-ssl-certificates/",[1006],"“Android and self-signed ssl certificates”","\ngained a lot of attraction over the past couple of weeks. He basically brings you up to speed on how to tweak Android’s\nversion of ",[1002,85802,85805],{"href":85803,"rel":85804},"http://hc.apache.org/httpcomponents-client/httpclient/",[1006],"Apache Commons Http"," to work with your own\ncertificate:",[11947,85808,85809,85812],{},[439,85810,85811],{},"Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and\nlet android accept them.",[439,85813,85814],{},"But fortunately, there’s a workaround that uses an own SSLSocketFactory and an own TrustManager. With this, only your\nadded site is beeing able to be called, so theres no security issue.",[439,85816,85817,85818,85823,85824,85829],{},"Another blog post I’d like to point out is by yours truly,\non ",[1002,85819,85822],{"href":85820,"rel":85821},"http://mobile.synyx.de/2010/06/ui-prototyping-iphone-apps/",[1006],"“UI Prototyping iPhone Apps”",". It covers a very simple\nconcept and provides you with a ",[1002,85825,85828],{"href":85826,"rel":85827},"http://github.com/dlinsin/district9/tree/master/UIPrototyping/",[1006],"framework"," to employ it\nin your App development:",[11947,85831,85832],{},[439,85833,85834],{},"I watched a whole bunch of sessions from 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret\nVictor… In his session, Bret shows how to prototype an interface only by using with screenshots! … It inspired me to use\nhis framework and the whole process for our own development… Unfortunately, the code for the session isn’t available …\nAfter some digging, I found Michael Fey’s blog, who was able to successfully reverse engineer the missing parts, which\nwere not shown in the presentation.",[439,85836,85837,85838,85842,85843,85848],{},"I hope by this time you have already subscribed to our ",[1002,85839,85841],{"href":77932,"rel":85840},[1006],"mobile blog"," and discovered a couple\nof ",[1002,85844,85847],{"href":85845,"rel":85846},"http://mobile.synyx.de/2010/07/split-nsstring-by-characters/",[1006],"interesting posts",", that our team put together over\npast couple of months.",{"title":469,"searchDepth":507,"depth":507,"links":85850},[],[1030],"2010-07-16T07:28:03","There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","https://synyx.de/blog/mobile-solutions-summary-4/",{},"/blog/mobile-solutions-summary-4",{"title":82565,"description":85858},"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","mobile-solutions-summary-4","blog/mobile-solutions-summary-4",[11132,63945,78398,18496],"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to our feed, which I hope you are, you can grab it…","6e3hlP9lM0lmBm1BXb-OBwSJM2DFtSNp5TIpHdJ1OXE",{"id":85865,"title":85866,"author":85867,"body":85868,"category":85950,"date":85951,"description":85952,"extension":1034,"link":85953,"meta":85954,"navigation":916,"path":85955,"seo":85956,"slug":85872,"stem":85958,"tags":85959,"teaser":85960,"__hash__":85961},"blog/blog/split-nsstring-by-characters.md","Split NSString by characters",[223],{"type":432,"value":85869,"toc":85948},[85870,85873,85895,85904,85943,85946],[435,85871,85866],{"id":85872},"split-nsstring-by-characters",[439,85874,85875,85876,85883,85884,85886,85887,85894],{},"Have you ever pondered on the ",[1002,85877,85880],{"href":85878,"rel":85879},"http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html",[1006],[990,85881,85882],{},"NSString","\nclass reference and you were overwhelmed by the sheer amount of methods? I certainly have and due to the endless\npossibilities those methods give you, I just couldn’t figure out how to split an ",[990,85885,85882],{}," by characters and create a ",[1002,85888,85891],{"href":85889,"rel":85890},"http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html",[1006],[990,85892,85893],{},"NSArray","\nof those characters.",[439,85896,85897,85898,85903],{},"Blame it on my lack of creativity or on the fact that I’m not a C buff. However, thanks to my creative Googling, I came\nacross ",[1002,85899,85902],{"href":85900,"rel":85901},"http://www.idev101.com/code/Objective-C/Strings/split.html",[1006],"this site",", which shows you how to do it:",[464,85905,85907],{"className":83142,"code":85906,"language":83144,"meta":469,"style":469},"NSMutableArray *characters =\n[[NSMutableArray alloc] initWithCapacity:[myString length]];\nfor (int i=0; i \u003C [myString length]; i++) {\n NSString *ichar =\n[NSString stringWithFormat:@\"%c\", [myString characterAtIndex:i]];\n [characters addObject:ichar];\n}\n",[471,85908,85909,85914,85919,85924,85929,85934,85939],{"__ignoreMap":469},[474,85910,85911],{"class":476,"line":477},[474,85912,85913],{},"NSMutableArray *characters =\n",[474,85915,85916],{"class":476,"line":507},[474,85917,85918],{},"[[NSMutableArray alloc] initWithCapacity:[myString length]];\n",[474,85920,85921],{"class":476,"line":547},[474,85922,85923],{},"for (int i=0; i \u003C [myString length]; i++) {\n",[474,85925,85926],{"class":476,"line":584},[474,85927,85928],{}," NSString *ichar =\n",[474,85930,85931],{"class":476,"line":607},[474,85932,85933],{},"[NSString stringWithFormat:@\"%c\", [myString characterAtIndex:i]];\n",[474,85935,85936],{"class":476,"line":642},[474,85937,85938],{}," [characters addObject:ichar];\n",[474,85940,85941],{"class":476,"line":663},[474,85942,703],{},[439,85944,85945],{},"Although Apple’s documentation is extensive, you sometimes need a little hint or guidance to achieve your goal.",[1024,85947,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":85949},[],[11122,1413],"2010-07-11T11:33:48","Have you ever pondered on the NSString\\nclass reference and you were overwhelmed by the sheer amount of methods? I certainly have and due to the endless\\npossibilities those methods give you, I just couldn’t figure out how to split an NSString by characters and create a NSArray\\nof those characters.","https://synyx.de/blog/split-nsstring-by-characters/",{},"/blog/split-nsstring-by-characters",{"title":85866,"description":85957},"Have you ever pondered on the NSString\nclass reference and you were overwhelmed by the sheer amount of methods? I certainly have and due to the endless\npossibilities those methods give you, I just couldn’t figure out how to split an NSString by characters and create a NSArray\nof those characters.","blog/split-nsstring-by-characters",[63949],"Have you ever pondered on the NSString class reference and you were overwhelmed by the sheer amount of methods? I certainly have and due to the endless possibilities those methods…","ilLoGswdB2P1Kne210H_GnKvBa-ixSkTUAhBMe3b3TY",{"id":85963,"title":85964,"author":85965,"body":85966,"category":86048,"date":86049,"description":86050,"extension":1034,"link":86051,"meta":86052,"navigation":916,"path":86053,"seo":86054,"slug":85970,"stem":86056,"tags":86057,"teaser":86059,"__hash__":86060},"blog/blog/creating-a-mysql-dump-for-jdbc.md","Creating a MySQL dump for JDBC",[148],{"type":432,"value":85967,"toc":86046},[85968,85971,85980,85989,85992,85999,86027,86031,86034,86043],[435,85969,85964],{"id":85970},"creating-a-mysql-dump-for-jdbc",[439,85972,85973,85974,85979],{},"I have just been fighting with\nthe ",[1002,85975,85978],{"href":85976,"rel":85977},"https://web.archive.org/web/20150526005826/http://mojo.codehaus.org:80/sql-maven-plugin/",[1006],"sql-maven-plugin"," while\ntrying to import an OpenCms MySQL dump automatically. The module wasn’t really verbose with an error message:",[439,85981,85982,85985,85986],{},[471,85983,85984],{},"[ERROR] Failed to execute: INSERT INTO ","CMS_CONTENTS",[471,85987,85988],{}," VALUES",[439,85990,85991],{},"followed by printing the rest of the dump to the standard output.",[439,85993,85994,85995,85998],{},"Fortunately my colleague ",[1002,85996,71526],{"href":71524,"rel":85997},[1006]," pointed me to the right direction: The dump\ncontains some statements that can’t be executed by JDBC. To create a correct dump for usage with pure JDBC or with the\nsql-maven-plugin the following command has to be applied:",[464,86000,86002],{"className":54685,"code":86001,"language":54687,"meta":469,"style":469},"mysqldump --hex-blob -u root -p opencms752 > install/opencms752.sql\n\n",[471,86003,86004],{"__ignoreMap":469},[474,86005,86006,86008,86011,86014,86016,86019,86022,86024],{"class":476,"line":477},[474,86007,54667],{"class":480},[474,86009,86010],{"class":510}," --hex-blob",[474,86012,86013],{"class":510}," -u",[474,86015,29895],{"class":484},[474,86017,86018],{"class":510}," -p",[474,86020,86021],{"class":484}," opencms752",[474,86023,12487],{"class":810},[474,86025,86026],{"class":484}," install/opencms752.sql\n",[439,86028,86029],{},[448,86030,64272],{},[439,86032,86033],{},"To prevent that some characters are encoded incorrectly, add the following to the configuration options:",[464,86035,86037],{"className":6253,"code":86036,"language":6255,"meta":469,"style":469},"\u003CescapeProcessing>false\u003C/escapeProcessing>\n",[471,86038,86039],{"__ignoreMap":469},[474,86040,86041],{"class":476,"line":477},[474,86042,86036],{},[1024,86044,86045],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":469,"searchDepth":507,"depth":507,"links":86047},[],[1030],"2010-07-09T16:38:56","I have just been fighting with\\nthe sql-maven-plugin while\\ntrying to import an OpenCms MySQL dump automatically. The module wasn’t really verbose with an error message:","https://synyx.de/blog/creating-a-mysql-dump-for-jdbc/",{},"/blog/creating-a-mysql-dump-for-jdbc",{"title":85964,"description":86055},"I have just been fighting with\nthe sql-maven-plugin while\ntrying to import an OpenCms MySQL dump automatically. The module wasn’t really verbose with an error message:","blog/creating-a-mysql-dump-for-jdbc",[74149,22988,86058,64517],"mysql","I have just been fighting with the sql-maven-plugin while trying to import an OpenCms MySQL dump automatically. The module wasn’t really verbose with an error message: [ERROR] Failed to execute:…","utIfI3bzTXA2v77k7OBCmKNqGmfWzL5W5-UgX284X1A",{"id":86062,"title":86063,"author":86064,"body":86065,"category":86152,"date":86153,"description":86154,"extension":1034,"link":86155,"meta":86156,"navigation":916,"path":86157,"seo":86158,"slug":86069,"stem":86160,"tags":86161,"teaser":86162,"__hash__":86163},"blog/blog/servlet-container-options-for-maven.md","Servlet container options for Maven",[148],{"type":432,"value":86066,"toc":86150},[86067,86070,86083,86090,86098,86105,86112,86115,86142,86145,86148],[435,86068,86063],{"id":86069},"servlet-container-options-for-maven",[439,86071,86072,86073,86076,86077,86082],{},"When developing web apps with ",[1002,86074,66749],{"href":47203,"rel":86075},[1006]," the de facto standard for running the app is to use the\nexcellent ",[1002,86078,86081],{"href":86079,"rel":86080},"https://web.archive.org/web/20150520205353/https://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin",[1006],"Maven Jetty Plugin","\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.",[439,86084,86085,86086,86089],{},"But if the production system does not run on Jetty but on ",[1002,86087,54620],{"href":54618,"rel":86088},[1006]," you might run into some\nproblems:",[994,86091,86092,86095],{},[997,86093,86094],{},"Some redirects from AJAX calls do work on Jetty but do not work on Tomcat",[997,86096,86097],{},"When submitting some forms on Jetty the parameters get lost",[439,86099,86100,86101,86104],{},"The latter can be noticed when running ",[1002,86102,71236],{"href":80557,"rel":86103},[1006]," on Jetty: Saving from the editor causes an error\nbecause the parameters are not available to OpenCms.",[439,86106,86107,86108,86111],{},"Fortunately there is a viable alternative:\nThe ",[1002,86109,73716],{"href":73714,"rel":86110},[1006],".\nBesides providing several options to deploy artifacts to an external server it can also be run in an embedded mode.",[439,86113,86114],{},"This is how the plugin is configured:",[464,86116,86118],{"className":6253,"code":86117,"language":6255,"meta":469,"style":469},"\n\u003Cplugin>\n \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n\u003C/plugin>\n\n",[471,86119,86120,86124,86128,86133,86138],{"__ignoreMap":469},[474,86121,86122],{"class":476,"line":477},[474,86123,917],{"emptyLinePlaceholder":916},[474,86125,86126],{"class":476,"line":507},[474,86127,44754],{},[474,86129,86130],{"class":476,"line":547},[474,86131,86132],{}," \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n",[474,86134,86135],{"class":476,"line":584},[474,86136,86137],{}," \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n",[474,86139,86140],{"class":476,"line":607},[474,86141,44834],{},[439,86143,86144],{},"It can be run using mvn tomcat:run and mvn tomcat:run-war for running in unpacked war mode.",[439,86146,86147],{},"Using this plugin you can be sure that the features your servlet container provides in production are the same during\ndevelopment.",[1024,86149,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":86151},[],[1030],"2010-07-09T08:48:36","When developing web apps with Maven the de facto standard for running the app is to use the\\nexcellent Maven Jetty Plugin\\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.","https://synyx.de/blog/servlet-container-options-for-maven/",{},"/blog/servlet-container-options-for-maven",{"title":86063,"description":86159},"When developing web apps with Maven the de facto standard for running the app is to use the\nexcellent Maven Jetty Plugin\nwhich runs the project in an embedded Jetty server. When configured, it can either run the project from the war file\ndirectly via mvn jetty:run or in exploded mode where the war is unpacked before being run (mvn jetty:run-exploded).\nThis noticably speeds up development as there is no need to manually deploy the artifact to a server.","blog/servlet-container-options-for-maven",[15546,711,45218,22988,51154],"When developing web apps with Maven the de facto standard for running the app is to use the excellent Maven Jetty Plugin which runs the project in an embedded Jetty…","fRYI1HRQAdsyYeA7rnRcKDEpC-Azc63aZtMg04FdDvU",{"id":86165,"title":86166,"author":86167,"body":86168,"category":86405,"date":86406,"description":86407,"extension":1034,"link":86408,"meta":86409,"navigation":916,"path":86410,"seo":86411,"slug":86172,"stem":86413,"tags":86414,"teaser":86415,"__hash__":86416},"blog/blog/ui-prototyping-iphone-apps.md","UI Prototyping iPhone Apps",[223],{"type":432,"value":86169,"toc":86403},[86170,86173,86181,86184,86193,86204,86257,86267,86389,86392,86395,86401],[435,86171,86166],{"id":86172},"ui-prototyping-iphone-apps",[439,86174,86175,86176,86180],{},"Before ",[1002,86177,86179],{"href":78707,"rel":86178},[1006],"flying off to WWDC"," last month, I watched a whole bunch of sessions\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\nyou’ve got access to the WWDC videos – stop right here and watch the video!",[439,86182,86183],{},"In his session, Bret shows how to prototype an interface only by using with screenshots! It’s amazing that a simple\nscreenshot on the device can show you so much more than by just looking at it in a document or print out. It inspired me\nto use his framework and the whole process for our own development.",[439,86185,86186,86187,86192],{},"Unfortunately, the code for the session isn’t available and neither Bret nor the frameworks evangelist mentioned in the\npresentation got back to me about the code. After some digging, I\nfound ",[1002,86188,86191],{"href":86189,"rel":86190},"https://web.archive.org/web/20130723100234/http://www.fruitstandsoftware.com:80/blog/2009/07/uiview-manipulation-made-easier-with-a-category/",[1006],"Michael Fey’s blog",",\nwho was able to successfully reverse engineer the missing parts, which were not shown in the presentation.",[439,86194,86195,86196,86199,86200,86203],{},"Michael’s ",[990,86197,86198],{},"UIViewAdditions"," basically allow easy access to frame properties and give you a neat init method, which adds\nthe passed ",[990,86201,86202],{},"UIView"," as a parent:",[464,86205,86207],{"className":83142,"code":86206,"language":83144,"meta":469,"style":469},"- (id)initWithParent:(UIView *)parent {\n self = [self initWithFrame:CGRectZero];\n if (!self)\n return nil;\n [parent addSubview:self];\n return self;\n}\n+ (id) viewWithParent:(UIView *)parent {\n return [[[self alloc] initWithParent:parent] autorelease];\n}\n",[471,86208,86209,86214,86219,86224,86229,86234,86239,86243,86248,86253],{"__ignoreMap":469},[474,86210,86211],{"class":476,"line":477},[474,86212,86213],{},"- (id)initWithParent:(UIView *)parent {\n",[474,86215,86216],{"class":476,"line":507},[474,86217,86218],{}," self = [self initWithFrame:CGRectZero];\n",[474,86220,86221],{"class":476,"line":547},[474,86222,86223],{}," if (!self)\n",[474,86225,86226],{"class":476,"line":584},[474,86227,86228],{}," return nil;\n",[474,86230,86231],{"class":476,"line":607},[474,86232,86233],{}," [parent addSubview:self];\n",[474,86235,86236],{"class":476,"line":642},[474,86237,86238],{}," return self;\n",[474,86240,86241],{"class":476,"line":663},[474,86242,703],{},[474,86244,86245],{"class":476,"line":694},[474,86246,86247],{},"+ (id) viewWithParent:(UIView *)parent {\n",[474,86249,86250],{"class":476,"line":700},[474,86251,86252],{}," return [[[self alloc] initWithParent:parent] autorelease];\n",[474,86254,86255],{"class":476,"line":913},[474,86256,703],{},[439,86258,86259,86260,86263,86264,86266],{},"There wasn’t much left to do for me. I only coded the class ",[990,86261,86262],{},"Root",", which is the parent of all ",[990,86265,78927],{}," instances\nused in the prototype. It provides a couple of methods to slide images back and forth:",[464,86268,86270],{"className":83142,"code":86269,"language":83144,"meta":469,"style":469},"@synthesize pageIndex = _pageIndex;\n- (id) initWithParent:(UIView *)parent {\n self = [super initWithParent:parent];\n if (self == nil) {\n return nil;\n }\n self.userInteractionEnabled = YES;\n self.size = self.window.size;\n [[UIImageView viewWithParent:self] setImageWithName:@\"dailies\"];\n self.pageIndex = 0;\n return self;\n}\n- (void)setPageIndex:(int)index {\n if (index \u003C 0 || index >= [self.subviews count]) {\n return;\n }\n _pageIndex = index;\n for (int i = 0; i \u003C [self.subviews count]; i++) {\n UIImageView *page = [self.subviews objectAtIndex:i];\n page.x = (i \u003C _pageIndex) ? -self.width : (i > _pageIndex) ? self.width : 0;\n }\n}\n- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {\n self.pageIndex++;\n}\n",[471,86271,86272,86277,86282,86287,86292,86296,86300,86305,86310,86315,86320,86324,86328,86333,86338,86343,86347,86352,86357,86362,86367,86371,86375,86380,86385],{"__ignoreMap":469},[474,86273,86274],{"class":476,"line":477},[474,86275,86276],{},"@synthesize pageIndex = _pageIndex;\n",[474,86278,86279],{"class":476,"line":507},[474,86280,86281],{},"- (id) initWithParent:(UIView *)parent {\n",[474,86283,86284],{"class":476,"line":547},[474,86285,86286],{}," self = [super initWithParent:parent];\n",[474,86288,86289],{"class":476,"line":584},[474,86290,86291],{}," if (self == nil) {\n",[474,86293,86294],{"class":476,"line":607},[474,86295,86228],{},[474,86297,86298],{"class":476,"line":642},[474,86299,15319],{},[474,86301,86302],{"class":476,"line":663},[474,86303,86304],{}," self.userInteractionEnabled = YES;\n",[474,86306,86307],{"class":476,"line":694},[474,86308,86309],{}," self.size = self.window.size;\n",[474,86311,86312],{"class":476,"line":700},[474,86313,86314],{}," [[UIImageView viewWithParent:self] setImageWithName:@\"dailies\"];\n",[474,86316,86317],{"class":476,"line":913},[474,86318,86319],{}," self.pageIndex = 0;\n",[474,86321,86322],{"class":476,"line":920},[474,86323,86238],{},[474,86325,86326],{"class":476,"line":926},[474,86327,703],{},[474,86329,86330],{"class":476,"line":932},[474,86331,86332],{},"- (void)setPageIndex:(int)index {\n",[474,86334,86335],{"class":476,"line":938},[474,86336,86337],{}," if (index \u003C 0 || index >= [self.subviews count]) {\n",[474,86339,86340],{"class":476,"line":944},[474,86341,86342],{}," return;\n",[474,86344,86345],{"class":476,"line":950},[474,86346,15319],{},[474,86348,86349],{"class":476,"line":956},[474,86350,86351],{}," _pageIndex = index;\n",[474,86353,86354],{"class":476,"line":962},[474,86355,86356],{}," for (int i = 0; i \u003C [self.subviews count]; i++) {\n",[474,86358,86359],{"class":476,"line":4876},[474,86360,86361],{}," UIImageView *page = [self.subviews objectAtIndex:i];\n",[474,86363,86364],{"class":476,"line":4888},[474,86365,86366],{}," page.x = (i \u003C _pageIndex) ? -self.width : (i > _pageIndex) ? self.width : 0;\n",[474,86368,86369],{"class":476,"line":4900},[474,86370,15319],{},[474,86372,86373],{"class":476,"line":4913},[474,86374,703],{},[474,86376,86377],{"class":476,"line":4921},[474,86378,86379],{},"- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {\n",[474,86381,86382],{"class":476,"line":4932},[474,86383,86384],{}," self.pageIndex++;\n",[474,86386,86387],{"class":476,"line":4938},[474,86388,703],{},[439,86390,86391],{},"With those two classes and a couple of screenshots, it is fairly easy to create an App that looks and feels almost real.\nI created a short demo video, which shows how easy it is to get a good feeling if your App is going to work or not:",[439,86393,86394],{},"Now don’t forget those are only screenshots and the App might need to load stuff over the network or do some animation,\nhence it might not feel exactly the same. However, this process of prototyping an UI is powerful enough to give you an\nidea, whether the workflow or the UI in general is going to “work” or needs some tweaking.",[439,86396,86397,86398,1402],{},"You can download the source code for the two classes, along with a sample project\nfrom ",[1002,86399,22987],{"href":85826,"rel":86400},[1006],[1024,86402,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":86404},[],[11122,1413],"2010-06-29T06:46:00","Before flying off to WWDC last month, I watched a whole bunch of sessions\\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\\nyou’ve got access to the WWDC videos – stop right here and watch the video!","https://synyx.de/blog/ui-prototyping-iphone-apps/",{},"/blog/ui-prototyping-iphone-apps",{"title":86166,"description":86412},"Before flying off to WWDC last month, I watched a whole bunch of sessions\nfrom 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you haven’t watched it and\nyou’ve got access to the WWDC videos – stop right here and watch the video!","blog/ui-prototyping-iphone-apps",[63945,18911,78398,63949,21702],"Before flying off to WWDC last month, I watched a whole bunch of sessions from 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret Victor. If you…","hEZiAfMXrZSp1Im2rsk88x4HQuKpzIhV5W2xo11faBA",{"id":86418,"title":86419,"author":86420,"body":86421,"category":86538,"date":86539,"description":86428,"extension":1034,"link":86540,"meta":86541,"navigation":916,"path":86542,"seo":86543,"slug":86544,"stem":86545,"tags":86546,"teaser":86547,"__hash__":86548},"blog/blog/betriebspraktikum-die-nachste.md","Betriebspraktikum, die nächste…..",[109],{"type":432,"value":86422,"toc":86536},[86423,86426,86429,86432,86435,86438,86446,86449,86452,86455,86458,86461,86464,86467,86470,86473,86476,86479,86482,86485,86488,86491,86494,86497,86500,86503,86506,86509,86512,86515,86518,86521,86524,86527,86530,86533],[435,86424,86419],{"id":86425},"betriebspraktikum-die-nächste",[439,86427,86428],{},"Hallo zusammen,",[439,86430,86431],{},"ich heiße Steffen Welp und habe heute den letzten",[439,86433,86434],{},"Tag meines 14-tägigen Praktikums als Fachinformatiker",[439,86436,86437],{},"für Systemintegration.",[439,86439,86440,86441],{},"Der Kontakt kam durch meine Bewerbung beim ",[1002,86442,86445],{"href":86443,"rel":86444},"http://cyberforum.de/",[1006],"Cyberforum e.V.",[439,86447,86448],{},"in Karlsruhe zustande. Dieser hatte zum Meet`n Greet zwischen",[439,86450,86451],{},"Bewerbern und ausbildenden Unternehmen eingeladen.",[439,86453,86454],{},"Dabei lernte ich Herrn Ferstl kennen, welcher mich spontan",[439,86456,86457],{},"zu einem weiteren Gespräch in den Geschäftsräumen der",[439,86459,86460],{},"Firma einlud. Bei diesem zweiten Treffen wurde dann der",[439,86462,86463],{},"Praktikumstermin vereinbart.",[439,86465,86466],{},"In den letzten beiden Wochen hatte ich nun das Glück",[439,86468,86469],{},"dem Treiben in der Firma beiwohnen zu dürfen und bin nachhaltig",[439,86471,86472],{},"schwer begeistert.",[439,86474,86475],{},"Zu Beginn wurde ich mit den Basics des Shellbedienung und",[439,86477,86478],{},"auch des -programmierens innerhalb von Linux vertraut gemacht.",[439,86480,86481],{},"Dies habe ich auch im Eigenstudium dann bis heute weitergeführt,",[439,86483,86484],{},"und werde es wohl auch privat fortsetzen.",[439,86486,86487],{},"Als weiteres konnte ich meinen beiden Kollegen Herrn Ferstl und",[439,86489,86490],{},"Herrn Ruessel täglich über die Schulter schauen. Beide waren",[439,86492,86493],{},"immer hilfsbereit und haben mir bei Schwierigkeiten stets unter",[439,86495,86496],{},"die Arme gegriffen. Abschließend stand in der zweiten Woche noch",[439,86498,86499],{},"die Wartung der Hardware an, damit diese an warmen Sommertagen",[439,86501,86502],{},"auch weiterhin zuverlässig arbeitet.",[439,86504,86505],{},"Vielen Dank an alle Mitarbeiter und -streiter der Synyx GmbH",[439,86507,86508],{},"für die freundliche Aufnahme und der Hilfestellung bei allen",[439,86510,86511],{},"wichtigen Fragen!",[439,86513,86514],{},"Diese 14 Tage haben mich in der Wahl meines gewünschten Berufs-",[439,86516,86517],{},"bildes bestätigt und ich würde mich sehr freuen, dürfte ich mit",[439,86519,86520],{},"ins Boot springen und im September hier eine Umschulung beginnen!",[439,86522,86523],{},"Ich drück der Synyx GmbH ganz fest die Daumen, daß ihr Konzept",[439,86525,86526],{},"weiterhin aufgehen möge und sie der IT-Welt noch lange erhalten",[439,86528,86529],{},"bleiben.",[439,86531,86532],{},"Gruß",[439,86534,86535],{},"Steffen Welp",{"title":469,"searchDepth":507,"depth":507,"links":86537},[],[13208],"2010-06-24T19:18:02","https://synyx.de/blog/betriebspraktikum-die-nachste/",{},"/blog/betriebspraktikum-die-nachste",{"title":86419,"description":86428},"betriebspraktikum-die-nachste","blog/betriebspraktikum-die-nachste",[],"Hallo zusammen, ich heiße Steffen Welp und habe heute den letzten Tag meines 14-tägigen Praktikums als Fachinformatiker für Systemintegration. Der Kontakt kam durch meine Bewerbung beim Cyberforum e.V. in Karlsruhe…","nlVhNewBHrUOxxvshGyRF4Di5V3AuzROWQe9qCqwHE0",{"id":86550,"title":86551,"author":86552,"body":86553,"category":86575,"date":86576,"description":86560,"extension":1034,"link":86577,"meta":86578,"navigation":916,"path":86579,"seo":86580,"slug":86557,"stem":86581,"tags":86582,"teaser":86583,"__hash__":86584},"blog/blog/jetzt-ist-es-nicht-mehr-weit.md","Jetzt ist es nicht mehr weit",[314],{"type":432,"value":86554,"toc":86573},[86555,86558,86561,86564,86567,86570],[435,86556,86551],{"id":86557},"jetzt-ist-es-nicht-mehr-weit",[439,86559,86560],{},"… bis zum Ende meiner dreijährigen Ausbildung zum Fachinformatiker Systemintegration.",[439,86562,86563],{},"Gestern durfte ich im Rahmen einer feierlichen Abschlusszeremonie in der Heinrich-Hertz-Schule, mit live-Musik und\nmehr oder weniger spannenden Reden von verschiedenen Vertretern der Schule, der IHK, und Ausbildungsbetrieben, mein\nAbschlusszeugnis, welches das Bestehen der Abschlussprüfung bescheinigt, entgegen nehmen. *freu*",[439,86565,86566],{},"Einen kleinen Wermutstropfen birgt die Sache dann doch, das war nun das allerletzte mal (offiziell zumindest) mit den\nJungs aus meiner Klasse und den Lehrern und Lehrerinnen der HHS, ja es gab/gibt einige die man wirklich vermisst. Ich\nwerde noch lange Geschichten erzählen können, die mit “Und einmal in der Berufsschule…” anfangen.",[439,86568,86569],{},"Aber jetzt heißt es noch einmal “Daumen drücken”, am 22. Juli findet die Abschlusspräsentation meines\nAbschlussprojektes vor der IHK und ein anschliessendes Fachgespräch (man wird gelöchert ob man sich denn wirklich mit\nder Materie befasst hat) statt.",[439,86571,86572],{},"Anschließend werde ich aller Voraussicht nach das Team Synyx (Fußball-Metaphern sind derzeit so modisch)auch in\nZukunft, dann aber als vollwertiger Informatiker, tatkräftig unterstützen.",{"title":469,"searchDepth":507,"depth":507,"links":86574},[],[13208],"2010-06-24T17:59:37","https://synyx.de/blog/jetzt-ist-es-nicht-mehr-weit/",{},"/blog/jetzt-ist-es-nicht-mehr-weit",{"title":86551,"description":86560},"blog/jetzt-ist-es-nicht-mehr-weit",[13219,71055,389],"… bis zum Ende meiner dreijährigen Ausbildung zum Fachinformatiker Systemintegration. Gestern durfte ich im Rahmen einer feierlichen Abschlusszeremonie in der Heinrich-Hertz-Schule, mit live-Musik und mehr oder weniger spannenden Reden von…","UyFiIeyV-p0kWARkAOAAIn_nCa7ZY_VwcacY5ojm_3A",{"id":86586,"title":86587,"author":86588,"body":86589,"category":87678,"date":87679,"description":87680,"extension":1034,"link":87681,"meta":87682,"navigation":916,"path":87683,"seo":87684,"slug":86593,"stem":87685,"tags":87686,"teaser":87692,"__hash__":87693},"blog/blog/android-and-self-signed-ssl-certificates.md","Android and self-signed ssl certificates",[190],{"type":432,"value":86590,"toc":87676},[86591,86594,86597,86599,86602,87127,87130,87445,87454,87457,87486,87628,87671,87674],[435,86592,86587],{"id":86593},"android-and-self-signed-ssl-certificates",[439,86595,86596],{},"Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let\nandroid accept them.",[439,86598,85814],{},[439,86600,86601],{},"First you have to create the SSLFactory:",[464,86603,86605],{"className":709,"code":86604,"language":711,"meta":469,"style":469},"\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.net.UnknownHostException;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLSocket;\nimport javax.net.ssl.TrustManager;\nimport org.apache.http.conn.ConnectTimeoutException;\nimport org.apache.http.conn.scheme.LayeredSocketFactory;\nimport org.apache.http.conn.scheme.SocketFactory;\nimport org.apache.http.params.HttpConnectionParams;\nimport org.apache.http.params.HttpParams;\n/**\n * This socket factory will create ssl socket that accepts self signed certificate\n *\n * @author olamy\n * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $\n * @since 1.2.3\n */\npublic class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory {\n private SSLContext sslcontext = null;\n private static SSLContext createEasySSLContext() throws IOException {\n try {\n SSLContext context = SSLContext.getInstance(\"TLS\");\n context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);\n return context;\n } catch (Exception e) {\n throw new IOException(e.getMessage());\n }\n }\n private SSLContext getSSLContext() throws IOException {\n if (this.sslcontext == null) {\n this.sslcontext = createEasySSLContext();\n }\n return this.sslcontext;\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,\n * java.net.InetAddress, int, org.apache.http.params.HttpParams)\n */\n public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort,\n HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {\n int connTimeout = HttpConnectionParams.getConnectionTimeout(params);\n int soTimeout = HttpConnectionParams.getSoTimeout(params);\n InetSocketAddress remoteAddress = new InetSocketAddress(host, port);\n SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());\n if ((localAddress != null) || (localPort > 0)) {\n // we need to bind explicitly\n if (localPort \u003C 0) {\n localPort = 0; // indicates \"any\"\n }\n InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);\n sslsock.bind(isa);\n }\n sslsock.connect(remoteAddress, connTimeout);\n sslsock.setSoTimeout(soTimeout);\n return sslsock;\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#createSocket()\n */\n public Socket createSocket() throws IOException {\n return getSSLContext().getSocketFactory().createSocket();\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)\n */\n public boolean isSecure(Socket socket) throws IllegalArgumentException {\n return true;\n }\n /**\n * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,\n * boolean)\n */\n public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,\n UnknownHostException {\n return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);\n }\n // -------------------------------------------------------------------\n // javadoc in org.apache.http.conn.scheme.SocketFactory says :\n // Both Object.equals() and Object.hashCode() must be overridden\n // for the correct operation of some connection managers\n // -------------------------------------------------------------------\n public boolean equals(Object obj) {\n return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));\n }\n public int hashCode() {\n return EasySSLSocketFactory.class.hashCode();\n }\n}\n\n",[471,86606,86607,86611,86616,86621,86626,86631,86636,86641,86646,86651,86656,86661,86665,86670,86675,86680,86685,86690,86695,86699,86703,86708,86713,86718,86723,86728,86733,86738,86743,86748,86753,86758,86763,86767,86772,86776,86781,86786,86791,86795,86800,86805,86810,86814,86819,86824,86829,86834,86839,86843,86847,86852,86857,86862,86866,86871,86875,86879,86884,86889,86894,86899,86904,86909,86914,86919,86924,86929,86934,86939,86944,86948,86953,86958,86962,86967,86972,86977,86981,86985,86990,86994,86999,87004,87008,87012,87017,87021,87026,87030,87034,87038,87043,87048,87052,87057,87062,87067,87071,87076,87081,87086,87091,87095,87100,87105,87109,87114,87119,87123],{"__ignoreMap":469},[474,86608,86609],{"class":476,"line":477},[474,86610,917],{"emptyLinePlaceholder":916},[474,86612,86613],{"class":476,"line":507},[474,86614,86615],{},"/*\n",[474,86617,86618],{"class":476,"line":547},[474,86619,86620],{}," * Licensed to the Apache Software Foundation (ASF) under one\n",[474,86622,86623],{"class":476,"line":584},[474,86624,86625],{}," * or more contributor license agreements. See the NOTICE file\n",[474,86627,86628],{"class":476,"line":607},[474,86629,86630],{}," * distributed with this work for additional information\n",[474,86632,86633],{"class":476,"line":642},[474,86634,86635],{}," * regarding copyright ownership. The ASF licenses this file\n",[474,86637,86638],{"class":476,"line":663},[474,86639,86640],{}," * to you under the Apache License, Version 2.0 (the\n",[474,86642,86643],{"class":476,"line":694},[474,86644,86645],{}," * \"License\"); you may not use this file except in compliance\n",[474,86647,86648],{"class":476,"line":700},[474,86649,86650],{}," * with the License. You may obtain a copy of the License at\n",[474,86652,86653],{"class":476,"line":913},[474,86654,86655],{}," *\n",[474,86657,86658],{"class":476,"line":920},[474,86659,86660],{}," * http://www.apache.org/licenses/LICENSE-2.0\n",[474,86662,86663],{"class":476,"line":926},[474,86664,86655],{},[474,86666,86667],{"class":476,"line":932},[474,86668,86669],{}," * Unless required by applicable law or agreed to in writing,\n",[474,86671,86672],{"class":476,"line":938},[474,86673,86674],{}," * software distributed under the License is distributed on an\n",[474,86676,86677],{"class":476,"line":944},[474,86678,86679],{}," * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n",[474,86681,86682],{"class":476,"line":950},[474,86683,86684],{}," * KIND, either express or implied. See the License for the\n",[474,86686,86687],{"class":476,"line":956},[474,86688,86689],{}," * specific language governing permissions and limitations\n",[474,86691,86692],{"class":476,"line":962},[474,86693,86694],{}," * under the License.\n",[474,86696,86697],{"class":476,"line":4876},[474,86698,73845],{},[474,86700,86701],{"class":476,"line":4888},[474,86702,80140],{},[474,86704,86705],{"class":476,"line":4900},[474,86706,86707],{},"import java.net.InetAddress;\n",[474,86709,86710],{"class":476,"line":4913},[474,86711,86712],{},"import java.net.InetSocketAddress;\n",[474,86714,86715],{"class":476,"line":4921},[474,86716,86717],{},"import java.net.Socket;\n",[474,86719,86720],{"class":476,"line":4932},[474,86721,86722],{},"import java.net.UnknownHostException;\n",[474,86724,86725],{"class":476,"line":4938},[474,86726,86727],{},"import javax.net.ssl.SSLContext;\n",[474,86729,86730],{"class":476,"line":4946},[474,86731,86732],{},"import javax.net.ssl.SSLSocket;\n",[474,86734,86735],{"class":476,"line":4952},[474,86736,86737],{},"import javax.net.ssl.TrustManager;\n",[474,86739,86740],{"class":476,"line":4957},[474,86741,86742],{},"import org.apache.http.conn.ConnectTimeoutException;\n",[474,86744,86745],{"class":476,"line":4969},[474,86746,86747],{},"import org.apache.http.conn.scheme.LayeredSocketFactory;\n",[474,86749,86750],{"class":476,"line":4990},[474,86751,86752],{},"import org.apache.http.conn.scheme.SocketFactory;\n",[474,86754,86755],{"class":476,"line":5001},[474,86756,86757],{},"import org.apache.http.params.HttpConnectionParams;\n",[474,86759,86760],{"class":476,"line":5013},[474,86761,86762],{},"import org.apache.http.params.HttpParams;\n",[474,86764,86765],{"class":476,"line":5024},[474,86766,73830],{},[474,86768,86769],{"class":476,"line":5035},[474,86770,86771],{}," * This socket factory will create ssl socket that accepts self signed certificate\n",[474,86773,86774],{"class":476,"line":5047},[474,86775,86655],{},[474,86777,86778],{"class":476,"line":5055},[474,86779,86780],{}," * @author olamy\n",[474,86782,86783],{"class":476,"line":5062},[474,86784,86785],{}," * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $\n",[474,86787,86788],{"class":476,"line":5067},[474,86789,86790],{}," * @since 1.2.3\n",[474,86792,86793],{"class":476,"line":5072},[474,86794,73845],{},[474,86796,86797],{"class":476,"line":5084},[474,86798,86799],{},"public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory {\n",[474,86801,86802],{"class":476,"line":5100},[474,86803,86804],{}," private SSLContext sslcontext = null;\n",[474,86806,86807],{"class":476,"line":5111},[474,86808,86809],{}," private static SSLContext createEasySSLContext() throws IOException {\n",[474,86811,86812],{"class":476,"line":5122},[474,86813,61985],{},[474,86815,86816],{"class":476,"line":5134},[474,86817,86818],{}," SSLContext context = SSLContext.getInstance(\"TLS\");\n",[474,86820,86821],{"class":476,"line":5145},[474,86822,86823],{}," context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);\n",[474,86825,86826],{"class":476,"line":5156},[474,86827,86828],{}," return context;\n",[474,86830,86831],{"class":476,"line":5163},[474,86832,86833],{}," } catch (Exception e) {\n",[474,86835,86836],{"class":476,"line":5170},[474,86837,86838],{}," throw new IOException(e.getMessage());\n",[474,86840,86841],{"class":476,"line":5175},[474,86842,5704],{},[474,86844,86845],{"class":476,"line":5180},[474,86846,1276],{},[474,86848,86849],{"class":476,"line":5192},[474,86850,86851],{}," private SSLContext getSSLContext() throws IOException {\n",[474,86853,86854],{"class":476,"line":5217},[474,86855,86856],{}," if (this.sslcontext == null) {\n",[474,86858,86859],{"class":476,"line":5229},[474,86860,86861],{}," this.sslcontext = createEasySSLContext();\n",[474,86863,86864],{"class":476,"line":5240},[474,86865,5704],{},[474,86867,86868],{"class":476,"line":5251},[474,86869,86870],{}," return this.sslcontext;\n",[474,86872,86873],{"class":476,"line":5262},[474,86874,1276],{},[474,86876,86877],{"class":476,"line":5274},[474,86878,31427],{},[474,86880,86881],{"class":476,"line":5281},[474,86882,86883],{}," * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,\n",[474,86885,86886],{"class":476,"line":5294},[474,86887,86888],{}," * java.net.InetAddress, int, org.apache.http.params.HttpParams)\n",[474,86890,86891],{"class":476,"line":5307},[474,86892,86893],{}," */\n",[474,86895,86896],{"class":476,"line":5318},[474,86897,86898],{}," public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort,\n",[474,86900,86901],{"class":476,"line":5323},[474,86902,86903],{}," HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {\n",[474,86905,86906],{"class":476,"line":5330},[474,86907,86908],{}," int connTimeout = HttpConnectionParams.getConnectionTimeout(params);\n",[474,86910,86911],{"class":476,"line":5335},[474,86912,86913],{}," int soTimeout = HttpConnectionParams.getSoTimeout(params);\n",[474,86915,86916],{"class":476,"line":5340},[474,86917,86918],{}," InetSocketAddress remoteAddress = new InetSocketAddress(host, port);\n",[474,86920,86921],{"class":476,"line":5352},[474,86922,86923],{}," SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());\n",[474,86925,86926],{"class":476,"line":5376},[474,86927,86928],{}," if ((localAddress != null) || (localPort > 0)) {\n",[474,86930,86931],{"class":476,"line":5387},[474,86932,86933],{}," // we need to bind explicitly\n",[474,86935,86936],{"class":476,"line":5398},[474,86937,86938],{}," if (localPort \u003C 0) {\n",[474,86940,86941],{"class":476,"line":5409},[474,86942,86943],{}," localPort = 0; // indicates \"any\"\n",[474,86945,86946],{"class":476,"line":5420},[474,86947,42564],{},[474,86949,86950],{"class":476,"line":5432},[474,86951,86952],{}," InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);\n",[474,86954,86955],{"class":476,"line":5439},[474,86956,86957],{}," sslsock.bind(isa);\n",[474,86959,86960],{"class":476,"line":5450},[474,86961,5704],{},[474,86963,86964],{"class":476,"line":5461},[474,86965,86966],{}," sslsock.connect(remoteAddress, connTimeout);\n",[474,86968,86969],{"class":476,"line":5470},[474,86970,86971],{}," sslsock.setSoTimeout(soTimeout);\n",[474,86973,86974],{"class":476,"line":5475},[474,86975,86976],{}," return sslsock;\n",[474,86978,86979],{"class":476,"line":5482},[474,86980,1276],{},[474,86982,86983],{"class":476,"line":5487},[474,86984,31427],{},[474,86986,86987],{"class":476,"line":5492},[474,86988,86989],{}," * @see org.apache.http.conn.scheme.SocketFactory#createSocket()\n",[474,86991,86992],{"class":476,"line":5504},[474,86993,86893],{},[474,86995,86996],{"class":476,"line":5521},[474,86997,86998],{}," public Socket createSocket() throws IOException {\n",[474,87000,87001],{"class":476,"line":5532},[474,87002,87003],{}," return getSSLContext().getSocketFactory().createSocket();\n",[474,87005,87006],{"class":476,"line":5543},[474,87007,1276],{},[474,87009,87010],{"class":476,"line":5554},[474,87011,31427],{},[474,87013,87014],{"class":476,"line":5565},[474,87015,87016],{}," * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)\n",[474,87018,87019],{"class":476,"line":5576},[474,87020,86893],{},[474,87022,87023],{"class":476,"line":5583},[474,87024,87025],{}," public boolean isSecure(Socket socket) throws IllegalArgumentException {\n",[474,87027,87028],{"class":476,"line":5590},[474,87029,42573],{},[474,87031,87032],{"class":476,"line":5595},[474,87033,1276],{},[474,87035,87036],{"class":476,"line":5600},[474,87037,31427],{},[474,87039,87040],{"class":476,"line":5612},[474,87041,87042],{}," * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,\n",[474,87044,87045],{"class":476,"line":5632},[474,87046,87047],{}," * boolean)\n",[474,87049,87050],{"class":476,"line":5643},[474,87051,86893],{},[474,87053,87054],{"class":476,"line":5654},[474,87055,87056],{}," public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,\n",[474,87058,87059],{"class":476,"line":5665},[474,87060,87061],{}," UnknownHostException {\n",[474,87063,87064],{"class":476,"line":5676},[474,87065,87066],{}," return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);\n",[474,87068,87069],{"class":476,"line":5687},[474,87070,1276],{},[474,87072,87073],{"class":476,"line":5694},[474,87074,87075],{}," // -------------------------------------------------------------------\n",[474,87077,87078],{"class":476,"line":5701},[474,87079,87080],{}," // javadoc in org.apache.http.conn.scheme.SocketFactory says :\n",[474,87082,87083],{"class":476,"line":5707},[474,87084,87085],{}," // Both Object.equals() and Object.hashCode() must be overridden\n",[474,87087,87088],{"class":476,"line":5713},[474,87089,87090],{}," // for the correct operation of some connection managers\n",[474,87092,87093],{"class":476,"line":5718},[474,87094,87075],{},[474,87096,87097],{"class":476,"line":5724},[474,87098,87099],{}," public boolean equals(Object obj) {\n",[474,87101,87102],{"class":476,"line":5732},[474,87103,87104],{}," return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));\n",[474,87106,87107],{"class":476,"line":5740},[474,87108,1276],{},[474,87110,87111],{"class":476,"line":76109},[474,87112,87113],{}," public int hashCode() {\n",[474,87115,87116],{"class":476,"line":76121},[474,87117,87118],{}," return EasySSLSocketFactory.class.hashCode();\n",[474,87120,87121],{"class":476,"line":76127},[474,87122,1276],{},[474,87124,87125],{"class":476,"line":76138},[474,87126,703],{},[439,87128,87129],{},"And the TrustManager:",[464,87131,87133],{"className":709,"code":87132,"language":711,"meta":469,"style":469},"\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\nimport java.security.KeyStore;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.TrustManagerFactory;\nimport javax.net.ssl.X509TrustManager;\n/**\n * @author olamy\n * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $\n * @since 1.2.3\n */\npublic class EasyX509TrustManager implements X509TrustManager {\n private X509TrustManager standardTrustManager = null;\n /**\n * Constructor for EasyX509TrustManager.\n */\n public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {\n super();\n TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n factory.init(keystore);\n TrustManager[] trustmanagers = factory.getTrustManagers();\n if (trustmanagers.length == 0) {\n throw new NoSuchAlgorithmException(\"no trust manager found\");\n }\n this.standardTrustManager = (X509TrustManager) trustmanagers[0];\n }\n /**\n * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)\n */\n public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n standardTrustManager.checkClientTrusted(certificates, authType);\n }\n /**\n * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)\n */\n public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n if ((certificates != null) && (certificates.length == 1)) {\n certificates[0].checkValidity();\n } else {\n standardTrustManager.checkServerTrusted(certificates, authType);\n }\n }\n /**\n * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()\n */\n public X509Certificate[] getAcceptedIssuers() {\n return this.standardTrustManager.getAcceptedIssuers();\n }\n}\n\n",[471,87134,87135,87139,87143,87147,87151,87155,87159,87163,87167,87171,87175,87179,87183,87187,87191,87195,87199,87203,87207,87211,87216,87221,87226,87231,87236,87240,87245,87250,87254,87258,87263,87267,87271,87276,87281,87285,87290,87294,87299,87304,87309,87314,87319,87324,87329,87333,87338,87342,87346,87351,87355,87360,87365,87369,87373,87378,87382,87387,87392,87397,87401,87406,87410,87414,87418,87423,87427,87432,87437,87441],{"__ignoreMap":469},[474,87136,87137],{"class":476,"line":477},[474,87138,917],{"emptyLinePlaceholder":916},[474,87140,87141],{"class":476,"line":507},[474,87142,86615],{},[474,87144,87145],{"class":476,"line":547},[474,87146,86620],{},[474,87148,87149],{"class":476,"line":584},[474,87150,86625],{},[474,87152,87153],{"class":476,"line":607},[474,87154,86630],{},[474,87156,87157],{"class":476,"line":642},[474,87158,86635],{},[474,87160,87161],{"class":476,"line":663},[474,87162,86640],{},[474,87164,87165],{"class":476,"line":694},[474,87166,86645],{},[474,87168,87169],{"class":476,"line":700},[474,87170,86650],{},[474,87172,87173],{"class":476,"line":913},[474,87174,86655],{},[474,87176,87177],{"class":476,"line":920},[474,87178,86660],{},[474,87180,87181],{"class":476,"line":926},[474,87182,86655],{},[474,87184,87185],{"class":476,"line":932},[474,87186,86669],{},[474,87188,87189],{"class":476,"line":938},[474,87190,86674],{},[474,87192,87193],{"class":476,"line":944},[474,87194,86679],{},[474,87196,87197],{"class":476,"line":950},[474,87198,86684],{},[474,87200,87201],{"class":476,"line":956},[474,87202,86689],{},[474,87204,87205],{"class":476,"line":962},[474,87206,86694],{},[474,87208,87209],{"class":476,"line":4876},[474,87210,73845],{},[474,87212,87213],{"class":476,"line":4888},[474,87214,87215],{},"import java.security.KeyStore;\n",[474,87217,87218],{"class":476,"line":4900},[474,87219,87220],{},"import java.security.KeyStoreException;\n",[474,87222,87223],{"class":476,"line":4913},[474,87224,87225],{},"import java.security.NoSuchAlgorithmException;\n",[474,87227,87228],{"class":476,"line":4921},[474,87229,87230],{},"import java.security.cert.CertificateException;\n",[474,87232,87233],{"class":476,"line":4932},[474,87234,87235],{},"import java.security.cert.X509Certificate;\n",[474,87237,87238],{"class":476,"line":4938},[474,87239,86737],{},[474,87241,87242],{"class":476,"line":4946},[474,87243,87244],{},"import javax.net.ssl.TrustManagerFactory;\n",[474,87246,87247],{"class":476,"line":4952},[474,87248,87249],{},"import javax.net.ssl.X509TrustManager;\n",[474,87251,87252],{"class":476,"line":4957},[474,87253,73830],{},[474,87255,87256],{"class":476,"line":4969},[474,87257,86780],{},[474,87259,87260],{"class":476,"line":4990},[474,87261,87262],{}," * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $\n",[474,87264,87265],{"class":476,"line":5001},[474,87266,86790],{},[474,87268,87269],{"class":476,"line":5013},[474,87270,73845],{},[474,87272,87273],{"class":476,"line":5024},[474,87274,87275],{},"public class EasyX509TrustManager implements X509TrustManager {\n",[474,87277,87278],{"class":476,"line":5035},[474,87279,87280],{}," private X509TrustManager standardTrustManager = null;\n",[474,87282,87283],{"class":476,"line":5047},[474,87284,31427],{},[474,87286,87287],{"class":476,"line":5055},[474,87288,87289],{}," * Constructor for EasyX509TrustManager.\n",[474,87291,87292],{"class":476,"line":5062},[474,87293,86893],{},[474,87295,87296],{"class":476,"line":5067},[474,87297,87298],{}," public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {\n",[474,87300,87301],{"class":476,"line":5072},[474,87302,87303],{}," super();\n",[474,87305,87306],{"class":476,"line":5084},[474,87307,87308],{}," TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n",[474,87310,87311],{"class":476,"line":5100},[474,87312,87313],{}," factory.init(keystore);\n",[474,87315,87316],{"class":476,"line":5111},[474,87317,87318],{}," TrustManager[] trustmanagers = factory.getTrustManagers();\n",[474,87320,87321],{"class":476,"line":5122},[474,87322,87323],{}," if (trustmanagers.length == 0) {\n",[474,87325,87326],{"class":476,"line":5134},[474,87327,87328],{}," throw new NoSuchAlgorithmException(\"no trust manager found\");\n",[474,87330,87331],{"class":476,"line":5145},[474,87332,5704],{},[474,87334,87335],{"class":476,"line":5156},[474,87336,87337],{}," this.standardTrustManager = (X509TrustManager) trustmanagers[0];\n",[474,87339,87340],{"class":476,"line":5163},[474,87341,1276],{},[474,87343,87344],{"class":476,"line":5170},[474,87345,31427],{},[474,87347,87348],{"class":476,"line":5175},[474,87349,87350],{}," * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)\n",[474,87352,87353],{"class":476,"line":5180},[474,87354,86893],{},[474,87356,87357],{"class":476,"line":5192},[474,87358,87359],{}," public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[474,87361,87362],{"class":476,"line":5217},[474,87363,87364],{}," standardTrustManager.checkClientTrusted(certificates, authType);\n",[474,87366,87367],{"class":476,"line":5229},[474,87368,1276],{},[474,87370,87371],{"class":476,"line":5240},[474,87372,31427],{},[474,87374,87375],{"class":476,"line":5251},[474,87376,87377],{}," * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)\n",[474,87379,87380],{"class":476,"line":5262},[474,87381,86893],{},[474,87383,87384],{"class":476,"line":5274},[474,87385,87386],{}," public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[474,87388,87389],{"class":476,"line":5281},[474,87390,87391],{}," if ((certificates != null) && (certificates.length == 1)) {\n",[474,87393,87394],{"class":476,"line":5294},[474,87395,87396],{}," certificates[0].checkValidity();\n",[474,87398,87399],{"class":476,"line":5307},[474,87400,21063],{},[474,87402,87403],{"class":476,"line":5318},[474,87404,87405],{}," standardTrustManager.checkServerTrusted(certificates, authType);\n",[474,87407,87408],{"class":476,"line":5323},[474,87409,5704],{},[474,87411,87412],{"class":476,"line":5330},[474,87413,1276],{},[474,87415,87416],{"class":476,"line":5335},[474,87417,31427],{},[474,87419,87420],{"class":476,"line":5340},[474,87421,87422],{}," * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()\n",[474,87424,87425],{"class":476,"line":5352},[474,87426,86893],{},[474,87428,87429],{"class":476,"line":5376},[474,87430,87431],{}," public X509Certificate[] getAcceptedIssuers() {\n",[474,87433,87434],{"class":476,"line":5387},[474,87435,87436],{}," return this.standardTrustManager.getAcceptedIssuers();\n",[474,87438,87439],{"class":476,"line":5398},[474,87440,1276],{},[474,87442,87443],{"class":476,"line":5409},[474,87444,703],{},[439,87446,87447,87448,87453],{},"(Both classes are\nfrom ",[1002,87449,87452],{"href":87450,"rel":87451},"https://web.archive.org/web/20111230175916/http://exchangeit.googlecode.com:80/svn-history/r23/trunk/src/com/byarger/exchangeit/",[1006],"exchangeit","\nwith a small change on the EasySSLSocketFactory to work on android 2.2)",[439,87455,87456],{},"Now we have to do some other preparations and create a HttpClient that we can use to establish the connection:",[464,87458,87460],{"className":709,"code":87459,"language":711,"meta":469,"style":469},"\n//members\nprivate ClientConnectionManager clientConnectionManager;\nprivate HttpContext context;\nprivate HttpParams params;\n\n",[471,87461,87462,87466,87471,87476,87481],{"__ignoreMap":469},[474,87463,87464],{"class":476,"line":477},[474,87465,917],{"emptyLinePlaceholder":916},[474,87467,87468],{"class":476,"line":507},[474,87469,87470],{},"//members\n",[474,87472,87473],{"class":476,"line":547},[474,87474,87475],{},"private ClientConnectionManager clientConnectionManager;\n",[474,87477,87478],{"class":476,"line":584},[474,87479,87480],{},"private HttpContext context;\n",[474,87482,87483],{"class":476,"line":607},[474,87484,87485],{},"private HttpParams params;\n",[464,87487,87489],{"className":709,"code":87488,"language":711,"meta":469,"style":469},"\n//constructor\npublic WebService(){\n setup();\n}\n//prepare for the https connection\n//call this in the constructor of the class that does the connection if\n//it's used multiple times\nprivate void setup(){\nSchemeRegistry schemeRegistry = new SchemeRegistry();\n // http scheme\n schemeRegistry.register(new Scheme(\"http\", PlainSocketFactory.getSocketFactory(), 80));\n // https scheme\n schemeRegistry.register(new Scheme(\"https\", new EasySSLSocketFactory(), 443));\n params = new BasicHttpParams();\n params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);\n params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));\n params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);\n HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);\n HttpProtocolParams.setContentCharset(params, \"utf8\");\n CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n //set the user credentials for our site \"example.com\"\n credentialsProvider.setCredentials(new AuthScope(\"example.com\", AuthScope.ANY_PORT),\n new UsernamePasswordCredentials(\"UserNameHere\", \"UserPasswordHere\"));\n clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);\n context = new BasicHttpContext();\n context.setAttribute(\"http.auth.credentials-provider\", credentialsProvider);\n}\n\n",[471,87490,87491,87495,87500,87505,87510,87514,87519,87524,87529,87534,87539,87544,87549,87554,87559,87564,87569,87574,87579,87584,87589,87594,87599,87604,87609,87614,87619,87624],{"__ignoreMap":469},[474,87492,87493],{"class":476,"line":477},[474,87494,917],{"emptyLinePlaceholder":916},[474,87496,87497],{"class":476,"line":507},[474,87498,87499],{},"//constructor\n",[474,87501,87502],{"class":476,"line":547},[474,87503,87504],{},"public WebService(){\n",[474,87506,87507],{"class":476,"line":584},[474,87508,87509],{}," setup();\n",[474,87511,87512],{"class":476,"line":607},[474,87513,703],{},[474,87515,87516],{"class":476,"line":642},[474,87517,87518],{},"//prepare for the https connection\n",[474,87520,87521],{"class":476,"line":663},[474,87522,87523],{},"//call this in the constructor of the class that does the connection if\n",[474,87525,87526],{"class":476,"line":694},[474,87527,87528],{},"//it's used multiple times\n",[474,87530,87531],{"class":476,"line":700},[474,87532,87533],{},"private void setup(){\n",[474,87535,87536],{"class":476,"line":913},[474,87537,87538],{},"SchemeRegistry schemeRegistry = new SchemeRegistry();\n",[474,87540,87541],{"class":476,"line":920},[474,87542,87543],{}," // http scheme\n",[474,87545,87546],{"class":476,"line":926},[474,87547,87548],{}," schemeRegistry.register(new Scheme(\"http\", PlainSocketFactory.getSocketFactory(), 80));\n",[474,87550,87551],{"class":476,"line":932},[474,87552,87553],{}," // https scheme\n",[474,87555,87556],{"class":476,"line":938},[474,87557,87558],{}," schemeRegistry.register(new Scheme(\"https\", new EasySSLSocketFactory(), 443));\n",[474,87560,87561],{"class":476,"line":944},[474,87562,87563],{}," params = new BasicHttpParams();\n",[474,87565,87566],{"class":476,"line":950},[474,87567,87568],{}," params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);\n",[474,87570,87571],{"class":476,"line":956},[474,87572,87573],{}," params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));\n",[474,87575,87576],{"class":476,"line":962},[474,87577,87578],{}," params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);\n",[474,87580,87581],{"class":476,"line":4876},[474,87582,87583],{}," HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);\n",[474,87585,87586],{"class":476,"line":4888},[474,87587,87588],{}," HttpProtocolParams.setContentCharset(params, \"utf8\");\n",[474,87590,87591],{"class":476,"line":4900},[474,87592,87593],{}," CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n",[474,87595,87596],{"class":476,"line":4913},[474,87597,87598],{}," //set the user credentials for our site \"example.com\"\n",[474,87600,87601],{"class":476,"line":4921},[474,87602,87603],{}," credentialsProvider.setCredentials(new AuthScope(\"example.com\", AuthScope.ANY_PORT),\n",[474,87605,87606],{"class":476,"line":4932},[474,87607,87608],{}," new UsernamePasswordCredentials(\"UserNameHere\", \"UserPasswordHere\"));\n",[474,87610,87611],{"class":476,"line":4938},[474,87612,87613],{}," clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);\n",[474,87615,87616],{"class":476,"line":4946},[474,87617,87618],{}," context = new BasicHttpContext();\n",[474,87620,87621],{"class":476,"line":4952},[474,87622,87623],{}," context.setAttribute(\"http.auth.credentials-provider\", credentialsProvider);\n",[474,87625,87626],{"class":476,"line":4957},[474,87627,703],{},[464,87629,87631],{"className":709,"code":87630,"language":711,"meta":469,"style":469},"\npublic HttpResponse getResponseFromUrl(String url){\n//connection (client has to be created for every new connection)\nclient = new DefaultHttpClient(clientConnectionManager, params);\nHttpGet get = new HttpGet(url);\nHttpResponse response = client.execute(get, context);\nreturn response;\n}\n\n",[471,87632,87633,87637,87642,87647,87652,87657,87662,87667],{"__ignoreMap":469},[474,87634,87635],{"class":476,"line":477},[474,87636,917],{"emptyLinePlaceholder":916},[474,87638,87639],{"class":476,"line":507},[474,87640,87641],{},"public HttpResponse getResponseFromUrl(String url){\n",[474,87643,87644],{"class":476,"line":547},[474,87645,87646],{},"//connection (client has to be created for every new connection)\n",[474,87648,87649],{"class":476,"line":584},[474,87650,87651],{},"client = new DefaultHttpClient(clientConnectionManager, params);\n",[474,87653,87654],{"class":476,"line":607},[474,87655,87656],{},"HttpGet get = new HttpGet(url);\n",[474,87658,87659],{"class":476,"line":642},[474,87660,87661],{},"HttpResponse response = client.execute(get, context);\n",[474,87663,87664],{"class":476,"line":663},[474,87665,87666],{},"return response;\n",[474,87668,87669],{"class":476,"line":694},[474,87670,703],{},[439,87672,87673],{},"And that's it. I hope this will help some of you to solve their problems with self-signed certs!",[1024,87675,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":87677},[],[11122,1413],"2010-06-24T16:08:24","Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let\\nandroid accept them.","https://synyx.de/blog/android-and-self-signed-ssl-certificates/",{},"/blog/android-and-self-signed-ssl-certificates",{"title":86587,"description":86596},"blog/android-and-self-signed-ssl-certificates",[11132,87687,87688,87689,87690,87691],"cert","certificate","https","self-signed","ssl","Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let android accept them. But fortunately, there’s a workaround…","gDXsqYNJRaGcit1nh6xgZqXcXw3DL1tsKjV5BU5dPqA",{"id":87695,"title":87696,"author":87697,"body":87698,"category":87782,"date":87783,"description":87784,"extension":1034,"link":87785,"meta":87786,"navigation":916,"path":87787,"seo":87788,"slug":87790,"stem":87791,"tags":87792,"teaser":87793,"__hash__":87794},"blog/blog/inintivative-rheumapreis-interview-mit-christian-vedder-bih.md","Initiative Rheumapreis: Interview mit Christian Vedder (BIH)",[172],{"type":432,"value":87699,"toc":87780},[87700,87703,87729,87732,87737,87740,87745,87748,87751,87754,87759,87762,87767,87770,87773],[435,87701,87696],{"id":87702},"initiative-rheumapreis-interview-mit-christian-vedder-bih",[439,87704,87705,87712,87713,87716,87717,87722,87723,87728],{},[1002,87706,87709],{"href":87707,"rel":87708},"https://media.synyx.de/uploads//2010/06/logo_rheumapreis1.gif",[1006],[2205,87710],{"alt":469,"src":87707,"title":87711},"logo_rheumapreis","\nHeute blogge ich zu einem Thema, welches auf den ersten Blick recht wenig mit ",[1002,87714,74175],{"href":26130,"rel":87715},[1006]," zu tun hat.\nIm Interview mit Christian Vedder, Geschäftsführer der ",[1002,87718,87721],{"href":87719,"rel":87720},"http://www.integrationsaemter.de",[1006],"BIH",",\nzur ",[1002,87724,87727],{"href":87725,"rel":87726},"http://www.rheumapreis.de/aktuelles/rheumapreis-geht-in-die-zweite-runde/",[1006],"Initiative Rheumapreis"," wird erörtert,\nwas sich hinter der Initiative verbirgt. Aber was hat das nun mit Synyx zu tun? Unsere Unternehmensphilosophie\nbeinhaltet unter anderem, sich für soziale Zwecke einzusetzen. Dazu gehört auch, am Arbeitsplatz den Mitarbeitern die\nbestmöglichen Bedingungen zu schaffen. Egal, ob sich das auf die technische Entwicklungsumgebung, den Wissenstransfer\noder auf die physischen Arbeitsplatzbedingungen bezieht. Mit der BIH besteht bereits seit Jahren eine Zusammenarbeit,\nmit der Initiative Rheumapreis seit ca. 2 Jahren. Und so ist es für uns selbstverständlich unsere Unterstützung\nanzubieten, um dieses Thema aktuell zu halten.",[439,87730,87731],{},"Und nun zum Interview.",[439,87733,87734],{},[448,87735,87736],{},"Über Christian Vedder und die BIH",[439,87738,87739],{},"Die Abkürzung BIH steht für (Bundesarbeitsgemeinschaft der Integrationsämter und Hauptfürsorgestellen). Die BIH ist der\nZusammenschluss aller Integrationsämter und HauptfürsorgesteIlen in Deutschland. Sie erarbeitet Empfehlungen zur\nUmsetzung des Schwerbehindertenrechts, vertritt ihre Mitglieder in Gremien auf Bundesebene und bei Absprachen mit\nanderen gesetzlichen Leistungsträgern. Die Integrationsämter nehmen gesetzliche Aufgaben im Rahmen der Beschäftigung\nschwerbehinderter Menschen auf dem allgemeinen Arbeitsmarkt wahr und fördern die berufliche Chancengleichheit von\nschwerbehinderten Menschen. Christian Vedder ist Geschäftsführer der BIH.",[439,87741,87742],{},[448,87743,87744],{},"Was verbirgt sich hinter der Initiative RheumaPreis?",[439,87746,87747],{},"Der RheumaPreis ist eine Initiative mit dem Ziel, die beruflichen Chancen von Menschen mit entzündlich-rheumatischen\nErkrankungen zu verbessern und zu mehr Offenheit im Umgang mit dem Thema „Rheuma in der Arbeitswelt“ anzuregen. Im\nMittelpunkt stehen dabei die Patienten, aber auch die kreativen Lösungen, die sie mit ihrem Arbeitgeber gefunden haben,\num Arbeitsfähigkeit und Selbstständigkeit am Arbeitsplatz zu erhalten. Die Geschichten der Preisträger sollen\nVorbildcharakter haben, denn noch immer scheiden viele Arbeitnehmer mit Rheuma vorzeitig aus dem Berufsleben aus. Meist\ngeschieht dies weniger aus medizinischer Notwendigkeit heraus, als vielfach aufgrund mangelnden Wissens über die zur\nVerfügung stehenden Möglichkeiten für eine berufliche Integration. Auch darauf möchte die Initiative aufmerksam machen.",[439,87749,87750],{},"**Mit welchen Beweggründen unterstützt die BIH den RheumaPreis? Was für eine Bedeutung hat der RheumaPreis für den BIH?\n**",[439,87752,87753],{},"Die berufliche Integration behinderter Menschen ist nicht allein mit Gesetzen und finanziellen Mitteln zu erreichen.\nSondern es braucht kreative Ideen, die anderen als Vorbild dienen, so wie die RheumaPreis-Beispiele. Die Initiative\nverbindet alle Elemente, die auch für die BIH wichtig sind: Einfallsreichtum und Innovation, aber auch Bodenhaftigkeit\nund Umsetzungswillen. Genau deshalb unterstützen wir den RheumaPreis.",[439,87755,87756],{},[448,87757,87758],{},"Wie ist das Auswahlverfahren der Preisträger(-innen)? Welche Aspekte berücksichtigen Sie besonders?",[439,87760,87761],{},"Es ist natürlich nicht einfach, die Gewinner aus all den Bewerbungen auszuwählen. Denn die vorgestellten Lösungsansätze\nsind so verschieden und deshalb schwer miteinander zu vergleichen. Dennoch haben wir feste Kriterien, anhand derer wir\nunsere Wahl festmachen, damit die Auswahl objektiv bleibt. Grundsätzlich wichtig ist für uns zu sehen, ob die Lösungen\nauch tatsächlich dauerhaft umsetzbar sind. Mit Blick auf die Zusammenarbeit achten wir besonders auf das\nEinfühlungsvermögen, Verständnis und Entgegenkommen der Arbeitgeber. Eine positive Einstellung und der Wille nicht\naufzugeben sind Faktoren, die wir beim Arbeitnehmer verstärkt berücksichtigen.",[439,87763,87764],{},[448,87765,87766],{},"Was für eine Bedeutung hat der RheumaPreis für die Gewinner?",[439,87768,87769],{},"Für die Gewinner ist der RheumaPreis sicherlich mehr als nur „ein Preis“. Viele von ihnen mussten in ihrer beruflichen\nLaufbahn diverse Hürden überwinden, Niederschläge verkraften und haben dennoch niemals aufgegeben. Der RheumaPreis soll\nden Gewinnern zeigen, dass sich dieser harte Weg gelohnt hat und sie sehr vielen Menschen in Deutschland damit als\nVorbild gelten sollten. Er zeichnet ihren Mut, ihr Durchhaltevermögen, ihren Willen und ihren Optimismus in besonderer\nWeise aus.",[439,87771,87772],{},"Vielen Dank für das Interview.",[439,87774,87775],{},[1002,87776,87779],{"href":87777,"rel":87778},"https://media.synyx.de/uploads//2010/06/RhPreis2010_PatientenFlyer_DINlang.pdf",[1006],"Download Infomationsbroschüre",{"title":469,"searchDepth":507,"depth":507,"links":87781},[],[1031],"2010-06-24T13:44:46","\\nHeute blogge ich zu einem Thema, welches auf den ersten Blick recht wenig mit Synyx zu tun hat.\\nIm Interview mit Christian Vedder, Geschäftsführer der BIH,\\nzur Initiative Rheumapreis wird erörtert,\\nwas sich hinter der Initiative verbirgt. Aber was hat das nun mit Synyx zu tun? Unsere Unternehmensphilosophie\\nbeinhaltet unter anderem, sich für soziale Zwecke einzusetzen. Dazu gehört auch, am Arbeitsplatz den Mitarbeitern die\\nbestmöglichen Bedingungen zu schaffen. Egal, ob sich das auf die technische Entwicklungsumgebung, den Wissenstransfer\\noder auf die physischen Arbeitsplatzbedingungen bezieht. Mit der BIH besteht bereits seit Jahren eine Zusammenarbeit,\\nmit der Initiative Rheumapreis seit ca. 2 Jahren. Und so ist es für uns selbstverständlich unsere Unterstützung\\nanzubieten, um dieses Thema aktuell zu halten.","https://synyx.de/blog/inintivative-rheumapreis-interview-mit-christian-vedder-bih/",{},"/blog/inintivative-rheumapreis-interview-mit-christian-vedder-bih",{"title":87696,"description":87789},"\nHeute blogge ich zu einem Thema, welches auf den ersten Blick recht wenig mit Synyx zu tun hat.\nIm Interview mit Christian Vedder, Geschäftsführer der BIH,\nzur Initiative Rheumapreis wird erörtert,\nwas sich hinter der Initiative verbirgt. Aber was hat das nun mit Synyx zu tun? Unsere Unternehmensphilosophie\nbeinhaltet unter anderem, sich für soziale Zwecke einzusetzen. Dazu gehört auch, am Arbeitsplatz den Mitarbeitern die\nbestmöglichen Bedingungen zu schaffen. Egal, ob sich das auf die technische Entwicklungsumgebung, den Wissenstransfer\noder auf die physischen Arbeitsplatzbedingungen bezieht. Mit der BIH besteht bereits seit Jahren eine Zusammenarbeit,\nmit der Initiative Rheumapreis seit ca. 2 Jahren. Und so ist es für uns selbstverständlich unsere Unterstützung\nanzubieten, um dieses Thema aktuell zu halten.","inintivative-rheumapreis-interview-mit-christian-vedder-bih","blog/inintivative-rheumapreis-interview-mit-christian-vedder-bih",[],"Heute blogge ich zu einem Thema, welches auf den ersten Blick recht wenig mit Synyx zu tun hat. Im Interview mit Christian Vedder, Geschäftsführer der BIH, zur Initiative Rheumapreis wird…","M680Z6JkYbTvXTS2b_gMlIq3rPDGtsWeyiMrsvfAd5Y",{"id":87796,"title":82565,"author":87797,"body":87798,"category":87909,"date":87910,"description":87911,"extension":1034,"link":87912,"meta":87913,"navigation":916,"path":87914,"seo":87915,"slug":87917,"stem":87918,"tags":87919,"teaser":87920,"__hash__":87921},"blog/blog/mobile-solutions-summary-3.md",[223],{"type":432,"value":87799,"toc":87907},[87800,87802,87822,87836,87874,87892,87899],[435,87801,82565],{"id":82571},[439,87803,87804,87805,87810,87811,87816,87817,87821],{},"In my ",[1002,87806,87809],{"href":87807,"rel":87808},"http://blog.synyx.de/2010/05/mobile-solutions-%E2%80%93-summary/",[1006],"last summary"," I forgot to mention, that I will be\nat ",[1002,87812,87815],{"href":87813,"rel":87814},"http://developer.apple.com/wwdc",[1006],"WWDC"," this year. Now WWDC is over, I just got back and if you want to know more,\ncheckout my blog post ",[1002,87818,22916],{"href":87819,"rel":87820},"http://dlinsin.blogspot.com/2010/06/wwdc10.html",[1006],". While I was gone, our team was busy\npublishing all sorts of good stuff!",[439,87823,87824,87825,87830,87831,87835],{},"Let’s start with the release of Android’s FroYo last\nmonth! ",[1002,87826,87829],{"href":87827,"rel":87828},"http://mobile.synyx.de/2010/05/in-my-humble-opinion-froyo-rocks/",[1006],"Achim"," wrote\na ",[1002,87832,87834],{"href":87827,"rel":87833},[1006],"nice blog post"," highlighting the noteworthy\nfeatures:",[11947,87837,87838,87841,87844,87852,87855,87863,87866],{},[439,87839,87840],{},"*FroYo is like each previous version a mixture between API Changes, new Userfeatures and some new cool Apps….",[439,87842,87843],{},"New API-Features:*",[994,87845,87846,87849],{},[997,87847,87848],{},"Data Backup API",[997,87850,87851],{},"Possibility to save passwords secure",[439,87853,87854],{},"New User Features:",[994,87856,87857,87860],{},[997,87858,87859],{},"updated Exchange Features",[997,87861,87862],{},"Remote Wipe",[439,87864,87865],{},"New Apps:",[994,87867,87868,87871],{},[997,87869,87870],{},"Camera and Camcorder updated (possible to enable manually the leds for usage within camcorder)",[997,87872,87873],{},"Android Tethering and Usage as Hotspot",[439,87875,87876,87880,87881,87886,87887,6562],{},[1002,87877,80682],{"href":87878,"rel":87879},"http://mobile.synyx.de/authors/?uid=6",[1006],", our ",[1002,87882,87885],{"href":87883,"rel":87884},"http://mobile.synyx.de/tag/maemo-5/",[1006],"Maemo"," guy, followed our\nGoogle Maps theme and published the first part of a nice introduction\nof ",[1002,87888,87891],{"href":87889,"rel":87890},"http://mobile.synyx.de/2010/06/google-maps-on-maemo-5-part-1/",[1006],"Google Maps on Maemo 5",[11947,87893,87894],{},[439,87895,87896],{},[990,87897,87898],{},"The idea is quite simple. Webkit will render a webpage insider your app. That webpage consists of javascript methods\nwhich use the Google Maps-API. The javascript methods can be triggered from the app. The map class acts as proxy for\nthe communication between your app and the website. Quite simple hm?",[439,87900,87901,87902,87906],{},"This is only a small sneak peak of what’s going on over at the mobile solutions blog. I suggest\nyou ",[1002,87903,87905],{"href":82583,"rel":87904},[1006],"add it"," to your favorite feed reader and check it out regularly.",{"title":469,"searchDepth":507,"depth":507,"links":87908},[],[1030],"2010-06-18T08:57:09","In my last summary I forgot to mention, that I will be\\nat WWDC this year. Now WWDC is over, I just got back and if you want to know more,\\ncheckout my blog post here. While I was gone, our team was busy\\npublishing all sorts of good stuff!","https://synyx.de/blog/mobile-solutions-summary-3/",{},"/blog/mobile-solutions-summary-3",{"title":82565,"description":87916},"In my last summary I forgot to mention, that I will be\nat WWDC this year. Now WWDC is over, I just got back and if you want to know more,\ncheckout my blog post here. While I was gone, our team was busy\npublishing all sorts of good stuff!","mobile-solutions-summary-3","blog/mobile-solutions-summary-3",[63945,78398,18496],"In my last summary I forgot to mention, that I will be at WWDC this year. Now WWDC is over, I just got back and if you want to know…","Vt3IIuwgnXxkVeplkiSoihd83Xam9gAc_bmG2yhTjts",{"id":87923,"title":87924,"author":87925,"body":87926,"category":88546,"date":88547,"description":88548,"extension":1034,"link":88549,"meta":88550,"navigation":916,"path":88551,"seo":88552,"slug":87930,"stem":88554,"tags":88555,"teaser":88559,"__hash__":88560},"blog/blog/routing-driving-directions-on-android-part-2-draw-the-route.md","Routing / Driving directions on Android – Part 2: Draw the route",[190],{"type":432,"value":87927,"toc":88541},[87928,87931,87940,87944,87947,87996,87999,88072,88075,88108,88112,88115,88118,88132,88135,88140,88147,88212,88215,88349,88353,88356,88359,88440,88443,88467,88470,88473,88476,88519,88522,88536,88539],[435,87929,87924],{"id":87930},"routing-driving-directions-on-android-part-2-draw-the-route",[439,87932,87933,87934,87939],{},"After you ",[1002,87935,87938],{"href":87936,"rel":87937},"http://mobile.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/",[1006],"got the route","\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.",[3938,87941,87943],{"id":87942},"create-a-suiting-overlay","Create a suiting Overlay",[439,87945,87946],{},"We basically need a Overlay that takes two Geopoints and maybe a color in which the lines should be drawn. So here We\nhave:",[464,87948,87950],{"className":709,"code":87949,"language":711,"meta":469,"style":469},"public class RouteOverlay extends Overlay {\n private GeoPoint gp1;\n private GeoPoint gp2;\n private int color;\npublic RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) {\n this.gp1 = gp1;\n this.gp2 = gp2;\n this.color = color;\n }\n",[471,87951,87952,87957,87962,87967,87972,87977,87982,87987,87992],{"__ignoreMap":469},[474,87953,87954],{"class":476,"line":477},[474,87955,87956],{},"public class RouteOverlay extends Overlay {\n",[474,87958,87959],{"class":476,"line":507},[474,87960,87961],{}," private GeoPoint gp1;\n",[474,87963,87964],{"class":476,"line":547},[474,87965,87966],{}," private GeoPoint gp2;\n",[474,87968,87969],{"class":476,"line":584},[474,87970,87971],{}," private int color;\n",[474,87973,87974],{"class":476,"line":607},[474,87975,87976],{},"public RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) {\n",[474,87978,87979],{"class":476,"line":642},[474,87980,87981],{}," this.gp1 = gp1;\n",[474,87983,87984],{"class":476,"line":663},[474,87985,87986],{}," this.gp2 = gp2;\n",[474,87988,87989],{"class":476,"line":694},[474,87990,87991],{}," this.color = color;\n",[474,87993,87994],{"class":476,"line":700},[474,87995,1276],{},[439,87997,87998],{},"Now all that’s left now for our Overlay is to override the draw() method and draw the line as we need it:",[464,88000,88002],{"className":709,"code":88001,"language":711,"meta":469,"style":469},"@Override\npublic void draw(Canvas canvas, MapView mapView, boolean shadow) {\n Projection projection = mapView.getProjection();\n Paint paint = new Paint();\n Point point = new Point();\n projection.toPixels(gp1, point);\n paint.setColor(color);\n Point point2 = new Point();\n projection.toPixels(gp2, point2);\n paint.setStrokeWidth(5);\n paint.setAlpha(120);\n canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);\n super.draw(canvas, mapView, shadow);\n}\n",[471,88003,88004,88008,88013,88018,88023,88028,88033,88038,88043,88048,88053,88058,88063,88068],{"__ignoreMap":469},[474,88005,88006],{"class":476,"line":477},[474,88007,29178],{},[474,88009,88010],{"class":476,"line":507},[474,88011,88012],{},"public void draw(Canvas canvas, MapView mapView, boolean shadow) {\n",[474,88014,88015],{"class":476,"line":547},[474,88016,88017],{}," Projection projection = mapView.getProjection();\n",[474,88019,88020],{"class":476,"line":584},[474,88021,88022],{}," Paint paint = new Paint();\n",[474,88024,88025],{"class":476,"line":607},[474,88026,88027],{}," Point point = new Point();\n",[474,88029,88030],{"class":476,"line":642},[474,88031,88032],{}," projection.toPixels(gp1, point);\n",[474,88034,88035],{"class":476,"line":663},[474,88036,88037],{}," paint.setColor(color);\n",[474,88039,88040],{"class":476,"line":694},[474,88041,88042],{}," Point point2 = new Point();\n",[474,88044,88045],{"class":476,"line":700},[474,88046,88047],{}," projection.toPixels(gp2, point2);\n",[474,88049,88050],{"class":476,"line":913},[474,88051,88052],{}," paint.setStrokeWidth(5);\n",[474,88054,88055],{"class":476,"line":920},[474,88056,88057],{}," paint.setAlpha(120);\n",[474,88059,88060],{"class":476,"line":926},[474,88061,88062],{}," canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);\n",[474,88064,88065],{"class":476,"line":932},[474,88066,88067],{}," super.draw(canvas, mapView, shadow);\n",[474,88069,88070],{"class":476,"line":938},[474,88071,703],{},[439,88073,88074],{},"Back in the Activity, just iterate over the GeoPoints that you got from google maps and add each of them to the MapView:",[464,88076,88078],{"className":709,"code":88077,"language":711,"meta":469,"style":469},"private void drawPath(List geoPoints, int color) {\n List overlays = mapView.getOverlays();\n for (int i = 1; i \u003C geoPoints.size(); i++) {\n overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color));\n }\n}\n",[471,88079,88080,88085,88090,88095,88100,88104],{"__ignoreMap":469},[474,88081,88082],{"class":476,"line":477},[474,88083,88084],{},"private void drawPath(List geoPoints, int color) {\n",[474,88086,88087],{"class":476,"line":507},[474,88088,88089],{}," List overlays = mapView.getOverlays();\n",[474,88091,88092],{"class":476,"line":547},[474,88093,88094],{}," for (int i = 1; i \u003C geoPoints.size(); i++) {\n",[474,88096,88097],{"class":476,"line":584},[474,88098,88099],{}," overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color));\n",[474,88101,88102],{"class":476,"line":607},[474,88103,23666],{},[474,88105,88106],{"class":476,"line":642},[474,88107,703],{},[3938,88109,88111],{"id":88110},"get-location-updates-from-the-location-manager","Get location updates from the location manager",[439,88113,88114],{},"So now we have the geopoints and also the overlays, but we’ve only got the last known location of the user! The app\ndoesn’t even updates his location!",[439,88116,88117],{},"What we need to achieve this is a listener from the location manager. That’s quite simple and we also got the\nLocationManager ready in onCreate, so we just have to add this little line to it:",[464,88119,88121],{"className":709,"code":88120,"language":711,"meta":469,"style":469},"\nlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n\n",[471,88122,88123,88127],{"__ignoreMap":469},[474,88124,88125],{"class":476,"line":477},[474,88126,917],{"emptyLinePlaceholder":916},[474,88128,88129],{"class":476,"line":507},[474,88130,88131],{},"locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[439,88133,88134],{},"The first number here is the timespan in milliseconds in which we want want to receive the updates, the second one is\nthe distance in meters that the user has to move before we get them. In our app we don’t have to update the route all\nthe time, so we go with 5 minutes and 5 kilometers.",[439,88136,88137],{},[448,88138,88139],{},"Be very careful with the values here, because the whole gps thing consumes a lot of energy! (And if the values are way\nto small it also blocks the whole app)",[439,88141,88142,88143,88146],{},"Also don’t forget to ",[448,88144,88145],{},"remove the listener"," if the MapView isn’t visible:",[464,88148,88150],{"className":709,"code":88149,"language":711,"meta":469,"style":469},"\n@Override\n protected void onPause() {\n //remove the listener\n locationManager.removeUpdates(this);\n super.onPause();\n }\n@Override\n protected void onResume() {\n //add the listener again\n locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n super.onResume();\n }\n\n",[471,88151,88152,88156,88160,88165,88170,88175,88180,88184,88188,88193,88198,88203,88208],{"__ignoreMap":469},[474,88153,88154],{"class":476,"line":477},[474,88155,917],{"emptyLinePlaceholder":916},[474,88157,88158],{"class":476,"line":507},[474,88159,29178],{},[474,88161,88162],{"class":476,"line":547},[474,88163,88164],{}," protected void onPause() {\n",[474,88166,88167],{"class":476,"line":584},[474,88168,88169],{}," //remove the listener\n",[474,88171,88172],{"class":476,"line":607},[474,88173,88174],{}," locationManager.removeUpdates(this);\n",[474,88176,88177],{"class":476,"line":642},[474,88178,88179],{}," super.onPause();\n",[474,88181,88182],{"class":476,"line":663},[474,88183,1276],{},[474,88185,88186],{"class":476,"line":694},[474,88187,29178],{},[474,88189,88190],{"class":476,"line":700},[474,88191,88192],{}," protected void onResume() {\n",[474,88194,88195],{"class":476,"line":913},[474,88196,88197],{}," //add the listener again\n",[474,88199,88200],{"class":476,"line":920},[474,88201,88202],{}," locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[474,88204,88205],{"class":476,"line":926},[474,88206,88207],{}," super.onResume();\n",[474,88209,88210],{"class":476,"line":932},[474,88211,1276],{},[439,88213,88214],{},"Now we have to let our MapActivity implement LocationListener and react to the updates:",[464,88216,88218],{"className":709,"code":88217,"language":711,"meta":469,"style":469},"\npublic class RouteActivity extends MapActivity implements LocationListener {\n@Override\npublic void onLocationChanged(Location location) {\ndrawUserPosition(location);\n}\nprivate void drawUserPosition(Location location) {\n GeoPoint currentLocation;\n currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location\n getLongitude() * 1E6));\n OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location),\n getString(R.string.current_location));\n mapOverlays.clear();\n if (locationOverlays.size() > 1) {\n // remove the old user position if there is one\n locationOverlays.removeOverlay(1);\n }\n //add new user position\n locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage));\n mapOverlays.add(locationOverlays);\n //.\n //. calculate / set the mapcenter, zoom to span\n //. see in previous posts\n //.\n RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler);\n rt.start();\n}\n",[471,88219,88220,88224,88229,88233,88238,88243,88247,88252,88257,88262,88267,88272,88277,88282,88287,88292,88297,88301,88306,88311,88316,88321,88326,88331,88335,88340,88345],{"__ignoreMap":469},[474,88221,88222],{"class":476,"line":477},[474,88223,917],{"emptyLinePlaceholder":916},[474,88225,88226],{"class":476,"line":507},[474,88227,88228],{},"public class RouteActivity extends MapActivity implements LocationListener {\n",[474,88230,88231],{"class":476,"line":547},[474,88232,29178],{},[474,88234,88235],{"class":476,"line":584},[474,88236,88237],{},"public void onLocationChanged(Location location) {\n",[474,88239,88240],{"class":476,"line":607},[474,88241,88242],{},"drawUserPosition(location);\n",[474,88244,88245],{"class":476,"line":642},[474,88246,703],{},[474,88248,88249],{"class":476,"line":663},[474,88250,88251],{},"private void drawUserPosition(Location location) {\n",[474,88253,88254],{"class":476,"line":694},[474,88255,88256],{}," GeoPoint currentLocation;\n",[474,88258,88259],{"class":476,"line":700},[474,88260,88261],{}," currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location\n",[474,88263,88264],{"class":476,"line":913},[474,88265,88266],{}," getLongitude() * 1E6));\n",[474,88268,88269],{"class":476,"line":920},[474,88270,88271],{}," OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location),\n",[474,88273,88274],{"class":476,"line":926},[474,88275,88276],{}," getString(R.string.current_location));\n",[474,88278,88279],{"class":476,"line":932},[474,88280,88281],{}," mapOverlays.clear();\n",[474,88283,88284],{"class":476,"line":938},[474,88285,88286],{}," if (locationOverlays.size() > 1) {\n",[474,88288,88289],{"class":476,"line":944},[474,88290,88291],{}," // remove the old user position if there is one\n",[474,88293,88294],{"class":476,"line":950},[474,88295,88296],{}," locationOverlays.removeOverlay(1);\n",[474,88298,88299],{"class":476,"line":956},[474,88300,23666],{},[474,88302,88303],{"class":476,"line":962},[474,88304,88305],{}," //add new user position\n",[474,88307,88308],{"class":476,"line":4876},[474,88309,88310],{}," locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage));\n",[474,88312,88313],{"class":476,"line":4888},[474,88314,88315],{}," mapOverlays.add(locationOverlays);\n",[474,88317,88318],{"class":476,"line":4900},[474,88319,88320],{}," //.\n",[474,88322,88323],{"class":476,"line":4913},[474,88324,88325],{}," //. calculate / set the mapcenter, zoom to span\n",[474,88327,88328],{"class":476,"line":4921},[474,88329,88330],{}," //. see in previous posts\n",[474,88332,88333],{"class":476,"line":4932},[474,88334,88320],{},[474,88336,88337],{"class":476,"line":4938},[474,88338,88339],{}," RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler);\n",[474,88341,88342],{"class":476,"line":4946},[474,88343,88344],{}," rt.start();\n",[474,88346,88347],{"class":476,"line":4952},[474,88348,703],{},[3938,88350,88352],{"id":88351},"make-it-threaded","Make it threaded",[439,88354,88355],{},"But we’re not quite finished yet, because you can’t just put the internet connection and parsing into the main thread!\nThis would block the ui for quite a long time if the users internet connection isn’t so fast.",[439,88357,88358],{},"So what we have to do is to create a handler as an inner class of our Activity that takes messages from the thread which\ngets us the geopoints:",[464,88360,88362],{"className":709,"code":88361,"language":711,"meta":469,"style":469},"\nprivate class RouteHandler extends Handler {\n public void handleMessage(Message msg) {\n boolean error = msg.getData().getBoolean(\"error\", false);\n if (!error) {\n // set the geopoints (we can't just add the overlays\n // to the map here, because it's on a different thread\n geoPoints = (List\u003CGeoPoint>) msg.obj;\n post(updateRoute);\n } else {\n // maybe you want to show an error message here to\n // notice the user that the route can not be displayed\n // because there's no connection to the internet\n }\n }\n }\n",[471,88363,88364,88368,88373,88378,88383,88388,88393,88398,88403,88408,88413,88418,88423,88428,88432,88436],{"__ignoreMap":469},[474,88365,88366],{"class":476,"line":477},[474,88367,917],{"emptyLinePlaceholder":916},[474,88369,88370],{"class":476,"line":507},[474,88371,88372],{},"private class RouteHandler extends Handler {\n",[474,88374,88375],{"class":476,"line":547},[474,88376,88377],{}," public void handleMessage(Message msg) {\n",[474,88379,88380],{"class":476,"line":584},[474,88381,88382],{}," boolean error = msg.getData().getBoolean(\"error\", false);\n",[474,88384,88385],{"class":476,"line":607},[474,88386,88387],{}," if (!error) {\n",[474,88389,88390],{"class":476,"line":642},[474,88391,88392],{}," // set the geopoints (we can't just add the overlays\n",[474,88394,88395],{"class":476,"line":663},[474,88396,88397],{}," // to the map here, because it's on a different thread\n",[474,88399,88400],{"class":476,"line":694},[474,88401,88402],{}," geoPoints = (List\u003CGeoPoint>) msg.obj;\n",[474,88404,88405],{"class":476,"line":700},[474,88406,88407],{}," post(updateRoute);\n",[474,88409,88410],{"class":476,"line":913},[474,88411,88412],{}," } else {\n",[474,88414,88415],{"class":476,"line":920},[474,88416,88417],{}," // maybe you want to show an error message here to\n",[474,88419,88420],{"class":476,"line":926},[474,88421,88422],{}," // notice the user that the route can not be displayed\n",[474,88424,88425],{"class":476,"line":932},[474,88426,88427],{}," // because there's no connection to the internet\n",[474,88429,88430],{"class":476,"line":938},[474,88431,42564],{},[474,88433,88434],{"class":476,"line":944},[474,88435,5704],{},[474,88437,88438],{"class":476,"line":950},[474,88439,1276],{},[439,88441,88442],{},"Send the geopoints from the RouteThread:",[464,88444,88446],{"className":709,"code":88445,"language":711,"meta":469,"style":469},"\nMessage msg = new Message();\nmsg.obj = decodePoly(encoded);\nhandler.dispatchMessage(msg);\n\n",[471,88447,88448,88452,88457,88462],{"__ignoreMap":469},[474,88449,88450],{"class":476,"line":477},[474,88451,917],{"emptyLinePlaceholder":916},[474,88453,88454],{"class":476,"line":507},[474,88455,88456],{},"Message msg = new Message();\n",[474,88458,88459],{"class":476,"line":547},[474,88460,88461],{},"msg.obj = decodePoly(encoded);\n",[474,88463,88464],{"class":476,"line":584},[474,88465,88466],{},"handler.dispatchMessage(msg);\n",[439,88468,88469],{},"(If you have further data to send, use a Bundle object and add it to the Message.)",[439,88471,88472],{},"And now we need the main thread to update the overlays if there are new geopoints available. Again, we need the handler\nto accomplish that, because it can start runnables into the same thread the handler was created in.",[439,88474,88475],{},"First we create a runnable in the Activity:",[464,88477,88479],{"className":709,"code":88478,"language":711,"meta":469,"style":469},"\nfinal Runnable updateRoute = new Runnable() {\n public void run() {\n // draw the path and then invalidate the mapview so that it redraws itself\n drawPath(geoPoints, Color.GREEN);\n mapView.invalidate();\n }\n };\n\n",[471,88480,88481,88485,88490,88495,88500,88505,88510,88514],{"__ignoreMap":469},[474,88482,88483],{"class":476,"line":477},[474,88484,917],{"emptyLinePlaceholder":916},[474,88486,88487],{"class":476,"line":507},[474,88488,88489],{},"final Runnable updateRoute = new Runnable() {\n",[474,88491,88492],{"class":476,"line":547},[474,88493,88494],{}," public void run() {\n",[474,88496,88497],{"class":476,"line":584},[474,88498,88499],{}," // draw the path and then invalidate the mapview so that it redraws itself\n",[474,88501,88502],{"class":476,"line":607},[474,88503,88504],{}," drawPath(geoPoints, Color.GREEN);\n",[474,88506,88507],{"class":476,"line":642},[474,88508,88509],{}," mapView.invalidate();\n",[474,88511,88512],{"class":476,"line":663},[474,88513,5704],{},[474,88515,88516],{"class":476,"line":694},[474,88517,88518],{}," };\n",[439,88520,88521],{},"And all that is left to do is to call it from inside the handler:",[464,88523,88525],{"className":709,"code":88524,"language":711,"meta":469,"style":469},"\npost(updateRoute);\n\n",[471,88526,88527,88531],{"__ignoreMap":469},[474,88528,88529],{"class":476,"line":477},[474,88530,917],{"emptyLinePlaceholder":916},[474,88532,88533],{"class":476,"line":507},[474,88534,88535],{},"post(updateRoute);\n",[439,88537,88538],{},"Anyway, this is how we solved the routing in our app. If you have a question, or have suggestions how we could make it\nbetter, please leave us a comment!",[1024,88540,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":88542},[88543,88544,88545],{"id":87942,"depth":507,"text":87943},{"id":88110,"depth":507,"text":88111},{"id":88351,"depth":507,"text":88352},[11122,1413],"2010-06-16T14:14:35","After you got the route\\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.","https://synyx.de/blog/routing-driving-directions-on-android-part-2-draw-the-route/",{},"/blog/routing-driving-directions-on-android-part-2-draw-the-route",{"title":87924,"description":88553},"After you got the route\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.","blog/routing-driving-directions-on-android-part-2-draw-the-route",[11132,88556,25467,88557,88558],"driving-directions","overlays","routing","After you got the route from wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now. Create a…","ef_SMhSPJEztFJzu7jCXik6lZ16Do7MuCSWGxxWmEuc",{"id":88562,"title":88563,"author":88564,"body":88565,"category":89038,"date":89039,"description":89040,"extension":1034,"link":89041,"meta":89042,"navigation":916,"path":89043,"seo":89044,"slug":88569,"stem":89046,"tags":89047,"teaser":89049,"__hash__":89050},"blog/blog/routing-driving-directions-on-android-part-1-get-the-route.md","Routing / Driving directions on Android – Part 1: Get the route",[190],{"type":432,"value":88566,"toc":89029},[88567,88570,88584,88587,88589,88592,88595,88604,88607,88642,88645,88649,88653,88656,88663,88671,88674,88751,88754,88790,88793,88796,88805,88809,88812,88815,88851,88860,89004,89008,89011,89019,89023,89026],[435,88568,88563],{"id":88569},"routing-driving-directions-on-android-part-1-get-the-route",[439,88571,88572,88573,7267,88578,88583],{},"Complementary to Sebastian’s posts\nabout ",[1002,88574,88577],{"href":88575,"rel":88576},"http://mobile.synyx.de/2010/04/google-maps-on-android/",[1006],"how to navigate with the MapView",[1002,88579,88582],{"href":88580,"rel":88581},"http://mobile.synyx.de/2010/05/google-maps-on-android-part-2-overlays/",[1006],"how to add customized overlays to it",", I\nwant to show you, how you display the route between two points.",[439,88585,88586],{},"Well, the whole thing wouldn’t be a pain now, if google hadn’t removed the DrivingDirection class since API 1.0, with\nwhich you could solve this problem in no time and with just a few lines of code. But because they did remove it, we have\nto go through a little bit more trouble.",[3938,88588,10009],{"id":10008},[439,88590,88591],{},"First you have to realize, if you only need the coordinates of the route, or also driving directions like “go left on\nthis street, drive right on that street…”.",[439,88593,88594],{},"The first step in both cases is to get the two locations between which the routing should happen. In our case we wanted\nto show the route from the current location of the user to our office, so one of the points is fixed:",[464,88596,88598],{"className":709,"code":88597,"language":711,"meta":469,"style":469},"synyxGeoPoint = new GeoPoint(49002175, 8394160);\n",[471,88599,88600],{"__ignoreMap":469},[474,88601,88602],{"class":476,"line":477},[474,88603,88597],{},[439,88605,88606],{},"And the user location is also quite easy to get:",[464,88608,88610],{"className":709,"code":88609,"language":711,"meta":469,"style":469},"locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\nCriteria criteria = new Criteria();\ncriteria.setAccuracy(Criteria.ACCURACY_FINE);\ncriteria.setAltitudeRequired(false);\nLocation lastKnownLocation =\nlocationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));\n",[471,88611,88612,88617,88622,88627,88632,88637],{"__ignoreMap":469},[474,88613,88614],{"class":476,"line":477},[474,88615,88616],{},"locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\n",[474,88618,88619],{"class":476,"line":507},[474,88620,88621],{},"Criteria criteria = new Criteria();\n",[474,88623,88624],{"class":476,"line":547},[474,88625,88626],{},"criteria.setAccuracy(Criteria.ACCURACY_FINE);\n",[474,88628,88629],{"class":476,"line":584},[474,88630,88631],{},"criteria.setAltitudeRequired(false);\n",[474,88633,88634],{"class":476,"line":607},[474,88635,88636],{},"Location lastKnownLocation =\n",[474,88638,88639],{"class":476,"line":642},[474,88640,88641],{},"locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));\n",[439,88643,88644],{},"With this we get the last known, accurate location of the user. So all there is left now, is to get the route.",[3938,88646,88648],{"id":88647},"getting-the-geopoints-from-google-maps","Getting the geopoints from google maps",[1065,88650,88652],{"id":88651},"kml-with-driving-directions","Kml (with driving directions)",[439,88654,88655],{},"If you need the driving directions, you can build yourself a url like this one to get a kml file with all the\ninformation:",[439,88657,88658],{},[1002,88659,88662],{"href":88660,"rel":88661},"http://maps.google.com/maps?f%5C=d%5C&hl%5C=en%5C&saddr%5C=9%5C.18333,48%5C.7667%5C&daddr%5C=8%5C.394160,49%5C.002175%5C&ie%5C=UTF8%5C&0%5C&om%5C=0%5C&z%5C=20%5C&output%5C=kml",[1006],"http://maps.google.com/maps?f\\=d\\&hl\\=en\\&saddr\\=9\\.18333,48\\.7667\\&daddr\\=8\\.394160,49\\.002175\\&ie\\=UTF8\\&0\\&om\\=0\\&z\\=20\\&output\\=kml",[439,88664,88665,88666,17548],{},"(For the list of parameters of google maps, see\nhere: ",[1002,88667,88670],{"href":88668,"rel":88669},"https://web.archive.org/web/20080901081831/http://mapki.com/wiki/Google_Map_Parameters",[1006],"mapki.com",[439,88672,88673],{},"Here’s how we did it:",[464,88675,88677],{"className":709,"code":88676,"language":711,"meta":469,"style":469},"\npublic String getUrl(GeoPoint src, GeoPoint dest){\nStringBuilder urlString = new StringBuilder();\nurlString.append(\"http://maps.google.com/maps?f=d&hl=en\");\nurlString.append(\"&saddr=\");\nurlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));\nurlString.append(\",\");\nurlString.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));\nurlString.append(\"&daddr=\");// to\nurlString.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));\nurlString.append(\",\");\nurlString.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));\nurlString.append(\"&ie=UTF8&0&om=0&output=kml\");\nreturn urlString;\n}\n",[471,88678,88679,88683,88688,88693,88698,88703,88708,88713,88718,88723,88728,88732,88737,88742,88747],{"__ignoreMap":469},[474,88680,88681],{"class":476,"line":477},[474,88682,917],{"emptyLinePlaceholder":916},[474,88684,88685],{"class":476,"line":507},[474,88686,88687],{},"public String getUrl(GeoPoint src, GeoPoint dest){\n",[474,88689,88690],{"class":476,"line":547},[474,88691,88692],{},"StringBuilder urlString = new StringBuilder();\n",[474,88694,88695],{"class":476,"line":584},[474,88696,88697],{},"urlString.append(\"http://maps.google.com/maps?f=d&hl=en\");\n",[474,88699,88700],{"class":476,"line":607},[474,88701,88702],{},"urlString.append(\"&saddr=\");\n",[474,88704,88705],{"class":476,"line":642},[474,88706,88707],{},"urlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));\n",[474,88709,88710],{"class":476,"line":663},[474,88711,88712],{},"urlString.append(\",\");\n",[474,88714,88715],{"class":476,"line":694},[474,88716,88717],{},"urlString.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));\n",[474,88719,88720],{"class":476,"line":700},[474,88721,88722],{},"urlString.append(\"&daddr=\");// to\n",[474,88724,88725],{"class":476,"line":913},[474,88726,88727],{},"urlString.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));\n",[474,88729,88730],{"class":476,"line":920},[474,88731,88712],{},[474,88733,88734],{"class":476,"line":926},[474,88735,88736],{},"urlString.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));\n",[474,88738,88739],{"class":476,"line":932},[474,88740,88741],{},"urlString.append(\"&ie=UTF8&0&om=0&output=kml\");\n",[474,88743,88744],{"class":476,"line":938},[474,88745,88746],{},"return urlString;\n",[474,88748,88749],{"class":476,"line":944},[474,88750,703],{},[439,88752,88753],{},"The file you get from this url looks like this:",[464,88755,88757],{"className":6253,"code":88756,"language":6255,"meta":469,"style":469},"\nHohenheimer Str./B27 to Karlstraße/L561\n....\nHead southeast on Hohenheimer Str./B27 toward Bopserwaldstraße Continue to follow B27\n....\n9.183560,48.766820,0.000000 9.183690,48.766670,0.000000 9.183640,48.766480,0.000000 9.183470,48.766380,0.000000\n....\n",[471,88758,88759,88763,88768,88772,88777,88781,88786],{"__ignoreMap":469},[474,88760,88761],{"class":476,"line":477},[474,88762,917],{"emptyLinePlaceholder":916},[474,88764,88765],{"class":476,"line":507},[474,88766,88767],{},"Hohenheimer Str./B27 to Karlstraße/L561\n",[474,88769,88770],{"class":476,"line":547},[474,88771,12731],{},[474,88773,88774],{"class":476,"line":584},[474,88775,88776],{},"Head southeast on Hohenheimer Str./B27 toward Bopserwaldstraße Continue to follow B27\n",[474,88778,88779],{"class":476,"line":607},[474,88780,12731],{},[474,88782,88783],{"class":476,"line":642},[474,88784,88785],{},"9.183560,48.766820,0.000000 9.183690,48.766670,0.000000 9.183640,48.766480,0.000000 9.183470,48.766380,0.000000\n",[474,88787,88788],{"class":476,"line":663},[474,88789,12731],{},[439,88791,88792],{},"(the first number of each pair is the longitude, the second the latitude)",[439,88794,88795],{},"To convert them to GeoPoints use:",[464,88797,88799],{"className":709,"code":88798,"language":711,"meta":469,"style":469},"GeoPoint geoPoint = new GeoPoint((int)(Double.parseDouble(latitude[0])*1E6),(int)(Double.parseDouble(longitude[0])*1E6));\n",[471,88800,88801],{"__ignoreMap":469},[474,88802,88803],{"class":476,"line":477},[474,88804,88798],{},[1065,88806,88808],{"id":88807},"json-without-driving-directions","JSON (without driving directions)",[439,88810,88811],{},"If you don’t need the driving directions, you can save a few kilobytes by changing the output parameter to\noutput=dragdir (else it’s exactly the same url as above) , which delivers you a json string with encrypted geopoints.",[439,88813,88814],{},"again an example of what the server returns:",[464,88816,88818],{"className":15199,"code":88817,"language":15201,"meta":469,"style":469},"{tooltipHtml:\" (572x26#160;km / 5 hours 14 mins)\",polylines:[{id:\"route0\",points:\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| .....\n",[471,88819,88820],{"__ignoreMap":469},[474,88821,88822,88824,88827,88829,88832,88834,88837,88840,88843,88846,88849],{"class":476,"line":477},[474,88823,68060],{"class":503},[474,88825,88826],{"class":480},"tooltipHtml",[474,88828,6562],{"class":503},[474,88830,88831],{"class":484},"\" (572x26#160;km / 5 hours 14 mins)\"",[474,88833,1489],{"class":503},[474,88835,88836],{"class":480},"polylines",[474,88838,88839],{"class":503},":[{id:",[474,88841,88842],{"class":484},"\"route0\"",[474,88844,88845],{"class":503},",points:",[474,88847,88848],{"class":484},"\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| ....",[474,88850,8993],{"class":523},[439,88852,88853,88854,88859],{},"So as you can see, you can’t just parse the string and get the geopoints out of it. You first have to decode them.\nHere’s a method that solves this for you (algorithm\nfrom ",[1002,88855,88858],{"href":88856,"rel":88857},"http://facstaff.unca.edu/mcmcclur/googlemaps/encodepolyline/",[1006],"http://facstaff.unca.edu/",") :",[464,88861,88863],{"className":709,"code":88862,"language":711,"meta":469,"style":469},"// get only the encoded geopoints\nencoded = encoded.split(\"points:\"\")[1].split(\"\",\")[0];\n// replace two backslashes by one (some error from the transmission)\nencoded = encoded.replace(\"\\\\\", \"\\\");\n//decoding\nList poly = new ArrayList();\n int index = 0, len = encoded.length();\n int lat = 0, lng = 0;\n while (index \u003C len) {\n int b, shift = 0, result = 0;\n do {\n b = encoded.charAt(index++) - 63;\n result |= (b & 0x1f) \u003C\u003C shift;\n shift += 5;\n } while (b >= 0x20);\n int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n lat += dlat;\n shift = 0;\n result = 0;\n do {\n b = encoded.charAt(index++) - 63;\n result |= (b & 0x1f) \u003C\u003C shift;\n shift += 5;\n } while (b >= 0x20);\n int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n lng += dlng;\n GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));\n poly.add(p);\n }\n",[471,88864,88865,88870,88875,88880,88885,88890,88895,88900,88905,88910,88915,88920,88925,88930,88935,88940,88945,88950,88955,88960,88964,88968,88972,88976,88980,88985,88990,88995,89000],{"__ignoreMap":469},[474,88866,88867],{"class":476,"line":477},[474,88868,88869],{},"// get only the encoded geopoints\n",[474,88871,88872],{"class":476,"line":507},[474,88873,88874],{},"encoded = encoded.split(\"points:\"\")[1].split(\"\",\")[0];\n",[474,88876,88877],{"class":476,"line":547},[474,88878,88879],{},"// replace two backslashes by one (some error from the transmission)\n",[474,88881,88882],{"class":476,"line":584},[474,88883,88884],{},"encoded = encoded.replace(\"\\\\\", \"\\\");\n",[474,88886,88887],{"class":476,"line":607},[474,88888,88889],{},"//decoding\n",[474,88891,88892],{"class":476,"line":642},[474,88893,88894],{},"List poly = new ArrayList();\n",[474,88896,88897],{"class":476,"line":663},[474,88898,88899],{}," int index = 0, len = encoded.length();\n",[474,88901,88902],{"class":476,"line":694},[474,88903,88904],{}," int lat = 0, lng = 0;\n",[474,88906,88907],{"class":476,"line":700},[474,88908,88909],{}," while (index \u003C len) {\n",[474,88911,88912],{"class":476,"line":913},[474,88913,88914],{}," int b, shift = 0, result = 0;\n",[474,88916,88917],{"class":476,"line":920},[474,88918,88919],{}," do {\n",[474,88921,88922],{"class":476,"line":926},[474,88923,88924],{}," b = encoded.charAt(index++) - 63;\n",[474,88926,88927],{"class":476,"line":932},[474,88928,88929],{}," result |= (b & 0x1f) \u003C\u003C shift;\n",[474,88931,88932],{"class":476,"line":938},[474,88933,88934],{}," shift += 5;\n",[474,88936,88937],{"class":476,"line":944},[474,88938,88939],{}," } while (b >= 0x20);\n",[474,88941,88942],{"class":476,"line":950},[474,88943,88944],{}," int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[474,88946,88947],{"class":476,"line":956},[474,88948,88949],{}," lat += dlat;\n",[474,88951,88952],{"class":476,"line":962},[474,88953,88954],{}," shift = 0;\n",[474,88956,88957],{"class":476,"line":4876},[474,88958,88959],{}," result = 0;\n",[474,88961,88962],{"class":476,"line":4888},[474,88963,88919],{},[474,88965,88966],{"class":476,"line":4900},[474,88967,88924],{},[474,88969,88970],{"class":476,"line":4913},[474,88971,88929],{},[474,88973,88974],{"class":476,"line":4921},[474,88975,88934],{},[474,88977,88978],{"class":476,"line":4932},[474,88979,88939],{},[474,88981,88982],{"class":476,"line":4938},[474,88983,88984],{}," int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[474,88986,88987],{"class":476,"line":4946},[474,88988,88989],{}," lng += dlng;\n",[474,88991,88992],{"class":476,"line":4952},[474,88993,88994],{}," GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));\n",[474,88996,88997],{"class":476,"line":4957},[474,88998,88999],{}," poly.add(p);\n",[474,89001,89002],{"class":476,"line":4969},[474,89003,5704],{},[3938,89005,89007],{"id":89006},"getting-the-geopoints-from-open-streetmap","Getting the geopoints from open streetmap",[439,89009,89010],{},"You can also get the geopoints from open streetmap. It’s quite the same procedure, so i don’t write it all again.",[439,89012,89013,89014],{},"Here you can see for yourself: ",[1002,89015,89018],{"href":89016,"rel":89017},"http://wiki.openstreetmap.org/wiki/YOURS#Routing_API",[1006],"YOURS Routing_API",[3938,89020,89022],{"id":89021},"whats-in-the-next-post","What’s in the next post",[439,89024,89025],{},"That’s it for today, in the next post i will show you how to draw the route on your MapView properly.",[1024,89027,89028],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":469,"searchDepth":507,"depth":507,"links":89030},[89031,89032,89036,89037],{"id":10008,"depth":507,"text":10009},{"id":88647,"depth":507,"text":88648,"children":89033},[89034,89035],{"id":88651,"depth":547,"text":88652},{"id":88807,"depth":547,"text":88808},{"id":89006,"depth":507,"text":89007},{"id":89021,"depth":507,"text":89022},[11122,1413],"2010-06-14T14:00:19","Complementary to Sebastian’s posts\\nabout how to navigate with the MapView\\nand how to add customized overlays to it, I\\nwant to show you, how you display the route between two points.","https://synyx.de/blog/routing-driving-directions-on-android-part-1-get-the-route/",{},"/blog/routing-driving-directions-on-android-part-1-get-the-route",{"title":88563,"description":89045},"Complementary to Sebastian’s posts\nabout how to navigate with the MapView\nand how to add customized overlays to it, I\nwant to show you, how you display the route between two points.","blog/routing-driving-directions-on-android-part-1-get-the-route",[11132,89048,25467,88558],"driving-direction","Complementary to Sebastian’s posts about how to navigate with the MapView and how to add customized overlays to it, I want to show you, how you display the route between…","xtRVy4MOC_yuKWFtuiezEbJjdTQLaLS26IGAAOAVvDk",{"id":89052,"title":77261,"author":89053,"body":89054,"category":89195,"date":89196,"description":89197,"extension":1034,"link":89198,"meta":89199,"navigation":916,"path":89200,"seo":89201,"slug":89058,"stem":89202,"tags":89203,"teaser":89206,"__hash__":89207},"blog/blog/template-based-document-generation-using-odfdom.md",[148],{"type":432,"value":89055,"toc":89189},[89056,89059,89062,89077,89081,89090,89101,89104,89108,89116,89119,89122,89125,89128,89131,89133,89136,89139,89144,89147,89150,89153,89156,89159,89162,89165,89168,89171,89174,89176,89178,89181,89184,89186],[435,89057,77261],{"id":89058},"template-based-document-generation-using-odfdom",[439,89060,89061],{},"Generating documents from data that is managed by a web application is a quite common need. Think about letters that are\ngenerated for a customer relationship management system or bills that are to be send for membership fees. For corporate\nidentity reasons you don’t want these documents to look like generated from a plain text file but you want to have\nlogos, tables, address labels and so on.",[439,89063,89064,89065,89070,89071,89076],{},"As the people that are designing the look of these documents often are not programmers it is a good idea to provide a\nway to use well know tools for creating and editing templates for these documents. What we have been doing for some time\nis to let the customer create template documents using ",[1002,89066,89069],{"href":89067,"rel":89068},"http://www.openoffice.org/",[1006],"OpenOffice.org",", the open source\nword processor, and transform these documents programmatically. OpenOffice.org uses the\nstandardized ",[1002,89072,89075],{"href":89073,"rel":89074},"http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office",[1006],"Open Document"," format to save its\ndocuments. Open Document files are zip archives that contain some XML documents as well as additional content (images,\nmacros, …).",[1065,89078,89080],{"id":89079},"the-uno-approach","The UNO approach",[439,89082,89083,89084,89089],{},"One of the older approaches we have been using for document processing is to access an OpenOffice.org instance running\non the server using the ",[1002,89085,89088],{"href":89086,"rel":89087},"http://wiki.services.openoffice.org/wiki/Uno",[1006],"UNO API",". UNO is a language agnostic API that\nprovides access to a lot of functionality of OpenOffice.org using an IDL. Though really powerful this approach also\nyields some drawbacks:",[994,89091,89092,89095,89098],{},[997,89093,89094],{},"Understanding and learning the UNO API is hard and takes a lot of time",[997,89096,89097],{},"Some features of a document can’t be accessed using the API (e.g. the id of form control elements is saved in the\ndocument but is not accessible using UNO)",[997,89099,89100],{},"An instance of OpenOffice.org can only be used by one thread at a time so you need some kind of instance pooling.",[439,89102,89103],{},"These drawbacks make it really hard to design and implement a robust system that can handle the load of a typical web\napplication and can still be maintained by a lot of developers.",[1065,89105,89107],{"id":89106},"odfdom","ODFDOM",[439,89109,89110,89111,89115],{},"Some time after the standardization of the Open Document format a new project was\nborn: ",[1002,89112,89107],{"href":89113,"rel":89114},"http://odftoolkit.org/projects/odfdom/pages/Home",[1006],", a sub project of the odftoolkt project. ODFDOM is a\npure Java API that provides both low level DOM access to the Open Document XML format as well as convenience\nfunctionality to manipulate document data.",[439,89117,89118],{},"As with ODFDOM the application and the document generation all run on the Java Virtual Machine it is easier to maintain\nfrom an adminitrators perspective. Also in contrast to the UNO API ODFDOM is really easy to use.",[439,89120,89121],{},"The following snippet creates a new text document, inserts some text and saves the document to a temp file.",[439,89123,89124],{},"`OdfTextDocument doc = OdfTextDocument.newTextDocument();",[439,89126,89127],{},"doc.addText(\"Hello World!\");",[439,89129,89130],{},"doc.save(File.createTempFile(\"odfdom\", \".odt\"));`",[1065,89132,71263],{"id":71262},[439,89134,89135],{},"To use Odfdom for templating you can choose one of the many placeholder approaches in OpenOffice.org. A very simple one\nis the use of user fields. To insert a user field in OpenOffice.org create a new document and go to Insert => Field\ncommand => Others. There you choose the tab variables and user field. You can add a name and a value. The value in our\ncase is only there to have a visual feedback when designing the document. The user field will be replaced automatically.",[439,89137,89138],{},"Let’s see how we can replace our placeholder value. The values for user fields as inserted above are stored in a node\n\u003Ctext:user-field-decl>. This is an excerpt from the Open Document content.xml for a simple example document:",[439,89140,89141],{},[471,89142,89143],{},"\u003Ctext:user-field-decl office:value-type=\"string\" office:string-value=\"hello\" text:name=\"test\"/>",[439,89145,89146],{},"The user field is named test, it’s initial value for visual feedback is set to “hello”.",[439,89148,89149],{},"Imagine that the data that we want to replace with the values in the template is stored in a simple Map of Strings. To\nreplace all dummy values with values from you application you can access the nodes using the method\ngetElementsByTagName(“element”):",[439,89151,89152],{},"`Map\u003CString, String> values = new HashMap\u003CString, String>();",[439,89154,89155],{},"values.put(\"test\", \"inserted automatically\");",[439,89157,89158],{},"OdfDocument doc = OdfDocument.loadDocument(\"/path/to/template.odt\");",[439,89160,89161],{},"NodeList nodes = doc.getOfficeBody().getElementsByTagName(OdfTextUserFieldDecl.ELEMENT_NAME.getQName());",[439,89163,89164],{},"for (int i = 0; i \u003C nodes.getLength(); i++) {",[439,89166,89167],{},"OdfTextUserFieldDecl element = (OdfTextUserFieldDecl) nodes.item(i);",[439,89169,89170],{},"if (values.containsKey(element.getTextNameAttribute())) {",[439,89172,89173],{},"element.setOfficeStringValueAttribute(values.get(element.getTextNameAttribute()));",[439,89175,19768],{},[439,89177,19768],{},[439,89179,89180],{},"doc.save(\"/path/to/result.odt\");`",[439,89182,89183],{},"When running the code above, the value in the document is replaced with the value set programmatically.",[1065,89185,9392],{"id":9391},[439,89187,89188],{},"So far we are running code using ODFDOM for document generation successfully in two larger projects that have been\ndeveloped recently. We believe that ODFDOM will help us delivering additional value for our customers with less\ndevelopment effort.",{"title":469,"searchDepth":507,"depth":507,"links":89190},[89191,89192,89193,89194],{"id":89079,"depth":547,"text":89080},{"id":89106,"depth":547,"text":89107},{"id":71262,"depth":547,"text":71263},{"id":9391,"depth":547,"text":9392},[1030,1412],"2010-06-13T17:57:40","Generating documents from data that is managed by a web application is a quite common need. Think about letters that are\\ngenerated for a customer relationship management system or bills that are to be send for membership fees. For corporate\\nidentity reasons you don’t want these documents to look like generated from a plain text file but you want to have\\nlogos, tables, address labels and so on.","https://synyx.de/blog/template-based-document-generation-using-odfdom/",{},"/blog/template-based-document-generation-using-odfdom",{"title":77261,"description":89061},"blog/template-based-document-generation-using-odfdom",[89204,711,89106,89205],"document-management","openoffice-org","Generating documents from data that is managed by a web application is a quite common need. Think about letters that are generated for a customer relationship management system or bills…","Te0CynJTMVBbqnvWt24B1elamQEf_B4gLPgjiTihm8M",{"id":89209,"title":89210,"author":89211,"body":89212,"category":89237,"date":89238,"description":89219,"extension":1034,"link":89239,"meta":89240,"navigation":916,"path":89241,"seo":89242,"slug":89216,"stem":89243,"tags":89244,"teaser":89245,"__hash__":89246},"blog/blog/praktikum-bei-synyx.md","Praktikum bei Synyx",[109],{"type":432,"value":89213,"toc":89235},[89214,89217,89220,89223,89226,89229,89232],[435,89215,89210],{"id":89216},"praktikum-bei-synyx",[439,89218,89219],{},"Hallo, mein Name ist Sebastian Schulte (Basti) und ich habe hier gerade ein einwöchiges Praktikum absolviert.",[439,89221,89222],{},"Der Kontakt zu Synyx kam durch Max Ferstl zustande, der in dieser Zeit auch mein “direkter Vorgesetzter” war. Durch ihn\nhabe ich einerseits von der im September frei werdenden Azubi-Stelle als auch von der angenehmen Arbeits-Atmosphäre\nhier erfahren, von der ich mich ja dann auch selbst überzeugen konnte.",[439,89224,89225],{},"Obwohl ich bisher nur sehr wenig / gar keine IT-Erfahrung vorweisen kann, wollte ich mir die Chance zumindest mal\nreinzuschauen nicht entgehen lassen.",[439,89227,89228],{},"Nachdem ein Termin gefunden war (“Komm einfach vorbei” 🙂 ), richteten wir zunächst meinen Arbeitsplatz ein, von dem aus\nich in den kommenden Tagen einen Praktikums-Rechner aufsetzte, Apache und Tomcat installierte und erste Erfahrungen\ndamit sammelte. Aufgrund meines geringen Vorwissens (z.B. erstes Arbeiten mit einer Text-Konsole) gestaltete sich der\nAnfang recht langwierig und ich verbrachte wohl die meiste Zeit auf google, um nicht alle fünf Minuten nachfragen zu\nmüssen ^^. Natürlich wurden meine Fragen trotzdem beantwortet und so konnte ich mich langsam “durchwursteln”.",[439,89230,89231],{},"Abschließend kann ich sagen, daß mir in dieser Woche ein kleiner Einblick in die Thematik gewährt und dadurch auch mein\nInteresse daran geweckt wurde. In besonderer Erinnerung wird mir aber auf jeden Fall die gute Stimmung und die\nUnkompliziertheit hier bleiben. Obwohl ich nur mit den SysAdmins direkt zu tun hatte, hat mir das gesamte Umfeld, das\nunbürokratische und entspannte Miteinander sehr gut gefallen!",[439,89233,89234],{},"Hoffentlich bis bald! Basti",{"title":469,"searchDepth":507,"depth":507,"links":89236},[],[13208],"2010-06-08T16:21:34","https://synyx.de/blog/praktikum-bei-synyx/",{},"/blog/praktikum-bei-synyx",{"title":89210,"description":89219},"blog/praktikum-bei-synyx",[11253],"Hallo, mein Name ist Sebastian Schulte (Basti) und ich habe hier gerade ein einwöchiges Praktikum absolviert. Der Kontakt zu Synyx kam durch Max Ferstl zustande, der in dieser Zeit auch…","yEaV5rTt3J7BLdn-e6UGu2Jm6o8yHRQ8SLMC2V-mdKQ",{"id":89248,"title":89249,"author":89250,"body":89251,"category":89281,"date":89282,"description":89283,"extension":1034,"link":89284,"meta":89285,"navigation":916,"path":89286,"seo":89287,"slug":89289,"stem":89290,"tags":89291,"teaser":89293,"__hash__":89294},"blog/blog/uberladen-vs-trivialisieren-java-magazin-artikel.md","Überladen vs. Trivialisieren – Zwischen Platin und Blech",[172],{"type":432,"value":89252,"toc":89279},[89253,89256,89276],[435,89254,89249],{"id":89255},"überladen-vs-trivialisieren-zwischen-platin-und-blech",[439,89257,89258,89259,89263,89264,89269,89270,89275],{},"Das Interview mit Joachim Arrasz, Softwarearchitekt bei ",[1002,89260,89262],{"href":26130,"rel":89261},[1006],"Synyx GmbH & Co. KG",",\nund ",[1002,89265,89268],{"href":89266,"rel":89267},"http://www.pbit.org/index.html",[1006],"Pavlo Baron",", Enterprise Architekt in München, ist nun\nim ",[1002,89271,89274],{"href":89272,"rel":89273},"http://it-republik.de/jaxenter/java-magazin-ausgaben/Lucene-000399.html",[1006],"Java Magazin"," erschienen. In der Rubrik\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.",[439,89277,89278],{},"In der Diskussion geht es um die Balance zwischen minimalistischen und total überzogenen Lösungswegen in der\nSoftwarearchitektur. Anhand eines konkreten Beispiels aus der Praxis diskutieren Joachim Arrasz und Pavlo Baron, wie man\neine Lösung überladen oder allzu sehr vereinfachen kann. Und vor allem: Wie findet man die goldene Mitte?",{"title":469,"searchDepth":507,"depth":507,"links":89280},[],[1030],"2010-06-08T13:28:00","Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG,\\nund Pavlo Baron, Enterprise Architekt in München, ist nun\\nim Java Magazin erschienen. In der Rubrik\\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.","https://synyx.de/blog/uberladen-vs-trivialisieren-java-magazin-artikel/",{},"/blog/uberladen-vs-trivialisieren-java-magazin-artikel",{"title":89249,"description":89288},"Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG,\nund Pavlo Baron, Enterprise Architekt in München, ist nun\nim Java Magazin erschienen. In der Rubrik\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.","uberladen-vs-trivialisieren-java-magazin-artikel","blog/uberladen-vs-trivialisieren-java-magazin-artikel",[89292,8279],"losungsweg","Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG, und Pavlo Baron, Enterprise Architekt in München, ist nun im Java Magazin erschienen. In der Rubrik “Architektur” erschien…","nhsOQi94sEgrfo5KsCn9ZYMiAsYPQWlLxJhTZ_KBJOA",{"id":89296,"title":89297,"author":89298,"body":89299,"category":89439,"date":89440,"description":89306,"extension":1034,"link":89441,"meta":89442,"navigation":916,"path":89443,"seo":89444,"slug":89303,"stem":89445,"tags":89446,"teaser":89449,"__hash__":89450},"blog/blog/google-maps-on-maemo-5-part-1.md","Google Maps on Maemo 5 Part 1",[109],{"type":432,"value":89300,"toc":89436},[89301,89304,89307,89311,89320,89323,89326,89329,89332,89335,89338,89341,89344,89347,89349,89352,89354,89357,89360,89362,89365,89368,89371,89374,89376,89378,89381,89384,89387,89389,89391,89394,89397,89399,89402,89405,89407,89410,89415,89418,89427,89430,89433],[435,89302,89297],{"id":89303},"google-maps-on-maemo-5-part-1",[439,89305,89306],{},"In this post i will show you how to realize a Maemo 5 Qt 4.6 application with google maps integration.",[3938,89308,89310],{"id":89309},"the-map","The map:",[439,89312,89313,89314,89319],{},"There is a good short tutorial with included source code: ",[1002,89315,89318],{"href":89316,"rel":89317},"http://efforts.embedded.ufcg.edu.br/qt/?p%5C=80%5C",[1006],"http://efforts.embedded.ufcg.edu.br/qt/?p\\=80\\",". Because there\nis no native library for the N900 you have to go another way to get the map in your application. The tutorial describes\nhowto load and render the map by the webkit library. I used parts of the html file of that project for my little app.",[439,89321,89322],{},"The idea is quite simple. Webkit will render a webpage insider your app. That webpage consists of javascript methods\nwhich use the Google Maps-API. The javascript methods can be triggered from the app. The map class acts as proxy for\nthe comunication between your app and the website. Quite simple hm?",[439,89324,89325],{},"The benefit of that method is: you may use the complete power of Google Maps-API. But if your internet connection is\nslow, you will have to wait until the map is loaded and that may need some time and a white page just looks ugly. Native\nimplementations often have some startup animations that have an effect of compensation.",[439,89327,89328],{},"Sourcecode of index.html with javascript methods for google’s api:",[439,89330,89331],{},"`function initialize(){",[439,89333,89334],{},"map = new GMap2(document.getElementById(\"map\"));",[439,89336,89337],{},"map.setCenter( new GLatLng(49.002397,8.394251),10 );",[439,89339,89340],{},"var point = new GLatLng(49.002397,8.394251);",[439,89342,89343],{},"map.addOverlay(new GMarker(point));",[439,89345,89346],{},"openSynyx();",[439,89348,34952],{},[439,89350,89351],{},"`function openSynyx()",[439,89353,68060],{},[439,89355,89356],{},"map.setCenter( new GLatLng(49.002397,8.394251),15 );",[439,89358,89359],{},"map.openInfoWindow(map.getCenter(),document.createTextNode(\"Synyx GmbH & Co. KG\"));",[439,89361,34952],{},[439,89363,89364],{},"`function route(from){",[439,89366,89367],{},"map.clearOverlays();",[439,89369,89370],{},"directions = new GDirections(map);",[439,89372,89373],{},"directions.load(\"from: \"+from+\" to: Karlsruhe, Karlstrasse 68\");",[439,89375,34952],{},[439,89377,8990],{},[15027,89379],{"id":25467,"style":89380},"width: 450px; height: 400px",[439,89382,89383],{},"The map.cpp acts is a Children of QWebView and acts as poxy to the javascript methods:",[439,89385,89386],{},"`Map::Map(QWidget *parent) : QWebView(parent)",[439,89388,68060],{},[439,89390,34952],{},[439,89392,89393],{},"`void Map::naviFrom(QString from){",[439,89395,89396],{},"this->page()->mainFrame()->evaluateJavaScript(QString(\"route(\"%1\")\").arg(from));",[439,89398,34952],{},[439,89400,89401],{},"`void Map::openSynyx(){",[439,89403,89404],{},"this->page()->mainFrame()->evaluateJavaScript(\"openSynyx()\");",[439,89406,34952],{},[439,89408,89409],{},"mainscreen.cpp initializes the map with the path to the html file",[439,89411,89412],{},[471,89413,89414],{},"map->load(QUrl(\"./index.html\") ) ;",[439,89416,89417],{},"Now if you put all things together in an app, by adding a few buttons, a textfield and connect them to the Map::naviFrom\nmethod it could look like this:",[439,89419,89420],{},[1002,89421,89424],{"href":89422,"rel":89423},"https://media.synyx.de/uploads//2010/06/maps_navi.png",[1006],[2205,89425],{"alt":469,"src":89426},"https://media.synyx.de/uploads//2010/06/maps_navi-300x180.png",[439,89428,89429],{},"google maps in maemo 5",[439,89431,89432],{},"The evaluation of the position (coordinates, or town labels, street names etc.) and the rendering of the route will be\ndone by google’s api. So in contrast to other mobile plattforms with native libraries, you do not have to bother about\nthat.",[439,89434,89435],{},"Part 2 will describe a way how to use the internal gps device of your N900 and how to use callback mechanisms for\nabstraction of the position handling.",{"title":469,"searchDepth":507,"depth":507,"links":89437},[89438],{"id":89309,"depth":507,"text":89310},[11122,1413],"2010-06-07T22:11:18","https://synyx.de/blog/google-maps-on-maemo-5-part-1/",{},"/blog/google-maps-on-maemo-5-part-1",{"title":89297,"description":89306},"blog/google-maps-on-maemo-5-part-1",[89447,85548,89448],"google-maps","qt-4-6","In this post i will show you how to realize a Maemo 5 Qt 4.6 application with google maps integration. The map: There is a good short tutorial with included…","pPyd-UEDzPs0FLjvbvthvyTqGjDaT34OjhGprt11KeM",{"id":89452,"title":78709,"author":89453,"body":89454,"category":89488,"date":89489,"description":89490,"extension":1034,"link":89491,"meta":89492,"navigation":916,"path":89493,"seo":89494,"slug":89458,"stem":89496,"tags":89497,"teaser":89498,"__hash__":89499},"blog/blog/wwdc-2010.md",[223],{"type":432,"value":89455,"toc":89486},[89456,89459,89468,89483],[435,89457,78709],{"id":89458},"wwdc-2010",[439,89460,89461,89462,89467],{},"I’m going to ",[1002,89463,89466],{"href":89464,"rel":89465},"http://developer.apple.com/wwdc/",[1006],"Apple’s WWDC"," this year and I’m pretty excited to fully dive into iPhone\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.",[439,89469,89470,89471,89476,89477,89482],{},"The conference is ",[1002,89472,89475],{"href":89473,"rel":89474},"http://developer.apple.com/wwdc/sessions/",[1006],"mostly covering iPhone"," related topics. There\nare ",[1002,89478,89481],{"href":89479,"rel":89480},"http://www.fscklog.com/2010/05/apple-best%C3%A4tigt-wwdc-keynote-mit-steve-jobs-am-7-juni.html",[1006],"rumors"," that Steven Jobs\nwill present the new iPhone on Monday’s keynote. I’ll make sure I won’t miss that. My schedule for the week is already\nplanned with topics like game development, multitasking and user interface design.",[439,89484,89485],{},"If you are going to WWDC as well and would like to meet up, let me know.",{"title":469,"searchDepth":507,"depth":507,"links":89487},[],[11122],"2010-06-03T15:12:08","I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone\\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.","https://synyx.de/blog/wwdc-2010/",{},"/blog/wwdc-2010",{"title":78709,"description":89495},"I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone\ndevelopment for the whole next week. That it takes place in San Francisco is a nice bonus, as well.","blog/wwdc-2010",[63945,8276,78398],"I’m going to Apple’s WWDC this year and I’m pretty excited to fully dive into iPhone development for the whole next week. That it takes place in San Francisco is…","0PlPIuqAd36_OiWhK21AO20tc4c6Y1ucyQYMRpnc0aQ",{"id":89501,"title":89502,"author":89503,"body":89504,"category":89590,"date":89591,"description":469,"extension":1034,"link":89592,"meta":89593,"navigation":916,"path":89594,"seo":89595,"slug":89596,"stem":89597,"tags":89598,"teaser":89599,"__hash__":89600},"blog/blog/howto-startup-with-maemo-and-qt-4-6-step2-configure-ide.md","Howto startup with Maemo and Qt 4.6 – Step2: Configure your IDE",[109],{"type":432,"value":89505,"toc":89583},[89506,89509,89513,89522,89525,89534,89537,89541,89544,89554,89556,89559,89563,89566,89570,89576,89580],[435,89507,89502],{"id":89508},"howto-startup-with-maemo-and-qt-46-step2-configure-your-ide",[3938,89510,89512],{"id":89511},"configuration-of-qt","Configuration of Qt:",[439,89514,89515,89516,89521],{},"My ",[1002,89517,89520],{"href":89518,"rel":89519},"http://mobile.synyx.de/2010/04/howto-startup-with-maemo-and-qt-4-6/",[1006],"last post"," described howto setup your sdk an\nide for Maemo5 and Qt development on the N900 devide. This time i will show you how to configure your ide and setup your\nfirst project.",[439,89523,89524],{},"First of all Goto Windows->Preferences_>Qt and add a new entry in the list. The Version needs a label. I chosed the\nversion of Qt. Then you have to add a few paths in the scratchbox environment where Qt and its helper programs are\nlocated. Have a look at the image below.",[439,89526,89527],{},[1002,89528,89531],{"href":89529,"rel":89530},"https://media.synyx.de/uploads//2010/06/setupqtCreator1.png",[1006],[2205,89532],{"alt":469,"src":89533},"https://media.synyx.de/uploads//2010/06/setupqtCreator1-300x176.png",[439,89535,89536],{},"configure your QT version for Eclipse",[3938,89538,89540],{"id":89539},"check-the-installed-targets","Check the installed targets:",[439,89542,89543],{},"Goto Windows->Preferences->Maemo->Installed Targets. You should see an entry “scratchbox 1” with two subentries(\ntargets).",[439,89545,89546],{},[1002,89547,89550],{"href":89548,"rel":89549},"https://media.synyx.de/uploads//2010/06/setupScratchbox.png",[1006],[2205,89551],{"alt":89552,"src":89553},"your configuration should look like this","https://media.synyx.de/uploads//2010/06/setupScratchbox-300x206.png",[439,89555,89552],{},[439,89557,89558],{},"If it is not there hit the “Install” Button and select “Scratchbox1”. Complete the wizzard for the fremantle plattform.\nNext you may install the targets “FREMANTLE_X86″(your emulator) and “FREMANTLE_ARMEL” (for your real N900 device) by\nclicking on the “Install” button again and chosing “Scratchbox1” targets.",[3938,89560,89562],{"id":89561},"create-a-new-project","Create a new project:",[439,89564,89565],{},"In the wizard dialog chose “C++ Maemo Projekt” and click Next. A new Windows wil appear. Select the “QT Hello World”\ntemplate and click “Next”. In that dialog check all configurations if you want to build your code for the N900. Type in\nan appropriate project name and hit the “Finish” button.",[3938,89567,89569],{"id":89568},"correct-the-qt-paths","Correct the Qt paths:",[439,89571,89572,89573,89575],{},"It looks like eclipse does not use the same paths for the editor and the build process. To have autocorrection of your\nsource code and so on, you should check the includes. Goto project properties ->c/c++ Paths and Symbols->Includes.\nCheck the paths for the different configurations for the “GNU C++ language”. I had to remove the part ../",[448,89574,42401],{},"/Qt…\nfrom the path.",[3938,89577,89579],{"id":89578},"congratulation","Congratulation:",[439,89581,89582],{},"You are done. Now you may add the perspective Qt C++ for visual editing. I will write a separate post for you how to\ndo that. Also remind that you have to add each gui mask, cpp file or header file to the Qt-Project file (.pro)\nmanually. Otherwise if you build a project the file will be not included result.",{"title":469,"searchDepth":507,"depth":507,"links":89584},[89585,89586,89587,89588,89589],{"id":89511,"depth":507,"text":89512},{"id":89539,"depth":507,"text":89540},{"id":89561,"depth":507,"text":89562},{"id":89568,"depth":507,"text":89569},{"id":89578,"depth":507,"text":89579},[11122,1413],"2010-06-02T07:23:51","https://synyx.de/blog/howto-startup-with-maemo-and-qt-4-6-step2-configure-ide/",{},"/blog/howto-startup-with-maemo-and-qt-4-6-step2-configure-ide",{"title":89502,"description":469},"howto-startup-with-maemo-and-qt-4-6-step2-configure-ide","blog/howto-startup-with-maemo-and-qt-4-6-step2-configure-ide",[85548,89448],"Configuration of Qt: My last post described howto setup your sdk an ide for Maemo5 and Qt development on the N900 devide. This time i will show you how to…","RGkESZYnRuOmkTYaSyHFlxhkMzgfPHDSTYgGq57KbBg",{"id":89602,"title":89603,"author":89604,"body":89605,"category":89841,"date":89842,"description":89843,"extension":1034,"link":89844,"meta":89845,"navigation":916,"path":89846,"seo":89847,"slug":89609,"stem":89849,"tags":89850,"teaser":89852,"__hash__":89853},"blog/blog/performance-optimization-in-synyx-sudoku.md","Performance optimization in Synyx Sudoku",[190],{"type":432,"value":89606,"toc":89839},[89607,89610,89619,89622,89625,89628,89631,89640,89643,89646,89649,89692,89695,89806,89809,89824,89831,89834,89837],[435,89608,89603],{"id":89609},"performance-optimization-in-synyx-sudoku",[439,89611,89612,89613,89618],{},"Now that ",[1002,89614,89617],{"href":89615,"rel":89616},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku",[1006],"SynyxSudoku"," has been on the market a little while,\nI want to tell a little about what kind of measurements we took to optimize the performance of it.",[439,89620,89621],{},"First of all I have to say, that this was our first android project and I’m also just a first year trainee at the\nmoment, so there were quite a few issues, not only about the performance that had to be solved. I learned a lot in this\nproject and want to share a bit of what I learned with you, so I hope this will help you to improve the performance of\nyour apps, as well.",[439,89623,89624],{},"The biggest problem was probably the large number of views that the activity had at start (somewhat about 300…), so the\napp needed really long to start and didn’t run that fast because of it.",[439,89626,89627],{},"To track this kind of problem, google integrated the hierarchy viewer to the toolkit of android, which gives you an\noverview of all the currently loaded views in the selected app.",[439,89629,89630],{},"So it was soon clear, that the highscore was the big problem, because it was built as a table with as much rows as there\nwas data – what actually causes two different performance issues, first the big number of views and second the\ninefficient way of creating this views.",[439,89632,89633,89634,89639],{},"After a little bit of research we found the solution to this in the google I/O\nvideo ",[1002,89635,89638],{"href":89636,"rel":89637},"http://www.youtube.com/watch?v=N6YdwzAvwOA",[1006],"“Make your Android UI Fast and Efficient”",", which you really should\nwatch, if you haven’t by now!",[439,89641,89642],{},"So we found out that there’s a ListView element that takes care of such big lists as the highscore quite easily. The\ntrick to it is, that it only has that much views loaded, as there are visible to the user and it recycles them if the\nscroll out of the screen -> the views that scroll out of the screen are taken out of the list, have their data\nreplaced, and are put in on the opposite side again.",[439,89644,89645],{},"And that’s how it’s looking in the code:",[439,89647,89648],{},"First you have to overwrite the BaseAdapter from android so that you can give it your specific views to display:",[464,89650,89652],{"className":709,"code":89651,"language":711,"meta":469,"style":469},"public class HighscoreListAdapter extends BaseAdapter {\n private List valueList;\n private LayoutInflater inflater;\n public HighscoreListAdapter(List highscoreValueList, LayoutInflater inflater) {\n this.inflater = inflater;\n this.valueList = highscoreValueList;\n }\n}\n",[471,89653,89654,89659,89664,89669,89674,89679,89684,89688],{"__ignoreMap":469},[474,89655,89656],{"class":476,"line":477},[474,89657,89658],{},"public class HighscoreListAdapter extends BaseAdapter {\n",[474,89660,89661],{"class":476,"line":507},[474,89662,89663],{}," private List valueList;\n",[474,89665,89666],{"class":476,"line":547},[474,89667,89668],{}," private LayoutInflater inflater;\n",[474,89670,89671],{"class":476,"line":584},[474,89672,89673],{}," public HighscoreListAdapter(List highscoreValueList, LayoutInflater inflater) {\n",[474,89675,89676],{"class":476,"line":607},[474,89677,89678],{}," this.inflater = inflater;\n",[474,89680,89681],{"class":476,"line":642},[474,89682,89683],{}," this.valueList = highscoreValueList;\n",[474,89685,89686],{"class":476,"line":663},[474,89687,15319],{},[474,89689,89690],{"class":476,"line":694},[474,89691,703],{},[439,89693,89694],{},"and also overwrite the getView method from it, so that you can make your recycling in there:",[464,89696,89698],{"className":709,"code":89697,"language":711,"meta":469,"style":469},"public View getView(int position, View convertView, ViewGroup parent) {\n HighscoreViewHolder highscoreViewHolder;\n if (convertView == null) {\n // the first few elements of the list are created out of the xml\n convertView = inflater.inflate(R.layout.highscore_list_entry, null);\n highscoreViewHolder = new HighscoreViewHolder();\n highscoreViewHolder.name = (TextView) convertView.findViewById(R.id.highscore_list_entry_name);\n highscoreViewHolder.score = (TextView) convertView.findViewById(R.id.highscore_list_entry_score);\n highscoreViewHolder.rank = (TextView) convertView.findViewById(R.id.highscore_list_entry_rank);\n convertView.setTag(highscoreViewHolder);\n convertView.setFocusable(false);\n } else {\n // recycle of the view that went out of the view\n highscoreViewHolder = (HighscoreViewHolder) convertView.getTag();\n }\n HighscoreValue value = this.valueList.get(position);\n // set the new values\n highscoreViewHolder.name.setText(value.getName());\n highscoreViewHolder.score.setText(Integer.toString(value.getScore()));\n highscoreViewHolder.rank.setText(Integer.toString(value.getRank()));\n return convertView;\n}\n",[471,89699,89700,89704,89709,89714,89719,89724,89729,89734,89739,89744,89749,89754,89758,89763,89768,89772,89777,89782,89787,89792,89797,89802],{"__ignoreMap":469},[474,89701,89702],{"class":476,"line":477},[474,89703,72160],{},[474,89705,89706],{"class":476,"line":507},[474,89707,89708],{}," HighscoreViewHolder highscoreViewHolder;\n",[474,89710,89711],{"class":476,"line":547},[474,89712,89713],{}," if (convertView == null) {\n",[474,89715,89716],{"class":476,"line":584},[474,89717,89718],{}," // the first few elements of the list are created out of the xml\n",[474,89720,89721],{"class":476,"line":607},[474,89722,89723],{}," convertView = inflater.inflate(R.layout.highscore_list_entry, null);\n",[474,89725,89726],{"class":476,"line":642},[474,89727,89728],{}," highscoreViewHolder = new HighscoreViewHolder();\n",[474,89730,89731],{"class":476,"line":663},[474,89732,89733],{}," highscoreViewHolder.name = (TextView) convertView.findViewById(R.id.highscore_list_entry_name);\n",[474,89735,89736],{"class":476,"line":694},[474,89737,89738],{}," highscoreViewHolder.score = (TextView) convertView.findViewById(R.id.highscore_list_entry_score);\n",[474,89740,89741],{"class":476,"line":700},[474,89742,89743],{}," highscoreViewHolder.rank = (TextView) convertView.findViewById(R.id.highscore_list_entry_rank);\n",[474,89745,89746],{"class":476,"line":913},[474,89747,89748],{}," convertView.setTag(highscoreViewHolder);\n",[474,89750,89751],{"class":476,"line":920},[474,89752,89753],{}," convertView.setFocusable(false);\n",[474,89755,89756],{"class":476,"line":926},[474,89757,61330],{},[474,89759,89760],{"class":476,"line":932},[474,89761,89762],{}," // recycle of the view that went out of the view\n",[474,89764,89765],{"class":476,"line":938},[474,89766,89767],{}," highscoreViewHolder = (HighscoreViewHolder) convertView.getTag();\n",[474,89769,89770],{"class":476,"line":944},[474,89771,15319],{},[474,89773,89774],{"class":476,"line":950},[474,89775,89776],{}," HighscoreValue value = this.valueList.get(position);\n",[474,89778,89779],{"class":476,"line":956},[474,89780,89781],{}," // set the new values\n",[474,89783,89784],{"class":476,"line":962},[474,89785,89786],{}," highscoreViewHolder.name.setText(value.getName());\n",[474,89788,89789],{"class":476,"line":4876},[474,89790,89791],{}," highscoreViewHolder.score.setText(Integer.toString(value.getScore()));\n",[474,89793,89794],{"class":476,"line":4888},[474,89795,89796],{}," highscoreViewHolder.rank.setText(Integer.toString(value.getRank()));\n",[474,89798,89799],{"class":476,"line":4900},[474,89800,89801],{}," return convertView;\n",[474,89803,89804],{"class":476,"line":4913},[474,89805,703],{},[439,89807,89808],{},"now whats only left is to create the adapter, and assign it to the ListView:",[464,89810,89812],{"className":709,"code":89811,"language":711,"meta":469,"style":469},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\nlistView.setAdapter(highscoreListAdapter);\n",[471,89813,89814,89819],{"__ignoreMap":469},[474,89815,89816],{"class":476,"line":477},[474,89817,89818],{},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\n",[474,89820,89821],{"class":476,"line":507},[474,89822,89823],{},"listView.setAdapter(highscoreListAdapter);\n",[439,89825,89826,89827,89830],{},"If you make some changes in the data set you gave to the adapter, just call ",[990,89828,89829],{},"notifyDataSetChanged()"," on it to let it\nrefresh itself.",[439,89832,89833],{},"So after this change the app started remarkably faster and also ran faster then before, the views were cut to a merely\n120 in numbers.",[439,89835,89836],{},"Well, that’s it for the moment. If you want further advices regarding the performance of android apps, please watch the\nvideo I mentioned above. It helped me a lot and I’m sure it will also show you a few tricks how to make your app faster.",[1024,89838,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":89840},[],[11122,1413],"2010-05-28T08:06:05","Now that SynyxSudoku has been on the market a little while,\\nI want to tell a little about what kind of measurements we took to optimize the performance of it.","https://synyx.de/blog/performance-optimization-in-synyx-sudoku/",{},"/blog/performance-optimization-in-synyx-sudoku",{"title":89603,"description":89848},"Now that SynyxSudoku has been on the market a little while,\nI want to tell a little about what kind of measurements we took to optimize the performance of it.","blog/performance-optimization-in-synyx-sudoku",[11132,89851],"performance","Now that SynyxSudoku has been on the market a little while, I want to tell a little about what kind of measurements we took to optimize the performance of it.…","5S0ZY4kFZSda0-ABG8x4vCBcnZfT2QfTdDlf0aTxKYk",{"id":89855,"title":89856,"author":89857,"body":89858,"category":89971,"date":89972,"description":89973,"extension":1034,"link":89974,"meta":89975,"navigation":916,"path":89976,"seo":89977,"slug":89862,"stem":89978,"tags":89979,"teaser":89981,"__hash__":89982},"blog/blog/in-my-humble-opinion-froyo-rocks.md","In my humble opinion — FroYo rocks!",[15],{"type":432,"value":89859,"toc":89969},[89860,89863,89866,89869,89874,89877,89883,89885,89891,89896,89898,89904,89906,89932,89937,89948,89951,89956,89959,89962],[435,89861,89856],{"id":89862},"in-my-humble-opinion-froyo-rocks",[439,89864,89865],{},"FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a\nmixture between API Changes, new Userfeatures and some new cool Apps.",[439,89867,89868],{},"So, let’s divide this review into theese several specific parts and some addons at the end:",[439,89870,89871],{},[448,89872,89873],{},"First, the imho, absolutely most important part, the Enterprise Features:",[439,89875,89876],{},"New API-Features:",[994,89878,89879,89881],{},[997,89880,87848],{},[997,89882,87851],{},[439,89884,87854],{},[994,89886,89887,89889],{},[997,89888,87859],{},[997,89890,87862],{},[439,89892,89893],{},[448,89894,89895],{},"Furthermore there are, of course, some more, not so enterprisy features 😉 :",[439,89897,87865],{},[994,89899,89900,89902],{},[997,89901,87870],{},[997,89903,87873],{},[439,89905,87854],{},[994,89907,89908,89911,89914,89917,89920,89923,89926,89929],{},[997,89909,89910],{},"Flashsupport , Airsupport (useful?)",[997,89912,89913],{},"Possibility to save Apps on SD-Card",[997,89915,89916],{},"JIT Compiler (hey this improves performance 3-5 times!!!)",[997,89918,89919],{},"HTML5 compatible browser?",[997,89921,89922],{},"With FroYo, users will be able to sync their local music collection with their Android device and stream wirelessly.",[997,89924,89925],{},"Users will also be available to backup their Apps in the Cloud",[997,89927,89928],{},"Android 2.2 finally supports multi-language keyboards too!",[997,89930,89931],{},"a lot of cool Marketplace updates (Webmarket like Androidpit.de and Search and Categories! and One click to update\nall, allow automatic update!!!! )",[439,89933,89934],{},[448,89935,89936],{},"And now some more rumors on what helps FroYo being the Apple poison 😉 :",[994,89938,89939,89942,89945],{},[997,89940,89941],{},"Kernel 2.6.32 will also improve speed for snapdragon processor smartphones like the Desire or the Milestone",[997,89943,89944],{},"Developers will be able to implement Services which interact with kind of Push Notifications. The killer here is that\nthis works bidirectional so that apps can also notifiy of feed Webapps in the Cloud with data! Interesting could be if\nGoogle is here open minded enough not to chain this, imho killerfeature, to Chrome.",[997,89946,89947],{},"Browserapps are able to use camera or sensors directly via JS! Is this the end of Tools like PhoneGap?",[439,89949,89950],{},"For those who like it … the new Google Buzz app should be much better than the crappy web version.",[994,89952,89953],{},[997,89954,89955],{},"Rumors told also that the new market is able to keep parts of froyo OS up to date automatically… could help to reduce\ndevice fragmentation!!!",[439,89957,89958],{},"As you can see, the list of new improvements is very long and in my opinion this is only the start of a little\nrevolution. All theese new features will enable Android Smartphone for a wide variety of really cool apps which we\nactually never thaught about because it was just not possible to implement ’em… we will dig more into Froyo if the first\nupdated Smartphone is available for us at Synyx… so stay tuned…",[439,89960,89961],{},"As addon, today the first Nexus One users told about their experience after the update… read more",[439,89963,89964],{},[1002,89965,89968],{"href":89966,"rel":89967},"http://forum.xda%5C-developers.com/showthread.php?t%5C=686591",[1006],"http://forum.xda\\-developers.com/showthread.php?t\\=686591",{"title":469,"searchDepth":507,"depth":507,"links":89970},[],[11122],"2010-05-26T11:39:24","FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a\\nmixture between API Changes, new Userfeatures and some new cool Apps.","https://synyx.de/blog/in-my-humble-opinion-froyo-rocks/",{},"/blog/in-my-humble-opinion-froyo-rocks",{"title":89856,"description":89865},"blog/in-my-humble-opinion-froyo-rocks",[11132,89980,711],"froyo","FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a mixture between API Changes, new Userfeatures and some new cool…","vN70uct3slEe-Cpa7GTMmxV2VPJ7Hq7ssPQVU1B6XMc",{"id":89984,"title":89985,"author":89986,"body":89987,"category":90081,"date":90082,"description":89994,"extension":1034,"link":90083,"meta":90084,"navigation":916,"path":90085,"seo":90086,"slug":90087,"stem":90088,"tags":90089,"teaser":90090,"__hash__":90091},"blog/blog/5-reasons-for-teams.md","Five reasons why you should not work alone on IT-Projects",[166],{"type":432,"value":89988,"toc":90074},[89989,89992,89995,89998,90001,90005,90014,90017,90021,90030,90034,90037,90040,90043,90046,90049,90053,90056,90065],[435,89990,89985],{"id":89991},"five-reasons-why-you-should-not-work-alone-on-it-projects",[439,89993,89994],{},"In my opinion its much better to have a team working on a project than a single person.",[439,89996,89997],{},"Even if this means that your customer might have to wait a bit longer for his project to start (because other projects\nalso occupy more people) everybody benefits because of increased productivity, better code and happy team members.",[439,89999,90000],{},"Here are my top five reasons why you should not leave one guy alone with a IT project…",[1065,90002,90004],{"id":90003},"avoid-single-points-of-failure","Avoid Single Points of Failure",[439,90006,90007,90008,90013],{},"People get sick, are on vacation or might even resign from their job. You have to be able to compensate this by having\nother members that don’t need weeks or months to understand the projects requirements or codebase.\nAvoiding ",[1002,90009,90012],{"href":90010,"rel":90011},"http://en.wikipedia.org/wiki/Single_Point_of_Failure",[1006],"single points of failures"," saves you from having to get\nnew (other) people up-to-date which will cost you time, money and probably even upsets your customer.",[439,90015,90016],{},"Additionally your customer might ask for enhancements, bugfixes or even new (related) applications any time after the\noriginal project is finished. People that were involved on that project might be working on all kind of other projects\nthen. If you have more than one guy that knows the domain and the code then you gain alot of flexibility in resource\nmanagement.",[1065,90018,90020],{"id":90019},"think-twice-triply","Think Twice / Triply / …",[439,90022,90023,90024,90029],{},"Another big benefit is, that team members have somebody to discuss any tasks with. These discussions might be about how\nto design a special feature or how the customers domain is modeled best. The members can save each other from producing\nbugs by reviewing each others code. They can also\nuse ",[1002,90025,90028],{"href":90026,"rel":90027},"http://www.extremeprogramming.org/rules/pair.html",[1006],"Pairprogramming"," for tricky parts of the application.",[1065,90031,90033],{"id":90032},"individual-skills","Individual Skills",[439,90035,90036],{},"Each member of your team also brings his special and individual skills and expirience. One guy might be better when it\ncomes down to software architecture, another one might be the best choice to communicate with the customer and a third\nmight be an expert at designing user interfaces.",[439,90038,90039],{},"Since IT-Projects require alot of different skills each of them will benefit from an increased bandwidth of skills.",[1065,90041,90042],{"id":8470},"Motivation",[439,90044,90045],{},"IT-Projects can be frustrating sometimes. One person that works alone gets demotivated easily because he feels left\nalone with whatever is frustrating him.",[439,90047,90048],{},"Being able to talk about problems and motivating each other helps to stay in a good temper and thus be more productive.\nHaving a good team and fun at work helps to endure frustrating parts of a project.",[1065,90050,90052],{"id":90051},"distraction","Distraction",[439,90054,90055],{},"If someone has to accomplish everything by himself he might also get easy distracted. He might start browsing the web or\nhe pays more attention his colleagues projects than to his own. But he will probably stay focused if you have a team\nthat works with him, because he has someone to justify himself to.",[439,90057,90058,90059,90064],{},"A small daily standup-meeting (e.g. a ",[1002,90060,90063],{"href":90061,"rel":90062},"http://www.scrumbasics.com/conducting-daily-scrum-meeting/",[1006],"daily SCRUM",") where\neveryone explains what he has done the last day can help the team to stay focused.",[439,90066,90067,90068,90073],{},"Imagine how guilty you’d feel if everyone worked hard and you only watched videos on ",[1002,90069,90072],{"href":90070,"rel":90071},"http://www.youtube.com/",[1006],"youtube","\ninstead of writing a unit test for the feature you implemented the day before.",{"title":469,"searchDepth":507,"depth":507,"links":90075},[90076,90077,90078,90079,90080],{"id":90003,"depth":547,"text":90004},{"id":90019,"depth":547,"text":90020},{"id":90032,"depth":547,"text":90033},{"id":8470,"depth":547,"text":90042},{"id":90051,"depth":547,"text":90052},[1030],"2010-05-25T11:03:19","https://synyx.de/blog/5-reasons-for-teams/",{},"/blog/5-reasons-for-teams",{"title":89985,"description":89994},"5-reasons-for-teams","blog/5-reasons-for-teams",[77893,81347,47537],"In my opinion its much better to have a team working on a project than a single person. Even if this means that your customer might have to wait a…","IVwgKz9GQYkBCYmZch_5lMbuzLkIv6gZqFpTi0fFih8",{"id":90093,"title":90094,"author":90095,"body":90096,"category":90125,"date":90126,"description":90127,"extension":1034,"link":90128,"meta":90129,"navigation":916,"path":90130,"seo":90131,"slug":90133,"stem":90134,"tags":90135,"teaser":90136,"__hash__":90137},"blog/blog/jug-ka-20-grunde-warum-man-keine-architekten-braucht-eine-personliche-meinung.md","jug-ka: '20 Gründe warum man keine Architekten braucht', eine persönliche Meinung",[51],{"type":432,"value":90097,"toc":90123},[90098,90102,90111,90114,90117,90120],[435,90099,90101],{"id":90100},"jug-ka-20-gründe-warum-man-keine-architekten-braucht-eine-persönliche-meinung","jug-ka: \"20 Gründe warum man keine Architekten braucht\", eine persönliche Meinung",[439,90103,90104,90105,90110],{},"Am 12.05.2010 fand die Java User Group Karlsruhe\nVeranstaltung “",[1002,90106,90109],{"href":90107,"rel":90108},"http://jug-karlsruhe.de/content/20-gruende-warum-man-keine-architekten-braucht/",[1006],"20 Gründe warum man keine Architekten braucht","”\nstatt.",[439,90112,90113],{},"Da ich die letzten Treffen versäumt habe wollte ich, auch als Getränkelieferant, mal wieder dabei sein.Die\nTeilnehmerliste versprach, einige Leute zu treffen, die ich schon lange nicht mehr gesehen hatte. Der angekündigte\nSprecher sagte mir nichts und das Thema schien mir provokant aber nicht wichtig.",[439,90115,90116],{},"Der Vortrag schilderte Situationen in großen Unternehmen, untermalte diese mit witzigen Bildern, die allerdings auch\nprovokant und teilweise am Rande des guten Geschmacks waren.",[439,90118,90119],{},"Zusammenfassend schien es um die Problematik zu gehen, dass der “Architekt” zu weit von der jeweiligen Projekt- und\nEntwicklertätigkeit entfernt ist und um die sich daraus ergebenden Problematiken. Da ich in einem Unternehmen ohne\ndedizierte Architektenrolle zu hause bin, war das für mich interessant aber nicht akut. Schön fand ich, dass es dem\nSprecher trotz seiner provokanten Art gelungen ist auf ebenfalls provokante Fragen ruhig und sachliche zu reagieren,\nobwohl einige Fragen nicht unbedingt als sachlich anzusehen sind.",[439,90121,90122],{},"Sich im Anschluss bei einem Bier bzw. Cola noch auszutauschen hat das Ganze abgerundet.",{"title":469,"searchDepth":507,"depth":507,"links":90124},[],[1031],"2010-05-22T11:39:04","Am 12.05.2010 fand die Java User Group Karlsruhe\\nVeranstaltung “20 Gründe warum man keine Architekten braucht”\\nstatt.","https://synyx.de/blog/jug-ka-20-grunde-warum-man-keine-architekten-braucht-eine-personliche-meinung/",{},"/blog/jug-ka-20-grunde-warum-man-keine-architekten-braucht-eine-personliche-meinung",{"title":90094,"description":90132},"Am 12.05.2010 fand die Java User Group Karlsruhe\nVeranstaltung “20 Gründe warum man keine Architekten braucht”\nstatt.","jug-ka-20-grunde-warum-man-keine-architekten-braucht-eine-personliche-meinung","blog/jug-ka-20-grunde-warum-man-keine-architekten-braucht-eine-personliche-meinung",[],"Am 12.05.2010 fand die Java User Group Karlsruhe Veranstaltung “20 Gründe warum man keine Architekten braucht” statt. Da ich die letzten Treffen versäumt habe wollte ich, auch als Getränkelieferant, mal…","qCYhhIkzcPhqv8u1NAJvi5XEkBKoXYsmLxAaf2n-KnE",{"id":90139,"title":82565,"author":90140,"body":90141,"category":90239,"date":90240,"description":90241,"extension":1034,"link":90242,"meta":90243,"navigation":916,"path":90244,"seo":90245,"slug":90247,"stem":90248,"tags":90249,"teaser":90250,"__hash__":90251},"blog/blog/mobile-solutions-summary-2.md",[223],{"type":432,"value":90142,"toc":90237},[90143,90145,90158,90177,90187,90193,90202,90226],[435,90144,82565],{"id":82571},[439,90146,90147,90148,90153,90154,90157],{},"It has been quite a while since ",[1002,90149,90152],{"href":90150,"rel":90151},"http://blog.synyx.de/2010/04/30/mobile-solutions-summary/",[1006],"my last update"," on what’s\nhappening over at our ",[1002,90155,83379],{"href":77932,"rel":90156},[1006],", so let’s get right into it.",[439,90159,90160,90161,74182,90166,90170,90171,90176],{},"One of the most interesting posts\nwas ",[1002,90162,90165],{"href":90163,"rel":90164},"http://mobile.synyx.de/2010/05/10/user-statistics-from-synyxsudoku/",[1006],"“User statistics from SynyxSudoku”",[1002,90167,90169],{"href":85791,"rel":90168},[1006],"Tobias",". It shows the distribution of Android devices, which are running our\nvery own Sudoku App, which by the\nway ",[1002,90172,90175],{"href":90173,"rel":90174},"http://mobile.synyx.de/2010/05/10/synyxsudoku-update-to-version-1-02/",[1006],"got updated"," a couple of weeks ago:",[11947,90178,90179,90182,90184],{},[439,90180,90181],{},"*First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that uploaded their highscores have\nalso agreed to send us their device specific data, because I really didn’t expect more than 10-20%.",[439,90183,27402],{},[439,90185,90186],{},"The devices came mostly with the latest Android versions available. … The smaller devices (240×320) aren’t that\npopular as it seems (maybe because there’s only the HTC Tattoo that uses this resolution), but the others are quite\nevenly matched.*",[439,90188,90189,90190,1402],{},"If you are into Android and need some hard facts, go and checkout the numbers on\nour ",[1002,90191,83379],{"href":90163,"rel":90192},[1006],[439,90194,90195,90196,90201],{},"We were looking for a consistent example to show how to solve a problem over the 3 major platforms iPhone, Android and\nMaemo and ",[1002,90197,90200],{"href":90198,"rel":90199},"http://maps.google.com",[1006],"Google Maps"," seemed to be a nice show case.",[439,90203,90204,90205,90210,90211,90216,90217,90220,90221,1402],{},"We decided to start a little ",[1002,90206,90209],{"href":90207,"rel":90208},"http://mobile.synyx.de/tag/google-maps/",[1006],"tutorial series",", which results in three\nhands-on examples of how to integrate Google Maps in your application. There\nare ",[1002,90212,90215],{"href":90213,"rel":90214},"http://mobile.synyx.de/2010/05/07/google-maps-on-android-part-2-overlays/",[1006],"code samples for Android"," and you can\nalready download a ",[990,90218,90219],{},"UIViewController",", which\nshows ",[1002,90222,90225],{"href":90223,"rel":90224},"http://mobile.synyx.de/2010/05/19/how-to-add-a-%E2%80%9Cfind-your-company%E2%80%9D-feature-to-your-iphone-app-%E2%80%93-part-ii/",[1006],"“How to add a ‘Find Your Company’ feature to your iPhone App”",[439,90227,90228,90229,90232,90233,1402],{},"There’s a lot happening in the mobile world and on our ",[1002,90230,83379],{"href":77932,"rel":90231},[1006],", so you better\nhead over and subscribe to ",[1002,90234,90236],{"href":85784,"rel":90235},[1006],"our feed",{"title":469,"searchDepth":507,"depth":507,"links":90238},[],[1030],"2010-05-21T13:52:57","It has been quite a while since my last update on what’s\\nhappening over at our Mobile Solutions Blog, so let’s get right into it.","https://synyx.de/blog/mobile-solutions-summary-2/",{},"/blog/mobile-solutions-summary-2",{"title":82565,"description":90246},"It has been quite a while since my last update on what’s\nhappening over at our Mobile Solutions Blog, so let’s get right into it.","mobile-solutions-summary-2","blog/mobile-solutions-summary-2",[18496],"It has been quite a while since my last update on what’s happening over at our Mobile Solutions Blog, so let’s get right into it. One of the most interesting…","1p6YXeszwV5TrMg-KutpsDhtSaWLSU9xOSsSsDGZ9Qo",{"id":90253,"title":90254,"author":90255,"body":90256,"category":90369,"date":90370,"description":90371,"extension":1034,"link":90372,"meta":90373,"navigation":916,"path":90374,"seo":90375,"slug":90376,"stem":90377,"tags":90378,"teaser":90380,"__hash__":90381},"blog/blog/scrum-an-anti-word.md","Why is Scrum getting an anti-word?",[27],{"type":432,"value":90257,"toc":90364},[90258,90261,90264,90267,90274,90279,90289,90292,90318,90321,90325,90328,90331,90334,90337,90340,90343,90346,90349,90352,90355,90358,90361],[435,90259,90254],{"id":90260},"why-is-scrum-getting-an-anti-word",[439,90262,90263],{},"For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got\nmainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream as words, in business talk, in dev talk, in\ntrainings.",[439,90265,90266],{},"But what did it really achive for better communication, better relations and collaboration between developers, managers,\ncustomers etc. Has Scrum fundamentally improved the way software is delivered in our industry?",[439,90268,90269,90270,90273],{},"I probably couldn’t find many people who’d respond with an unconditional “",[448,90271,90272],{},"YES!","” to this question.",[439,90275,90276],{},[448,90277,90278],{},"But why? Why is Scrum getting an anti-word for many?",[439,90280,90281],{},[1002,90282,90285],{"href":90283,"rel":90284},"http://defunctscrum.blogspot.com/2007/07/should-you-leave-scrum-off-resume.html",[1006],[2205,90286],{"alt":90287,"src":90288,"title":90287},"Security!","https://media.synyx.de/uploads//2010/05/Scrumtoon31.jpg",[439,90290,90291],{},"There are various different reasons:",[994,90293,90294,90297,90300,90303,90306,90309,90312,90315],{},[997,90295,90296],{},"Scrum’s transparency creats angst for people living in and from intransparency",[997,90298,90299],{},"Scrum’s need for change is uncomfortable for people’s need for stability",[997,90301,90302],{},"Wrong implementation of Scrum",[997,90304,90305],{},"Scrum Master who don’t take people with them and run ahead in their own speed",[997,90307,90308],{},"Unbalanced power division between roles",[997,90310,90311],{},"Scrum Master who have THE solution instead of enabling teams to find differing ways for different problems",[997,90313,90314],{},"The Scrum hype and overwhelming/missleading marketing",[997,90316,90317],{},"Could go on with lots more",[439,90319,90320],{},"Let’s focus on a few and look at them a little bit more in-depth.",[1065,90322,90324],{"id":90323},"speedy-scrum-master","Speedy Scrum Master",[439,90326,90327],{},"A person who just got his Scrum Master Certificate and gets all enthusiastic about it returns to his company from\ntraining and wants to start. His textbook knowledge tells him how to technically implement Scrum, but without years of\nexperience it’s applied in a step-by-step way. Without soft-skills and knowing what’s appropriate when it’s allmost\nimpossible to be successful right away without watering down Scrum to non-Scrum. Picking people up where they are is\none of the key things. You can’t just tell them where they ought to be without telling why and what for.",[439,90329,90330],{},"Once this poor Scrum Master introduced Scrum to team for a few sprints he and his team will hit walls without knowing\nhow to climb over them. At this point the lack of experience of the Scrum Master leads to the first internal critics to\nsurface. The longer a Scrum Master only has his Scrum process goal in mind without providing real solutions the more\nScrum will be the scapegoat.",[439,90332,90333],{},"The more this happens (and I guess it does a lot), the more people will say “Oh no, not another of these Scrum guys”.",[1065,90335,90308],{"id":90336},"unbalanced-power-division-between-roles",[439,90338,90339],{},"Unbalanced power division between roles causes wrong implementation and frustration.",[439,90341,90342],{},"For example Scrum Masters who don’t have the support of upper management to make things happen.",[439,90344,90345],{},"Teams without the power to stop a sprint, without the needed skills and not cross-functional are handicapped teams.",[439,90347,90348],{},"Product Owner who are not directly responsible for the profit and loss or ROI of the product. I often wonder how a\nProduct Owner is supposed to prioritize without? I still often hear something along the lines “we need everything!”.",[439,90350,90351],{},"One of the things that’s done wrong in projects that use Scrum is that Scrum should help a project to fail early instead\nof staying for a long time and dying a long slow death over years. Why’s that? Because often all people involved have\nconflicting interests regarding their job safety and early project death. The only one that could have an interest in\nthat is a Product Owner with budget responsibility who knows that a project shouldn’t be continued if the value of to be\nimplemented stories is lower than its costs.",[1065,90353,90314],{"id":90354},"the-scrum-hype-and-overwhelmingmissleading-marketing",[439,90356,90357],{},"Scrum is often advertised as solution for everything. Scrum has been hyped for years. The result is lots of so called\n‘experts’ promote and implement Scrum without ensuring it’s done right or often even without the experience on how to do\nit right. Again Scrum gets the blame for failing projects that might or might not have failed anyway.",[439,90359,90360],{},"So is Scrum at fault? Or is it the way Scrum is used today? Can Scrum be rescued or do we need something new just\nbecause Scrum is done wrong instead of because it is wrong?",[439,90362,90363],{},"As with so many things I think we should focus more on quality instead of quantity. Don’t look for cookbook receipts,\nlet your team tailor their process inside the Scrum skeleton and share (don’t force it) your experience with them. I\nmight follow up on this in a future article.",{"title":469,"searchDepth":507,"depth":507,"links":90365},[90366,90367,90368],{"id":90323,"depth":547,"text":90324},{"id":90336,"depth":547,"text":90308},{"id":90354,"depth":547,"text":90314},[1030],"2010-05-20T12:57:24","For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got\\nmainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream as words, in business talk, in dev talk, in\\ntrainings.","https://synyx.de/blog/scrum-an-anti-word/",{},"/blog/scrum-an-anti-word",{"title":90254,"description":90263},"scrum-an-anti-word","blog/scrum-an-anti-word",[9546,90379,37776,9557,14723],"anti-scrum","For quite some years Scrum has been THE agile development process. Scrum got mainstream. But let’s have a look what got mainstream here. Scrum, Agility, Buzzwords, Scrum Master got mainstream…","MRY5FlmmSh_1KHgqJrrMY74IdZlDb2A_FfKcbNtkQew",{"id":90383,"title":90384,"author":90385,"body":90386,"category":90679,"date":90680,"description":90681,"extension":1034,"link":90682,"meta":90683,"navigation":916,"path":90684,"seo":90685,"slug":90390,"stem":90687,"tags":90688,"teaser":90689,"__hash__":90690},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii.md","How to add a “Find Your Company” feature to your iPhone App – Part II",[223],{"type":432,"value":90387,"toc":90677},[90388,90391,90403,90406,90417,90420,90435,90601,90604,90609,90631,90638,90652,90659,90668,90675],[435,90389,90384],{"id":90390},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[439,90392,63115,90393,90398,90399,1402],{},[1002,90394,90397],{"href":90395,"rel":90396},"http://mobile.synyx.de/2010/05/06/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",[1006],"first installment",",\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on ",[1002,90400,22987],{"href":90401,"rel":90402},"http://gist.github.com/388323/ea35c6d31270babb73ec052f1442c43afd6b5510",[1006],[439,90404,90405],{},"If you start the App where we left off, you get the two pins, but it won’t be properly zoomed. In fact, it would look\nsomething like this:",[439,90407,90408],{},[1002,90409,90412],{"href":90410,"rel":90411},"https://media.synyx.de/uploads//2010/05/maps-3.png",[1006],[2205,90413],{"alt":90414,"src":90415,"title":90416},"Synyx Offices","https://media.synyx.de/uploads//2010/05/maps-3-208x300.png","screen_synyx_map",[439,90418,90419],{},"As you can see, the pins are not really helpful at this zoom level and it’s definitely not user friendly, if you have to\nzoom in by yourself. What we need is a way to zoom in, so that the pin and the current location nicely fit on the\nscreen.",[439,90421,90422,90423,90426,90427,90431,90432,90434],{},"That’s exactly what the method ",[990,90424,90425],{},"centerMapAroundAnnotations"," at\nline ",[1002,90428,90430],{"href":90401,"rel":90429},[1006],"108"," of our ",[990,90433,90219],{}," does:",[464,90436,90438],{"className":83142,"code":90437,"language":83144,"meta":469,"style":469},"\nif ( [[self.mapView annotations] count] \u003C 2 )\n return;\nCLLocationCoordinate2D min;\nCLLocationCoordinate2D max;\nBOOL minMaxInitialized = NO;\nfor ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n if ( !minMaxInitialized ) {\n min = a.coordinate;\n max = a.coordinate;\n minMaxInitialized = YES;\n } else {\n min.latitude = MIN( min.latitude, a.coordinate.latitude );\n min.longitude = MIN( min.longitude, a.coordinate.longitude );\n max.latitude = MAX( max.latitude, a.coordinate.latitude );\n max.longitude = MAX( max.longitude, a.coordinate.longitude );\n }\n}\u003Cbr/>\nCLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\nCLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\nCLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\nCLLocationCoordinate2D regionCenter;\nregionCenter.latitude = (min.latitude + max.latitude) / 2.0;\nregionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\nCLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\nCLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\nMKCoordinateRegion region;\nregion = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\nMKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n[self.mapView setRegion: fitRegion animated: YES];\n[locSouthWest release];\n[locSouthEast release];\n[locNorthEast release];\n\n",[471,90439,90440,90444,90449,90453,90458,90463,90468,90473,90478,90483,90488,90493,90497,90502,90507,90512,90517,90521,90526,90531,90536,90541,90546,90551,90556,90561,90566,90571,90576,90581,90586,90591,90596],{"__ignoreMap":469},[474,90441,90442],{"class":476,"line":477},[474,90443,917],{"emptyLinePlaceholder":916},[474,90445,90446],{"class":476,"line":507},[474,90447,90448],{},"if ( [[self.mapView annotations] count] \u003C 2 )\n",[474,90450,90451],{"class":476,"line":547},[474,90452,86342],{},[474,90454,90455],{"class":476,"line":584},[474,90456,90457],{},"CLLocationCoordinate2D min;\n",[474,90459,90460],{"class":476,"line":607},[474,90461,90462],{},"CLLocationCoordinate2D max;\n",[474,90464,90465],{"class":476,"line":642},[474,90466,90467],{},"BOOL minMaxInitialized = NO;\n",[474,90469,90470],{"class":476,"line":663},[474,90471,90472],{},"for ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n",[474,90474,90475],{"class":476,"line":694},[474,90476,90477],{}," if ( !minMaxInitialized ) {\n",[474,90479,90480],{"class":476,"line":700},[474,90481,90482],{}," min = a.coordinate;\n",[474,90484,90485],{"class":476,"line":913},[474,90486,90487],{}," max = a.coordinate;\n",[474,90489,90490],{"class":476,"line":920},[474,90491,90492],{}," minMaxInitialized = YES;\n",[474,90494,90495],{"class":476,"line":926},[474,90496,61330],{},[474,90498,90499],{"class":476,"line":932},[474,90500,90501],{}," min.latitude = MIN( min.latitude, a.coordinate.latitude );\n",[474,90503,90504],{"class":476,"line":938},[474,90505,90506],{}," min.longitude = MIN( min.longitude, a.coordinate.longitude );\n",[474,90508,90509],{"class":476,"line":944},[474,90510,90511],{}," max.latitude = MAX( max.latitude, a.coordinate.latitude );\n",[474,90513,90514],{"class":476,"line":950},[474,90515,90516],{}," max.longitude = MAX( max.longitude, a.coordinate.longitude );\n",[474,90518,90519],{"class":476,"line":956},[474,90520,15319],{},[474,90522,90523],{"class":476,"line":962},[474,90524,90525],{},"}\u003Cbr/>\n",[474,90527,90528],{"class":476,"line":4876},[474,90529,90530],{},"CLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\n",[474,90532,90533],{"class":476,"line":4888},[474,90534,90535],{},"CLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\n",[474,90537,90538],{"class":476,"line":4900},[474,90539,90540],{},"CLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\n",[474,90542,90543],{"class":476,"line":4913},[474,90544,90545],{},"CLLocationCoordinate2D regionCenter;\n",[474,90547,90548],{"class":476,"line":4921},[474,90549,90550],{},"regionCenter.latitude = (min.latitude + max.latitude) / 2.0;\n",[474,90552,90553],{"class":476,"line":4932},[474,90554,90555],{},"regionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\n",[474,90557,90558],{"class":476,"line":4938},[474,90559,90560],{},"CLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\n",[474,90562,90563],{"class":476,"line":4946},[474,90564,90565],{},"CLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\n",[474,90567,90568],{"class":476,"line":4952},[474,90569,90570],{},"MKCoordinateRegion region;\n",[474,90572,90573],{"class":476,"line":4957},[474,90574,90575],{},"region = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\n",[474,90577,90578],{"class":476,"line":4969},[474,90579,90580],{},"MKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n",[474,90582,90583],{"class":476,"line":4990},[474,90584,90585],{},"[self.mapView setRegion: fitRegion animated: YES];\n",[474,90587,90588],{"class":476,"line":5001},[474,90589,90590],{},"[locSouthWest release];\n",[474,90592,90593],{"class":476,"line":5013},[474,90594,90595],{},"[locSouthEast release];\n",[474,90597,90598],{"class":476,"line":5024},[474,90599,90600],{},"[locNorthEast release];\n",[439,90602,90603],{},"The code might look complicated, but we can break it down into 3 steps:",[8310,90605,90606],{},[997,90607,90608],{},"Iterate over all our custom annotations (we only have one) to determine the max/min latitude and longitude",[994,90610,90611,90624],{},[997,90612,90613,90614,520,90617,18175,90620,90623],{},"This results in a triangle ",[990,90615,90616],{},"locSouthWest",[990,90618,90619],{},"locSouthEast",[990,90621,90622],{},"locNorthEast",", that we can use to determine the center\nfor our zoom",[997,90625,90626,90627,90630],{},"Using the distance between the triangle points and the center, we can fit the map into a ",[990,90628,90629],{},"MKCoordinateRegion",", that\nwill zoom it so that everything fits on the screen.",[439,90632,90633,90634,90637],{},"One thing this algorithm doesn’t include, is the ",[990,90635,90636],{},"AnnotationView"," that display the name and location of the red pin. It\nsometimes happens, that it’s displayed outside of the screen, once you tap on the pin. However, if you add the code\nabove and hook it into the following method:",[464,90639,90641],{"className":83142,"code":90640,"language":83144,"meta":469,"style":469},"\n- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n\n",[471,90642,90643,90647],{"__ignoreMap":469},[474,90644,90645],{"class":476,"line":477},[474,90646,917],{"emptyLinePlaceholder":916},[474,90648,90649],{"class":476,"line":507},[474,90650,90651],{},"- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n",[439,90653,90654,90655,90658],{},"You still need to add the code, which handles the annotation, but you can feel free to steal it\nfrom ",[1002,90656,22987],{"href":90401,"rel":90657},[1006],". The result should look something\nlike this:",[439,90660,90661],{},[1002,90662,90665],{"href":90663,"rel":90664},"https://media.synyx.de/uploads//2010/05/screen_synyx_map.png",[1006],[2205,90666],{"alt":90414,"src":90667,"title":90416},"https://media.synyx.de/uploads//2010/05/screen_synyx_map-207x300.png",[439,90669,90670,90671,90674],{},"This concludes our little tutorial on Google Maps and ",[990,90672,90673],{},"MapKit",". I hope it was helpful! If you need any assistance, just\nleave a comment or drop me an email, I’ll be happy to help you out.",[1024,90676,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":90678},[],[11122,1413],"2010-05-19T06:44:08","In\\nmy first installment,\\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\\ndownload the code for this tutorial on github.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",{"title":90384,"description":90686},"In\nmy first installment,\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on github.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[89447,78398],"In my first installment, we laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can download the code…","RvtfFEehMAMdrgNNniaq_y-Jv6-wPsB0ASQmedbws-g",{"id":90692,"title":90693,"author":90694,"body":90695,"category":90768,"date":90769,"description":90770,"extension":1034,"link":90771,"meta":90772,"navigation":916,"path":90773,"seo":90774,"slug":90699,"stem":90776,"tags":90777,"teaser":90778,"__hash__":90779},"blog/blog/lessons-learned-iphone-review.md","Lessons learned – iPhone Review",[223],{"type":432,"value":90696,"toc":90766},[90697,90700,90719,90763],[435,90698,90693],{"id":90699},"lessons-learned-iphone-review",[439,90701,90702,90703,8351,90708,8351,90713,90718],{},"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\nAlthough ",[1002,90704,90707],{"href":90705,"rel":90706},"http://apprejections.com/index.php/post/171",[1006],"there are",[1002,90709,90712],{"href":90710,"rel":90711},"http://www.mobileorchard.com/avoiding-iphone-app-rejection-from-apple/",[1006],"plenty of resources",[1002,90714,90717],{"href":90715,"rel":90716},"https://web.archive.org/web/20100809102557/http://iphone.derheckser.com:80/2009/07/10/suffering-from-modus-operandi-of-reviewer-team/",[1006],"out there",",\nhere’s a short rundown of what we’ve learned ourselves so far:",[994,90720,90721,90727,90733,90739,90751],{},[997,90722,90723,90726],{},[448,90724,90725],{},"Marketing Apps are not allowed","\nIf the sole purpose of your App is marketing, you’ll have a hard time getting your App through. You need to add what\nApple calls “user functionality”. That could be something simple like a photo gallery or a feature to reserve a room\nor table.",[997,90728,90729,90732],{},[448,90730,90731],{},"You cannot tease your users with features that they have to pay for","\nIf you are offering a lite version of your App, you cannot add disabled functionality, which would be enabled in the\npaid version. A lite version usually is offered separately from a paid version, which means the user will constantly\nsee disabled menu items or buttons, since the App will never be updated. Instead add a info section about the paid\nversion in your App, which describes what the paid version offers.",[997,90734,90735,90738],{},[448,90736,90737],{},"Don’t ask your users to upgrade","\nYou cannot add an alert in a free/lite version of your App, which asks users to upgrade or buy the paid version.\nInstead you should add a “buy me” button or a section in your App further describing what your paid version offers.",[997,90740,90741,90744,90745,90750],{},[448,90742,90743],{},"Build a working App","\nYou are definitely rejected if your App is buggy! If the reviewer thinks he found a bug, he’ll reject your App. A\ncrash is the worst case scenario,\nbut ",[1002,90746,90749],{"href":90747,"rel":90748},"http://dlinsin.blogspot.com/2010/05/don-forget-your-linker-flags.html",[1006],"it happens",". However, don’t depend on\nApple as your QA, because the review times are too long to go back and forth this way.",[997,90752,90753,90756,90757,90762],{},[448,90754,90755],{},"Don’t infringe Trademarks or Copyrights","\nDon’t mention\nApple, ",[1002,90758,90761],{"href":90759,"rel":90760},"http://www.geek.com/articles/mobile/apple-demands-a-developer-removes-android-references-from-iphone-app-2010024/",[1006],"Android","\nor any other Trademark therefore – as long as you don’t own it. You should also resist to use iPhone like icons or\nimages.",[439,90764,90765],{},"If you respect all of these restrictions and gotchas, you should be save to get your App through the review process. I\nsay “should”, because it all appears to depend on the person who reviews your App. Let us know what you experienced,\nsubmitting your Apps, I bet there are a lot more of these gotchas.",{"title":469,"searchDepth":507,"depth":507,"links":90767},[],[11122,1413],"2010-05-13T12:37:53","When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\\nAlthough there are plenty of resources out there,\\nhere’s a short rundown of what we’ve learned ourselves so far:","https://synyx.de/blog/lessons-learned-iphone-review/",{},"/blog/lessons-learned-iphone-review",{"title":90693,"description":90775},"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple.\nAlthough there are plenty of resources out there,\nhere’s a short rundown of what we’ve learned ourselves so far:","blog/lessons-learned-iphone-review",[63945,78398],"When you submit an App to the Apple App Store it has to go through a “rigorous quality check”, conducted by Apple. Although there are plenty of resources out there,…","T4mUZEpPLjFmQlCw35HyM72gG1M41S4jYsCCinauV0A",{"id":90781,"title":90782,"author":90783,"body":90784,"category":90898,"date":90899,"description":90900,"extension":1034,"link":90901,"meta":90902,"navigation":916,"path":90903,"seo":90904,"slug":90788,"stem":90906,"tags":90907,"teaser":90909,"__hash__":90910},"blog/blog/user-statistics-from-synyxsudoku.md","User statistics from SynyxSudoku",[190],{"type":432,"value":90785,"toc":90896},[90786,90789,90797,90805,90808,90846,90849,90893],[435,90787,90782],{"id":90788},"user-statistics-from-synyxsudoku",[439,90790,90791,90792,90796],{},"First of all, I was quite surprised as i saw that 70% of the ",[1002,90793,89617],{"href":90794,"rel":90795},"http://tinyurl.com/SynyxSudoku",[1006]," users that\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\nthan 10-20%.",[439,90798,90799,90800,90804],{},"So as you can imagine, we’ve got quite a few samples of data since\nthe ",[1002,90801,22737],{"href":90802,"rel":90803},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku/",[1006],", on that we now want to give you a little\noverview.",[439,90806,90807],{},"The devices came mostly with the latest Android versions available:",[27328,90809,90810,90820],{},[27331,90811,90812],{},[27334,90813,90814,90817],{},[27337,90815,90816],{},"Version",[27337,90818,90819],{},"Count",[27342,90821,90822,90830,90838],{},[27334,90823,90824,90827],{},[27347,90825,90826],{},"1.6",[27347,90828,90829],{},"39%",[27334,90831,90832,90835],{},[27347,90833,90834],{},"2.1",[27347,90836,90837],{},"11%",[27334,90839,90840,90843],{},[27347,90841,90842],{},"2.1-update1",[27347,90844,90845],{},"50%",[439,90847,90848],{},"What I can say about the reported resolutions is, that the smaller devices (240×320) aren’t that popular as it seems (\nmaybe because there’s only the HTC Tattoo that uses this resolution), but the others are quite evenly matched.",[27328,90850,90851,90860],{},[27331,90852,90853],{},[27334,90854,90855,90858],{},[27337,90856,90857],{},"Resolution",[27337,90859,90819],{},[27342,90861,90862,90870,90878,90886],{},[27334,90863,90864,90867],{},[27347,90865,90866],{},"240×320",[27347,90868,90869],{},"9%",[27334,90871,90872,90875],{},[27347,90873,90874],{},"320×480",[27347,90876,90877],{},"33%",[27334,90879,90880,90883],{},[27347,90881,90882],{},"480×800",[27347,90884,90885],{},"25%",[27334,90887,90888,90891],{},[27347,90889,90890],{},"480×854",[27347,90892,90877],{},[439,90894,90895],{},"In terms of the reported devices, the Motorola Droid and the HTC Desire are at the top of the list, followed by the HTC\nMagic, HTC Tattoo, HTC Legend, G1 and the Sony Ericsson Xperia X10i.",{"title":469,"searchDepth":507,"depth":507,"links":90897},[],[11122,3799],"2010-05-10T17:30:45","First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that\\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\\nthan 10-20%.","https://synyx.de/blog/user-statistics-from-synyxsudoku/",{},"/blog/user-statistics-from-synyxsudoku",{"title":90782,"description":90905},"First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\nthan 10-20%.","blog/user-statistics-from-synyxsudoku",[11132,90908],"statistics","First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that uploaded their highscores have also agreed to send us their device specific data,…","Lu0rWnhyc5_KUTrPjR1VMv11nvaY7-qq8Fh410R9lDA",{"id":90912,"title":90913,"author":90914,"body":90915,"category":90955,"date":90956,"description":90957,"extension":1034,"link":90958,"meta":90959,"navigation":916,"path":90960,"seo":90961,"slug":90963,"stem":90964,"tags":90965,"teaser":90967,"__hash__":90968},"blog/blog/synyxsudoku-update-to-version-1-02.md","SynyxSudoku update to version 1.02",[190],{"type":432,"value":90916,"toc":90953},[90917,90920,90927,90930,90933,90936,90939,90942,90945],[435,90918,90913],{"id":90919},"synyxsudoku-update-to-version-102",[439,90921,90922,90923,90926],{},"There were a few little things that had to be changed on SynyxSudoku after\nthe ",[1002,90924,22737],{"href":90802,"rel":90925},[1006],", so today we uploaded an update to the version\n1.02.",[439,90928,90929],{},"Here’s a quick changelog:",[439,90931,90932],{},"* You can now only resume a game after a restart of the app, if you started a game in the previous session (or before)\nand didn’t solve it. (If the app was moved into the background while a solved game was active, you can still resume it\nif you like. It’s only gone if you close the app.)",[439,90934,90935],{},"* If you created a game and restarted the app twice you without resuming the sudoku on the first time, you couldn’t\nresume it the second time, it only displayed a empty field (Thanks to Markus who found this bug).",[439,90937,90938],{},"* The sleep mode is now deactivated if you are on the sudoku screen. (It’s quite annoying if you are in midst of your\nthinking process and then the screen suddenly turns black…)",[439,90940,90941],{},"If you notice any bugs or if something bothers you about the game, please leave a comment, or write us an email.",[439,90943,90944],{},"Download:",[439,90946,90947],{},[1002,90948,90950],{"href":90794,"rel":90949},[1006],[2205,90951],{"alt":469,"src":90952},"https://media.synyx.de/uploads//2010/04/qrcode.png",{"title":469,"searchDepth":507,"depth":507,"links":90954},[],[11122,3799],"2010-05-10T14:09:19","There were a few little things that had to be changed on SynyxSudoku after\\nthe release, so today we uploaded an update to the version\\n1.02.","https://synyx.de/blog/synyxsudoku-update-to-version-1-02/",{},"/blog/synyxsudoku-update-to-version-1-02",{"title":90913,"description":90962},"There were a few little things that had to be changed on SynyxSudoku after\nthe release, so today we uploaded an update to the version\n1.02.","synyxsudoku-update-to-version-1-02","blog/synyxsudoku-update-to-version-1-02",[11132,90966],"sudoku","There were a few little things that had to be changed on SynyxSudoku after the release, so today we uploaded an update to the version 1.02. Here’s a quick changelog:…","coEsVJmESzqGoRiOx4tGG-oKCIrhuqY3Q-snEzeUi-A",{"id":90970,"title":90971,"author":90972,"body":90973,"category":91130,"date":91131,"description":91132,"extension":1034,"link":91133,"meta":91134,"navigation":916,"path":91135,"seo":91136,"slug":90977,"stem":91138,"tags":91139,"teaser":91141,"__hash__":91142},"blog/blog/google-maps-on-android-part-2-overlays.md","Google Maps on Android – Part 2: Overlays",[133],{"type":432,"value":90974,"toc":91128},[90975,90978,90986,91003,91006,91009,91012,91015,91018,91021,91024,91035,91049,91052,91055,91058,91060,91067,91070,91073,91089,91092,91095,91098,91100,91106],[435,90976,90971],{"id":90977},"google-maps-on-android-part-2-overlays",[439,90979,87804,90980,90985],{},[1002,90981,90984],{"href":90982,"rel":90983},"http://mobile.synyx.de/2010/04/30/google-maps-on-android/",[1006],"last post about Google Maps on Android"," I showed you\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\nlevel.",[439,90987,90988,90989,90994,90995,90998,90999,91002],{},"Now as you have centred your map and zoomed in, it would be a good idea to show some kind of marker. Otherwise the user\nwon’t actually realize what you want to show him. How a basic overlay is done, is described in\nthe ",[1002,90990,90993],{"href":90991,"rel":90992},"http://developer.android.com/resources/tutorials/views/hello-mapview.html",[1006],"tutorial on the android developer site",",\nalready mentioned in the ",[1002,90996,89520],{"href":90982,"rel":90997},[1006],". The\n",[990,91000,91001],{},"HelloItemizedOverlay"," that is created there, enables you to add as many markers to your map as you want:",[439,91004,91005],{},"`List mapOverlays = mapView.getOverlays();",[439,91007,91008],{},"Drawable drawable = this.getResources().getDrawable(R.drawable.synyxLogo);",[439,91010,91011],{},"HelloItemizedOverlay itemizedOverlay = new HelloItemizedOverlay(drawable);",[439,91013,91014],{},"OverlayItem synyxOfficeOverlay = new OverlayItem(synyxOfficeLocation, \"Synyx\", \"This is the Synyx office!\");",[439,91016,91017],{},"itemizedOverlay.addOverlay(synyxOfficeOverlay);",[439,91019,91020],{},"// add more overlayItems...",[439,91022,91023],{},"mapOverlays.add(itemizedOverlay);`",[439,91025,91026,91027,91030,91031,91034],{},"This will draw a nice marker just at the location of the synyx office. The drawback of this method is, that all markers\non the map just look the same. You might think: No problem – just use the ",[990,91028,91029],{},"setMarker()"," method of the ",[990,91032,91033],{},"OverlayItem"," to\nset a new marker for just this item. As this looks like the way to do it, it won’t work. All you get is no marker (not\nthe one you just set, nor the default one). The map is just shown as if your marker was not defined at all.",[439,91036,91037,91038,91041,91042,91045,91046,91048],{},"So how to do it? The trick is the static method ",[990,91039,91040],{},"boundCenterBottom()"," of the ",[990,91043,91044],{},"ItemizedOverlay"," class. This method is\nalso called in the constructor of the ",[990,91047,91001],{},", when the default marker is set:",[439,91050,91051],{},"`public RouteMapOverlay(Drawable defaultMarker, Context context) {",[439,91053,91054],{},"super(boundCenterBottom(defaultMarker));",[439,91056,91057],{},"this.context = context;",[439,91059,34952],{},[439,91061,91062,91063,91066],{},"The problem is, that the visibility of this method is set to ",[990,91064,91065],{},"protected"," (why?!). So calling it when setting the marker\nwon’t work:",[439,91068,91069],{},"`// won't work as boundCenterBottom is protected",[439,91071,91072],{},"synyxOfficeOverlay.setOverlay(ItemizedOverlay.boundCenterBottom(myNewMarker));`",[439,91074,91075,91076,91078,91079,91081,91082,91085,91086,91088],{},"Because of this visibility feature, the “easier” way is to add a new setter to your ",[990,91077,91001],{}," class,\naccepting an ",[990,91080,91033],{}," and a ",[990,91083,91084],{},"Drawable"," to use as the marker. Within that setter, you are able to call the\n",[990,91087,91040],{}," method to set up the marker correctly:",[439,91090,91091],{},"`public void addOverlay(OverlayItem overlayItem, Drawable marker) {",[439,91093,91094],{},"overlayItem.setMarker(boundCenterBottom(marker));",[439,91096,91097],{},"addOverlay(overlayItem);",[439,91099,34952],{},[439,91101,91102,91103,91105],{},"Now if you use that setter, the map will correctly show your ",[990,91104,91033],{}," with your marker.",[439,91107,91108,91109,91111,91112,91114,91115,91118,91119,91122,91123,91125,91126,1402],{},"Btw: instead of calling ",[990,91110,91040],{}," (which places the bottom centre of your ",[990,91113,91084],{}," over the defined\n",[990,91116,91117],{},"GeoPoint","), you can also use ",[990,91120,91121],{},"boundCenter()"," to place the centre of your ",[990,91124,91084],{}," over the ",[990,91127,91117],{},{"title":469,"searchDepth":507,"depth":507,"links":91129},[],[11122,1413],"2010-05-07T16:52:13","In my last post about Google Maps on Android I showed you\\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\\nlevel.","https://synyx.de/blog/google-maps-on-android-part-2-overlays/",{},"/blog/google-maps-on-android-part-2-overlays",{"title":90971,"description":91137},"In my last post about Google Maps on Android I showed you\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\nlevel.","blog/google-maps-on-android-part-2-overlays",[11132,89447,91140,88557],"marker","In my last post about Google Maps on Android I showed you how to use the basic navigation features of google maps, like moving the map to a defined area…","GBZ-bHS5o5wBdJ9oZTRvsybQjazx-suQybxkfoerVPM",{"id":91144,"title":91145,"author":91146,"body":91147,"category":91250,"date":91251,"description":91252,"extension":1034,"link":91253,"meta":91254,"navigation":916,"path":91255,"seo":91256,"slug":91151,"stem":91257,"tags":91258,"teaser":91260,"__hash__":91261},"blog/blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails.md","RedmineUpgrade – Ruby on Debian without leaving the rails",[314],{"type":432,"value":91148,"toc":91248},[91149,91152,91155,91158,91166,91169,91174,91187,91190,91193,91196,91199,91219,91222,91228,91231,91239,91242,91245],[435,91150,91145],{"id":91151},"redmineupgrade-ruby-on-debian-without-leaving-the-rails",[439,91153,91154],{},"Auf der Suche nach “dem Ticketsystem” sind wir nach Scarab und Trac auf Redmine gestossen, einer Ruby-on-rails\napplication, die ihren Zweck erfüllt und unseren Bedürfnissen durchaus gerecht wird. Aber da Redmine glücklicherweise\nauch weiterentwickelt wird, verlangt auch diese Applikation hin und wieder ein Upgrade (dieses mal war es ein\nVersionssprung von 0.8.7 auf 0.9.4).",[439,91156,91157],{},"Das Setup:",[994,91159,91160,91163],{},[997,91161,91162],{},"Debian GNU/Linux 5.0",[997,91164,91165],{},"Apache2.2 mit Phusion Passenger (a.k.a. mod_rails; mod_fcgi oder mod_fastcgi sind eher keine gelungen Apache-Module,\ndas liegt wohl in der Natur von fcgi 😉 )",[439,91167,91168],{},"Nun sind schon bei vergangenen Updates verschiedene Stolpersteine ans Tageslicht gekommen, einige erwiesen sich\nallerdings als ein und der selbe:",[439,91170,91171],{},[990,91172,91173],{},"An der Debian-Paket-Verwaltung vorbei(!) die richtigen Versionen (wie im Redmine-Upgrade-Tutorial genannt)\ninstallieren",[994,91175,91176],{},[997,91177,91178,91179,91182,91183],{},"rubygems – wichtig hierbei ",[448,91180,91181],{},"nicht"," das Paket “rubygems1.8” aus dem Debian-Repository verwenden sondern von Hand\ninstallieren: `wget ",[1002,91184,91185],{"href":91185,"rel":91186},"http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz",[1006],[439,91188,91189],{},"tar xzf rubygems-1.3.1.tgz",[439,91191,91192],{},"cd rubygems-1.3.1",[439,91194,91195],{},"sudo ruby setup.rb",[439,91197,91198],{},"gem -v`",[994,91200,91201,91210],{},[997,91202,91203,91204,91206,91207],{},"Rake – auch hier gilt ",[448,91205,91181],{}," das Paket “rake” v=0.7.1-1 aus dem Debian-Repository verwenden. Man nutze den\n“Ruby-way”: ",[471,91208,91209],{},"gem install rake -v=0.8.3",[997,91211,91212,91213,91215,91216],{},"rails – guess what… richtig, ",[448,91214,91181],{}," das Paket “rails” aus dem Debian-Repository verwenden. Man nutze abermals den\n“Ruby-way”: ",[471,91217,91218],{},"gem install rails -v=2.3.5",[439,91220,91221],{},"Nachdem die Gleise damit verlegt sind, funktioniert auch der Upgrade-Guide wie gewünscht.",[439,91223,91224,91225],{},"Wenn der kritische “point of no return” ",[471,91226,91227],{},"rake db:migrate RAILS_ENV=production",[439,91229,91230],{},"überstanden ist, die neue Version von Redmine gestartet wurde, aber dann Dinge wie:",[994,91232,91233,91236],{},[997,91234,91235],{},"“*** Exception NoMethodError in application (undefined method…”",[997,91237,91238],{},"“Premature end of script headers…”",[439,91240,91241],{},"im Apache-Errorlog auftauchen oder man die Meldung “application could not be started…” im Browser sieht,dann hilft ein\nUpdate von Phusion Passenger (auf Version 2.2.11 ): `gem install passenger",[439,91243,91244],{},"passenger-install-apache2-module`",[439,91246,91247],{},"Sobald auch die letzten Anweisungen der Installation befolgt worden sind, können die Bugs kommen.",{"title":469,"searchDepth":507,"depth":507,"links":91249},[],[13208],"2010-05-07T09:05:46","Auf der Suche nach “dem Ticketsystem” sind wir nach Scarab und Trac auf Redmine gestossen, einer Ruby-on-rails\\napplication, die ihren Zweck erfüllt und unseren Bedürfnissen durchaus gerecht wird. Aber da Redmine glücklicherweise\\nauch weiterentwickelt wird, verlangt auch diese Applikation hin und wieder ein Upgrade (dieses mal war es ein\\nVersionssprung von 0.8.7 auf 0.9.4).","https://synyx.de/blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails/",{},"/blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails",{"title":91145,"description":91154},"blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails",[73058,29262,91259,46998],"debian","Auf der Suche nach “dem Ticketsystem” sind wir nach Scarab und Trac auf Redmine gestossen, einer Ruby-on-rails application, die ihren Zweck erfüllt und unseren Bedürfnissen durchaus gerecht wird. Aber da…","kojvi8lS66BVvtV-_dEKfjXkUvQ2IgNFu8PR1xO5XFA",{"id":91263,"title":91264,"author":91265,"body":91266,"category":91356,"date":91357,"description":91358,"extension":1034,"link":91359,"meta":91360,"navigation":916,"path":91361,"seo":91362,"slug":91270,"stem":91363,"tags":91364,"teaser":91365,"__hash__":91366},"blog/blog/java-mail-properties-esmtp-authentication-error.md","Java Mail Properties – esmtp authentication error",[142],{"type":432,"value":91267,"toc":91354},[91268,91271,91274,91277,91311,91314,91349,91352],[435,91269,91264],{"id":91270},"java-mail-properties-esmtp-authentication-error",[439,91272,91273],{},"Last week, we had a problem with sending emails from our application. Currently we use the spring email integration to\ndo this.",[439,91275,91276],{},"An esmtp server runs at customers side, which does not accept the default mail settings we made for our local test\nenvironment:",[464,91278,91280],{"className":6253,"code":91279,"language":6255,"meta":469,"style":469},"\u003Cbean>\n \u003Cproperty name=\"host\" value=\"${mail.host}\"/>\n \u003Cproperty name=\"port\" value=\"${mail.port}\" />\n \u003Cproperty name=\"username\" value=\"${mail.user}\" />\n \u003Cproperty name=\"password\" value=\"${mail.password}\" />\n\u003C/bean>\n",[471,91281,91282,91287,91292,91297,91302,91307],{"__ignoreMap":469},[474,91283,91284],{"class":476,"line":477},[474,91285,91286],{},"\u003Cbean>\n",[474,91288,91289],{"class":476,"line":507},[474,91290,91291],{}," \u003Cproperty name=\"host\" value=\"${mail.host}\"/>\n",[474,91293,91294],{"class":476,"line":547},[474,91295,91296],{}," \u003Cproperty name=\"port\" value=\"${mail.port}\" />\n",[474,91298,91299],{"class":476,"line":584},[474,91300,91301],{}," \u003Cproperty name=\"username\" value=\"${mail.user}\" />\n",[474,91303,91304],{"class":476,"line":607},[474,91305,91306],{}," \u003Cproperty name=\"password\" value=\"${mail.password}\" />\n",[474,91308,91309],{"class":476,"line":642},[474,91310,69979],{},[439,91312,91313],{},"Following property fixed the problem (after a long time of experimenting with different mail.smtp.* properties!):",[464,91315,91317],{"className":6253,"code":91316,"language":6255,"meta":469,"style":469},"\u003Cproperty name=\"javaMailProperties\">\n \u003Cprops>\n \u003Cprop key=\"mail.smtp.localhost\">mylocalhost\u003C/prop>\n \u003Cprop key=\"mail.smtp.ehlo\">false\u003C/prop>\n \u003C/props>\n\u003C/property>\n\n",[471,91318,91319,91324,91329,91334,91339,91344],{"__ignoreMap":469},[474,91320,91321],{"class":476,"line":477},[474,91322,91323],{},"\u003Cproperty name=\"javaMailProperties\">\n",[474,91325,91326],{"class":476,"line":507},[474,91327,91328],{}," \u003Cprops>\n",[474,91330,91331],{"class":476,"line":547},[474,91332,91333],{}," \u003Cprop key=\"mail.smtp.localhost\">mylocalhost\u003C/prop>\n",[474,91335,91336],{"class":476,"line":584},[474,91337,91338],{}," \u003Cprop key=\"mail.smtp.ehlo\">false\u003C/prop>\n",[474,91340,91341],{"class":476,"line":607},[474,91342,91343],{}," \u003C/props>\n",[474,91345,91346],{"class":476,"line":642},[474,91347,91348],{},"\u003C/property>\n",[439,91350,91351],{},"With these two settings you create at least the following command that is required to send the mails : helo mylocalhost",[1024,91353,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":91355},[],[1030],"2010-05-06T17:22:50","Last week, we had a problem with sending emails from our application. Currently we use the spring email integration to\\ndo this.","https://synyx.de/blog/java-mail-properties-esmtp-authentication-error/",{},"/blog/java-mail-properties-esmtp-authentication-error",{"title":91264,"description":91273},"blog/java-mail-properties-esmtp-authentication-error",[],"Last week, we had a problem with sending emails from our application. Currently we use the spring email integration to do this. An esmtp server runs at customers side, which…","JuUb87jTtYgzUj6ke8EUX_BsQ7XcnNlJOrPQYaKisqg",{"id":91368,"title":91369,"author":91370,"body":91371,"category":91579,"date":91580,"description":91581,"extension":1034,"link":91582,"meta":91583,"navigation":916,"path":91584,"seo":91585,"slug":91375,"stem":91587,"tags":91588,"teaser":91589,"__hash__":91590},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i.md","How to add a 'Find Your Company' feature to your iPhone App – Part I",[223],{"type":432,"value":91372,"toc":91577},[91373,91377,91389,91396,91412,91415,91423,91439,91451,91504,91519,91553,91572,91575],[435,91374,91376],{"id":91375},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i","How to add a \"Find Your Company\" feature to your iPhone App – Part I",[439,91378,91379,91380,91382,91383,91388],{},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the ",[990,91381,90673],{}," framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof ",[1002,91384,91387],{"href":91385,"rel":91386},"http://developer.apple.com/iphone/library/samplecode/CurrentAddress/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009469",[1006],"sample code",",\nwhich gets you up and running in no time.",[439,91390,91391,91392,91395],{},"What I’d like to show you today, is some code, which nicely zooms the Map to your office and current location of the App\nuser, once the ",[990,91393,91394],{},"MapView"," is loaded. Here are a couple of screenshots to give you an idea of what I’m talking about:",[439,91397,91398,8351,91403,91411],{},[1002,91399,91401],{"href":90663,"rel":91400},[1006],[2205,91402],{"alt":90414,"src":90667,"title":90416},[1002,91404,91407],{"href":91405,"rel":91406},"https://media.synyx.de/uploads//2010/05/screen_synyx_map_1.png",[1006],[2205,91408],{"alt":91409,"src":91410,"title":91409},"Synyx Routing","https://media.synyx.de/uploads//2010/05/screen_synyx_map_1-209x300.png","\nAs you can see, Synyx moved their offices to San Francisco. The iPhone App user, with the blue dot, is currently in\ncupertino. If he taps on the white/blue arrow in the annotation of the red dot, he is asked whether he’d really want to\nleave the App and start Google Maps to route from his current location to the Synyx Offices in San Francisco.",[439,91413,91414],{},"Now, the main focus on this blog post is how to get you using this in your App. I’m gonna split this little tutorial in\n2 parts:",[8310,91416,91417,91420],{},[997,91418,91419],{},"Showing the Synyx Offices on the Map",[997,91421,91422],{},"Zooming in, so that everything fits on the screen",[439,91424,91425,91426,91428,91429,91432,91433,91438],{},"If you feel like going off on your own, the code for the ",[990,91427,90219],{}," used, is available for download\non ",[1002,91430,22987],{"href":90401,"rel":91431},[1006],". The zooming algorithm is borrowed\nfrom ",[1002,91434,91437],{"href":91435,"rel":91436},"http://stackoverflow.com/questions/1303265/algorithm-for-determining-minimum-bounding-rectangle-for-collection-of-lat-lon-co/1413264#1413264",[1006],"stackoverflow",".\nHowever, the code is one of my first iPhone projects and not polished, so don’t use it blindly. There are a couple of\nissue, e.g. I’m pretty sure it won’t survive a memory warning. It’s meant for demonstration solely!",[439,91440,91441,91442,91444,91445,91447,91448,91450],{},"So let’s get into it. We first need a ",[990,91443,90219],{}," with a ",[990,91446,91394],{}," associated. It is pretty straightforward and\nyou should have no problems doing that in Interface Builder. The next thing you need and that is missing from the code\non github is a custom ",[990,91449,90673],{}," annotation:",[464,91452,91454],{"className":83142,"code":91453,"language":83144,"meta":469,"style":469},"\n@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n CLLocationCoordinate2D coordinate;\n NSString *title;\n NSString *subtitle;\n}\n- (id)initWith:(CLLocationCoordinate2D)_coords;\n@property(retain,nonatomic) NSString *title;\n@property(retain,nonatomic) NSString *subtitle;\n@end\n\n",[471,91455,91456,91460,91465,91470,91475,91480,91484,91489,91494,91499],{"__ignoreMap":469},[474,91457,91458],{"class":476,"line":477},[474,91459,917],{"emptyLinePlaceholder":916},[474,91461,91462],{"class":476,"line":507},[474,91463,91464],{},"@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n",[474,91466,91467],{"class":476,"line":547},[474,91468,91469],{}," CLLocationCoordinate2D coordinate;\n",[474,91471,91472],{"class":476,"line":584},[474,91473,91474],{}," NSString *title;\n",[474,91476,91477],{"class":476,"line":607},[474,91478,91479],{}," NSString *subtitle;\n",[474,91481,91482],{"class":476,"line":642},[474,91483,703],{},[474,91485,91486],{"class":476,"line":663},[474,91487,91488],{},"- (id)initWith:(CLLocationCoordinate2D)_coords;\n",[474,91490,91491],{"class":476,"line":694},[474,91492,91493],{},"@property(retain,nonatomic) NSString *title;\n",[474,91495,91496],{"class":476,"line":700},[474,91497,91498],{},"@property(retain,nonatomic) NSString *subtitle;\n",[474,91500,91501],{"class":476,"line":913},[474,91502,91503],{},"@end\n",[439,91505,91506,91507,91511,91512,91515,91516,1402],{},"This will be responsible for displaying the name and city of the Synyx Offices above the red pin on the map. But it’s\nonly responsible for displaying the information, it doesn’t know where yet. In order to find the location of our offices\nusing the Google Maps API, I came up with a little helper method\ncalled ",[1002,91508,91510],{"href":90401,"rel":91509},[1006],"synyxLocation (line 175)",". It simply\nreturns the ",[990,91513,91514],{},"CLLocationCoordinate2D"," struct, which is needed in our ",[990,91517,91518],{},"AddressAnnotation",[464,91520,91522],{"className":83142,"code":91521,"language":83144,"meta":469,"style":469},"\nCLLocationCoordinate2D location = [self synyxLocation];\nself.synyx = [[AddressAnnotation alloc] initWith:location];\n[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n[self.synyx setSubtitle:synyxLoc];\n[mapView addAnnotation:synyx];\n\n",[471,91523,91524,91528,91533,91538,91543,91548],{"__ignoreMap":469},[474,91525,91526],{"class":476,"line":477},[474,91527,917],{"emptyLinePlaceholder":916},[474,91529,91530],{"class":476,"line":507},[474,91531,91532],{},"CLLocationCoordinate2D location = [self synyxLocation];\n",[474,91534,91535],{"class":476,"line":547},[474,91536,91537],{},"self.synyx = [[AddressAnnotation alloc] initWith:location];\n",[474,91539,91540],{"class":476,"line":584},[474,91541,91542],{},"[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n",[474,91544,91545],{"class":476,"line":607},[474,91546,91547],{},"[self.synyx setSubtitle:synyxLoc];\n",[474,91549,91550],{"class":476,"line":642},[474,91551,91552],{},"[mapView addAnnotation:synyx];\n",[439,91554,91555,91556,91558,91559,91561,91562,91041,91565,91567,91568,91571],{},"Instantiating the ",[990,91557,91518],{}," with the previously determined location and adding it to the ",[990,91560,91394],{}," will to the\nrest. I did this in the ",[990,91563,91564],{},"viewDidLoad",[990,91566,90219],{},", which is not be the best place. Maybe the\n",[990,91569,91570],{},"viewWillAppear"," method would have been better.",[439,91573,91574],{},"After adding those parts discussed above, you can fire up the Simulator and get the location of our offices with a red\npin and your current location (in the Simulator it’s always Cupertino). The map doesn’t zoom yet and is not properly\nlocated, we leave that to our next installment.",[1024,91576,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":91578},[],[11122,1413],"2010-05-06T09:19:07","We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\\nawesome and they provide a lot\\nof sample code,\\nwhich gets you up and running in no time.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",{"title":91369,"description":91586},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof sample code,\nwhich gets you up and running in no time.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",[89447,78398],"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use Google Maps and the MapKit framework.…","_O6Obe2j2TOkOGkGbYXvcyu2cE2nivHG7cGF_hrejxA",{"id":91592,"title":91593,"author":91594,"body":91595,"category":91691,"date":91692,"description":91602,"extension":1034,"link":91693,"meta":91694,"navigation":916,"path":91695,"seo":91696,"slug":91599,"stem":91697,"tags":91698,"teaser":91699,"__hash__":91700},"blog/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration.md","Ausbildungsplatz zum Fachinformatiker/in Systemintegeration",[172],{"type":432,"value":91596,"toc":91689},[91597,91600,91603,91607,91621,91625,91642,91646,91660,91664,91675,91681,91683,91686],[435,91598,91593],{"id":91599},"ausbildungsplatz-zum-fachinformatikerin-systemintegeration",[439,91601,91602],{},"Wir haben noch einen Ausbildungsplatz als Fachinformatiker/in Systemintegration ab September 2010 zu vergeben.",[1633,91604,91606],{"id":91605},"deine-aufgaben","Deine Aufgaben:",[994,91608,91609,91612,91615,91618],{},[997,91610,91611],{},"Unterstützung bei der Planung und Erweiterung unserer Infrastruktur",[997,91613,91614],{},"Inbetriebnahme, Betreuung und Überwachung unserer Systeme (Debian, Ubuntu, SuSE, Windows 2000, Windows XP)",[997,91616,91617],{},"Unterstützen unserer Mitarbeiter bei technischen Problemen",[997,91619,91620],{},"Unterstützung von Kunden bei Software- und Hardwareproblemen",[1633,91622,91624],{"id":91623},"unsere-anforderungen","Unsere Anforderungen:",[994,91626,91627,91630,91633,91636,91639],{},[997,91628,91629],{},"Mittlere Reife oder Hochschulreife",[997,91631,91632],{},"Gute Englischkenntnisse in Wort und Schrift",[997,91634,91635],{},"Erfahrungen in Netzwerktechnik, TCP/IP",[997,91637,91638],{},"Gute EDV-Kenntnisse",[997,91640,91641],{},"Grundkenntnisse im Bereich Linux",[1633,91643,91645],{"id":91644},"das-solltest-du-mitbringen","Das solltest Du mitbringen:",[994,91647,91648,91651,91654,91657],{},[997,91649,91650],{},"Eigeninitiative und Engagement",[997,91652,91653],{},"Teamfähigkeit",[997,91655,91656],{},"Freude am Umgang mit neuen Technologien",[997,91658,91659],{},"Entschlossenheit, Lösungen für Probleme zu finden",[1633,91661,91663],{"id":91662},"das-erwartet-dich","Das erwartet Dich:",[994,91665,91666,91669,91672],{},[997,91667,91668],{},"Ein junges, innovatives Team",[997,91670,91671],{},"Ein technologisch spannendes Umfeld",[997,91673,91674],{},"Flexible, eigenständige, verantwortungsvolle Tätigkeiten",[439,91676,91677,91678,91680],{},"Bewerbungen bitte per Mail an ",[1002,91679,35012],{"href":35011}," oder per Post an:",[439,91682,89262],{},[439,91684,91685],{},"Karlstraße 68",[439,91687,91688],{},"D-76137 Karlsruhe",{"title":469,"searchDepth":507,"depth":507,"links":91690},[],[13208],"2010-05-03T11:08:44","https://synyx.de/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration/",{},"/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration",{"title":91593,"description":91602},"blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration",[13219,71055,389],"Wir haben noch einen Ausbildungsplatz als Fachinformatiker/in Systemintegration ab September 2010 zu vergeben. Deine Aufgaben: Unterstützung bei der Planung und Erweiterung unserer Infrastruktur Inbetriebnahme, Betreuung und Überwachung unserer Systeme (Debian,…","exUqJwKlx_VsMnsLbWd_obLbh3PP-k1D2p81qk_HnbU",{"id":91702,"title":91703,"author":91704,"body":91705,"category":91732,"date":91733,"description":91734,"extension":1034,"link":91735,"meta":91736,"navigation":916,"path":91737,"seo":91738,"slug":91709,"stem":91739,"tags":91740,"teaser":91741,"__hash__":91742},"blog/blog/synyx-bloggt.md","Synyx bloggt",[172],{"type":432,"value":91706,"toc":91730},[91707,91710,91713,91716,91719,91727],[435,91708,91703],{"id":91709},"synyx-bloggt",[439,91711,91712],{},"Paralell zur Überarbeitung der Synyx Homepage, starten wir unseren Corporate Blog. Interessenten werden dadurch stets\nüber Neues und Wissenswertes von Synyx informiert:",[439,91714,91715],{},"Unsere Entwickler berichten über aktuelle Themen aus ihrem Arbeitsalltag und geben ihre Tipps und Tricks zum besten.\nDazu gehören auch Blogs rund um das Thema Open Source.",[439,91717,91718],{},"Weiterhin wird es Einblicke hinter die Kulissen von Synyx geben. Lasst euch überraschen!",[439,91720,91721,91722,91726],{},"Doch das ist noch nicht alles! Seit einer Woche gibt es\neinen",[1002,91723,83379],{"href":77932,"rel":91724,"title":91725},[1006],"Mobile Solutions Blog",", welcher sich rund um die Themen Apple\niPhone, Google Android und MeeGo dreht. In diesem Blog steht vor allem die Technik im Vordergrund. Er soll Ihnen\nInformationen zu den neusten Entwicklungen und Applikationen widerspiegeln.",[439,91728,91729],{},"S7634VKZC748",{"title":469,"searchDepth":507,"depth":507,"links":91731},[],[1031],"2010-04-30T15:03:05","Paralell zur Überarbeitung der Synyx Homepage, starten wir unseren Corporate Blog. Interessenten werden dadurch stets\\nüber Neues und Wissenswertes von Synyx informiert:","https://synyx.de/blog/synyx-bloggt/",{},"/blog/synyx-bloggt",{"title":91703,"description":91712},"blog/synyx-bloggt",[18496,389],"Paralell zur Überarbeitung der Synyx Homepage, starten wir unseren Corporate Blog. Interessenten werden dadurch stets über Neues und Wissenswertes von Synyx informiert: Unsere Entwickler berichten über aktuelle Themen aus ihrem…","KsZ6He7TW5ziYsmaKrqRP-q1xjPb64j0nRZGtFPzsFo",{"id":91744,"title":91745,"author":91746,"body":91747,"category":91817,"date":91818,"description":91819,"extension":1034,"link":91820,"meta":91821,"navigation":916,"path":91822,"seo":91823,"slug":91825,"stem":91826,"tags":91827,"teaser":91829,"__hash__":91830},"blog/blog/google-maps-on-android.md","Google Maps on Android – Part 1: Navigation",[133],{"type":432,"value":91748,"toc":91815},[91749,91752,91758,91769,91774,91786,91789,91792,91795,91798],[435,91750,91745],{"id":91751},"google-maps-on-android-part-1-navigation",[439,91753,91754,91755,1402],{},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe ",[1002,91756,90993],{"href":90991,"rel":91757},[1006],[439,91759,91760,91761,91765,91766,91768],{},"Showing the map itself is one part, but most likely you want to interact with it in some way. The default map is\ncompletely zoomed out and centred somewhere over America. As this gives you a good idea how America looks like, it is\nnot very useful for showing the location of the Synyx office. So what we need to do, is to move the map to some point\nand zoom in to a certain level. First, lets get the coordinates for the Synyx office. This can easily be done by using\ngoogle maps: Navigate to ",[1002,91762,91764],{"href":90198,"rel":91763},[1006],"maps.google.com",", click on the “New” link on the top right and\nactivate the “LatLng Marker” from the Google Maps Labs. This adds an option to the context menu to drop a marker that\nshows the latitude/ longitude values. Use those values to create a new ",[990,91767,91117],{}," in your Android app:",[439,91770,91771],{},[471,91772,91773],{},"GeoPoint synyxOfficeLocation = new GeoPoint(49002175, 8394160);",[439,91775,91776,91777,91779,91780,91782,91783,6562],{},"As the ",[990,91778,91117],{}," handles its values in microdegrees, the values obtained from google maps must be multiplied with 100\n000. Now as we have the GeoPoint, we need to centre the map to it and zoom in to a certain level. To perform this\ntasks, each ",[990,91781,91394],{}," has a ",[990,91784,91785],{},"MapController",[439,91787,91788],{},"`MapController mapController = mapView.getController();",[439,91790,91791],{},"mapController.setCenter(synyxOfficeLocation);",[439,91793,91794],{},"mapController.setZoom(20);`",[439,91796,91797],{},"This centres the map to the Synyx office. The zoom level must be a value between 1 (fully zoomed out) and 21 (fully\nzoomed in), so the value of 20 is already quiet close. Please note, that the highest zoom levels might not be available\nfor all areas.",[439,91799,91800,91801,4715,91804,18175,91807,91810,91811,91814],{},"The above mentioned methods change the map in a very static way. For some more visual effects try ",[990,91802,91803],{},"animateTo()",[990,91805,91806],{},"zoomIn()",[990,91808,91809],{},"zoomOut()"," to change the view of the map by showing a short animation. An also very helpful method is\n",[990,91812,91813],{},"zoomToSpan()"," which lets you define a latitude and longitude span that should be visible on the map – very handy if you\nwant to ensure that two or more points are visible to the user on the map.",{"title":469,"searchDepth":507,"depth":507,"links":91816},[],[11122,1413],"2010-04-30T12:42:54","Integrating a google map on android is quiet simple – how to do this basically is shown in\\nthe tutorial on the android developer site.","https://synyx.de/blog/google-maps-on-android/",{},"/blog/google-maps-on-android",{"title":91745,"description":91824},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe tutorial on the android developer site.","google-maps-on-android","blog/google-maps-on-android",[11132,89447,91828],"interaction","Integrating a google map on android is quiet simple – how to do this basically is shown in the tutorial on the android developer site. Showing the map itself is…","I9Gaz8vCUclQdFQPhhnVZcaIFhvVrRr0ST0iLZtq7GQ",{"id":91832,"title":82565,"author":91833,"body":91834,"category":91897,"date":91898,"description":91899,"extension":1034,"link":91900,"meta":91901,"navigation":916,"path":91902,"seo":91903,"slug":82571,"stem":91905,"tags":91906,"teaser":91907,"__hash__":91908},"blog/blog/mobile-solutions-summary.md",[223],{"type":432,"value":91835,"toc":91895},[91836,91838,91845,91856,91863,91869,91881,91888],[435,91837,82565],{"id":82571},[439,91839,91840,91841,91844],{},"Since the inception of our mobile blog last month a lot has happened over at ",[1002,91842,77932],{"href":77932,"rel":91843},[1006],". I’m going to\nhighlight some of the stuff for you here.",[439,91846,22944,91847,91851,91852,91855],{},[1002,91848,91850],{"href":90802,"rel":91849},[1006],"biggest news"," is that our\ntrainee ",[1002,91853,90169],{"href":85791,"rel":91854},[1006]," released our very own Sudoku game for Android handsets:",[11947,91857,91858],{},[439,91859,91860],{},[990,91861,91862],{},"It’s completely free of charge! …. SynyxSudoku offers 3 difficulty levels, containing nearly unlimited puzzling fun\ndue to new created sudokus each time you launch! And even if none of these difficulty levels fits your needs –\nSynyxSudoku has the option to let you create a difficulty level of your own.",[439,91864,91865,91866,1402],{},"Our Synyx Sudoku was very well received and downloaded more than 300 times in less than a week. Congratulations to\nTobias and if you haven’t checked it out, head over to our ",[1002,91867,85841],{"href":82604,"rel":91868},[1006],[439,91870,91871,91872,91875,91876,6562],{},"If you are into the hard stuff, be sure to check out ",[1002,91873,80682],{"href":87878,"rel":91874},[1006],"\n‘s ",[1002,91877,91880],{"href":91878,"rel":91879},"http://mobile.synyx.de/2010/04/22/howto-startup-with-maemo-and-qt-4-6/",[1006],"tutorial on how to get started programing for the N900",[11947,91882,91883],{},[439,91884,91885],{},[990,91886,91887],{},"As a Java developer it was not easy for me to find the right entry point for developing c++, using the\ntrend-setting Qt 4.6 environment and having a cute ide with rapid prototyping capabilities. After a little bit of\nreading and lots of trials and errors i found a way for me that worked.",[439,91889,91890,91891,1402],{},"It’ll make things less tough and get you started with your development environment. Don’t miss Florian’s next\ninstallment and subscribe to the ",[1002,91892,91894],{"href":85784,"rel":91893},[1006],"mobile solutions blog’s feed",{"title":469,"searchDepth":507,"depth":507,"links":91896},[],[1030],"2010-04-30T06:46:25","Since the inception of our mobile blog last month a lot has happened over at http://mobile.synyx.de. I’m going to\\nhighlight some of the stuff for you here.","https://synyx.de/blog/mobile-solutions-summary/",{},"/blog/mobile-solutions-summary",{"title":82565,"description":91904},"Since the inception of our mobile blog last month a lot has happened over at http://mobile.synyx.de. I’m going to\nhighlight some of the stuff for you here.","blog/mobile-solutions-summary",[18496],"Since the inception of our mobile blog last month a lot has happened over at http://mobile.synyx.de. I’m going to highlight some of the stuff for you here. The biggest news…","5Tr_PJe_pLL4p-X-PYcVotPpC2D6CuNgaiXYPG0-eKc",{"id":91910,"title":91911,"author":91912,"body":91913,"category":92109,"date":92110,"description":92111,"extension":1034,"link":91923,"meta":92112,"navigation":916,"path":92113,"seo":92114,"slug":91917,"stem":92116,"tags":92117,"teaser":92119,"__hash__":92120},"blog/blog/modular-web-applications-based-on-spring.md","Modular Web-Applications based on Spring",[166],{"type":432,"value":91914,"toc":92107},[91915,91918,91927,91930,91939,91952,91955,91977,91983,91991,92001,92034,92051,92057,92095,92105],[435,91916,91911],{"id":91917},"modular-web-applications-based-on-spring",[439,91919,91920,91921,91926],{},"Many of the Web-Applications we develop for our customers are based upon our small Framework on top\nof ",[1002,91922,91925],{"href":91923,"rel":91924},"https://synyx.de/blog/modular-web-applications-based-on-spring/",[1006],"Spring / Spring MVC",". This framework basically\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\nSpring already does.",[439,91928,91929],{},"Modular design of applications brings a lot of advantages but – as always – also some disadvantages. A modular structure\ncan help to increase cohesion and let developers focus on the function their concrete module has. Another big thing is\nreusability. So the core framework already brings functionality that is used in all projects that depend on the\nframework so far: user management for example. On the other hand modular design also brings complexity. I think its\nbusiness of a framework to hide this complexity from the user (developer in this case). Nevertheless its good when the\ndeveloper knows (and understands) what goes on under the hood and (even more important) can easily extend the framework\nwhere he needs to.",[439,91931,91932,91933,91938],{},"As I mentioned we use Spring / Spring MVC as a base for many projects. Spring provides a lot of points where you can\nextend the framework by implementing interfaces and defining or injecting these implementations to the classes that do\nthe real work (like interceptors\nin ",[1002,91934,91937],{"href":91935,"rel":91936},"http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.html",[1006],"AnnotationHandlerMapping",").\nIn most cases this is enough. If you start to develop a modular application it is not. At least it was not for our case.",[439,91940,91941,91942,91944,91945,91948,91949],{},"Our Web-Applications based on the mentioned Framework always use at least two “modules”: The core-module (that brings\nuser management and some nice features to make coders happy) and the application itself. Each of these modules brings\nits own bean-configurations which are loaded all together using a wildcard resource like the following that reads all\n",[471,91943,66393],{}," files within any subfolder of",[471,91946,91947],{},"META-INF"," in the applications classpath: ",[471,91950,91951],{},"classpath*:META-INF/**/beans.xml",[439,91953,91954],{},"This is a really simple way how the modules can interact since one module can provide a Service another depends on.",[439,91956,91957,91958,91963,91964,91966,91967,91970,91971,91973,91974,91976],{},"Now, the big problem is, that each module often brings its own components that have to be registered to Springs\n“services”. Let me explain the problem to you by talking\nabout",[1002,91959,91962],{"href":91960,"rel":91961},"http://blog.synyx.de/2010/04/21/know-your-apis-lessons-learned-from-resourcebundle/",[1006],"internationalization again",".\nEach module brings its own resource bundle containing the internationalization for its web-interface components. Spring\nprovides a simple way to register a",[471,91965,78129],{}," by defining one bean with id",[471,91968,91969],{},"messageSource"," in your context. And that\nis exactly the problem. It is",[448,91972,24777],{},"bean. So you need a way where each module can register its own",[471,91975,78129],{},", even\nif Spring only supports one. So our framework has to handle this, because it also introduces the modular structure.",[439,91978,91979,91980,91982],{},"The “out of the box” way that would work is that the application, that assembles all these modules together defines the\n",[471,91981,78129],{}," with all basenames (of the properties-files) the application uses. But this would be part of the\nmentioned complexity that should be kept away from the daily business and brings some other problems in (what, if one\nmodule wants to store its internationalization within the database?…).",[439,91984,91985,91986],{},"So what did we do? We use a Simple plugin-mechanism a colleague developed and Synyx publishes\nOpenSource:",[1002,91987,91990],{"href":91988,"rel":91989},"http://hera.synyx.org",[1006],"Hera",[439,91992,91993,91994,91996,91997,92000],{},"We use a bean that gets registered as",[471,91995,78129],{}," to Spring that takes care of dispatching the message-resolving\nrequests to the real ",[471,91998,91999],{},"MessageSources"," implementations spread all over the modules.",[464,92002,92004],{"className":16895,"code":92003,"language":16897,"meta":469,"style":469},"\u003Cbean id=\"messageSource\">\n \u003Cproperty name=\"sources\">\n \u003Cplugin:list class=\"org.synyx.minos.message.ModuleMessageSource\"/>\n \u003C/property>\n \u003Cproperty name=\"useCodeAsDefaultMessage\" value=\"true\" />\n\u003C/bean>\n",[471,92005,92006,92011,92016,92021,92025,92030],{"__ignoreMap":469},[474,92007,92008],{"class":476,"line":477},[474,92009,92010],{},"\u003Cbean id=\"messageSource\">\n",[474,92012,92013],{"class":476,"line":507},[474,92014,92015],{}," \u003Cproperty name=\"sources\">\n",[474,92017,92018],{"class":476,"line":547},[474,92019,92020],{}," \u003Cplugin:list class=\"org.synyx.minos.message.ModuleMessageSource\"/>\n",[474,92022,92023],{"class":476,"line":584},[474,92024,73244],{},[474,92026,92027],{"class":476,"line":607},[474,92028,92029],{}," \u003Cproperty name=\"useCodeAsDefaultMessage\" value=\"true\" />\n",[474,92031,92032],{"class":476,"line":642},[474,92033,69979],{},[439,92035,92036,92037,92040,92041,92044,92045,92047,92048,92050],{},"So this registers our",[471,92038,92039],{},"DispatchingMessageSource"," that gets injected into all beans within the context, implementing\n",[471,92042,92043],{},"ModuleMessageSource"," by Hera. This pretty much does the trick. The reason that we use",[471,92046,92043],{}," instead of\nSprings built-in",[471,92049,78129],{},"-interface is on the one hand so that we can do some performance-tweaks and on the\nother hand so that we dont get any “unwanted” implementations, which get to the context somehow.",[439,92052,92053,92054,92056],{},"With some simple dispatching logic within",[471,92055,92039],{}," we found a powerful way to conquer the insufficiency\nof Spring, in conjunction with our modular system.",[464,92058,92060],{"className":16895,"code":92059,"language":16897,"meta":469,"style":469},"List candidates = sources.getPluginsFor(getPrefixFromCode(code));\nfor (MessageSourcePlugin source : candidates) {\n MessageFormat format = resolveMessageWithSource(source, code, locale);\n if (null != format) {\n return format;\n }\n}\n",[471,92061,92062,92067,92072,92077,92082,92087,92091],{"__ignoreMap":469},[474,92063,92064],{"class":476,"line":477},[474,92065,92066],{},"List candidates = sources.getPluginsFor(getPrefixFromCode(code));\n",[474,92068,92069],{"class":476,"line":507},[474,92070,92071],{},"for (MessageSourcePlugin source : candidates) {\n",[474,92073,92074],{"class":476,"line":547},[474,92075,92076],{}," MessageFormat format = resolveMessageWithSource(source, code, locale);\n",[474,92078,92079],{"class":476,"line":584},[474,92080,92081],{}," if (null != format) {\n",[474,92083,92084],{"class":476,"line":607},[474,92085,92086],{}," return format;\n",[474,92088,92089],{"class":476,"line":642},[474,92090,1276],{},[474,92092,92093],{"class":476,"line":663},[474,92094,703],{},[439,92096,92097,92098,1489,92101,92104],{},"By the way, we use this mechanism a lot when it comes to easily extending functionality of the framework-core including\n",[471,92099,92100],{},"HandlerInterceptor",[471,92102,92103],{},"PropertyEditorRegistrar"," and our Modules itself.",[1024,92106,1405],{},{"title":469,"searchDepth":507,"depth":507,"links":92108},[],[1030],"2010-04-23T11:45:49","Many of the Web-Applications we develop for our customers are based upon our small Framework on top\\nof Spring / Spring MVC. This framework basically\\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\\nSpring already does.",{},"/blog/modular-web-applications-based-on-spring",{"title":91911,"description":92115},"Many of the Web-Applications we develop for our customers are based upon our small Framework on top\nof Spring / Spring MVC. This framework basically\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\nSpring already does.","blog/modular-web-applications-based-on-spring",[85763,85828,78171,92118,6505,1426],"modular","Many of the Web-Applications we develop for our customers are based upon our small Framework on top of Spring / Spring MVC. This framework basically brings often used components ready-to-use…","9CCujFfUkmvCk9K2-lTvameRh4QQAhnYiCvpeGTJeT8",{"id":92122,"title":92123,"author":92124,"body":92125,"category":92181,"date":92182,"description":92183,"extension":1034,"link":92184,"meta":92185,"navigation":916,"path":92186,"seo":92187,"slug":92129,"stem":92188,"tags":92189,"teaser":92191,"__hash__":92192},"blog/blog/release-of-synyxsudoku.md","Release of SynyxSudoku",[190],{"type":432,"value":92126,"toc":92179},[92127,92130,92133,92136,92139,92142,92145,92148,92155,92158],[435,92128,92123],{"id":92129},"release-of-synyxsudoku",[439,92131,92132],{},"We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all:\nIt’s completely free of charge !(except for your traffic costs, of course 🙂 )",[439,92134,92135],{},"SynyxSudoku offers 3 difficulty levels, containing nearly unlimited puzzling fun due to new created sudokus each time\nyou launch!",[439,92137,92138],{},"And even if none of these difficulty levels fits your needs – SynyxSudoku has the option to let you create a difficulty\nlevel of your own.",[439,92140,92141],{},"You can also save your best performances in solving the sudokus in the highscore and even upload it to our server to\ncompete with other sudoku players around the world to finally answer the question: “Who is the fastest sudoku-solver?”.",[439,92143,92144],{},"Need a little break during a game? No problem! Just pause, or even close it and you can continue the next time you\nstart. And if somebody calls you in midst of a game, it is automatically paused!",[439,92146,92147],{},"Download our game by searching for Synyx Sudoku on the android market or using this qr-code:",[439,92149,92150],{},[1002,92151,92153],{"href":90794,"rel":92152},[1006],[2205,92154],{"alt":469,"src":90952},[439,92156,92157],{},"If you are curious how SynyxSudoku looks like, here we have a few screenshots:",[439,92159,92160,92167,92173],{},[1002,92161,92164],{"href":92162,"rel":92163},"https://media.synyx.de/uploads//2010/04/sudoku_1.png",[1006],[2205,92165],{"alt":469,"src":92166},"https://media.synyx.de/uploads//2010/04/sudoku_1-180x300.png",[1002,92168,92171],{"href":92169,"rel":92170},"https://media.synyx.de/uploads//2010/04/sudoku_menu_en.png",[1006],[2205,92172],{"alt":469,"src":92169},[1002,92174,92177],{"href":92175,"rel":92176},"https://media.synyx.de/uploads//2010/04/sudoku_4_en.png",[1006],[2205,92178],{"alt":469,"src":92175},{"title":469,"searchDepth":507,"depth":507,"links":92180},[],[11122,3799],"2010-04-22T17:12:34","We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all:\\nIt’s completely free of charge !(except for your traffic costs, of course 🙂 )","https://synyx.de/blog/release-of-synyxsudoku/",{},"/blog/release-of-synyxsudoku",{"title":92123,"description":92132},"blog/release-of-synyxsudoku",[11132,92190,90966],"gaming","We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all: It’s completely free of charge ! (except for your traffic costs,…","ZM-tTLTIdY468RVspisE5Ne8WFJeDFqXpIWVefOUUtA",{"id":92194,"title":92195,"author":92196,"body":92197,"category":92248,"date":92249,"description":92250,"extension":1034,"link":92251,"meta":92252,"navigation":916,"path":92253,"seo":92254,"slug":92255,"stem":92256,"tags":92257,"teaser":92258,"__hash__":92259},"blog/blog/howto-startup-with-maemo-and-qt-4-6.md","Howto startup with Maemo and Qt 4.6 – Step1: Setup",[109],{"type":432,"value":92198,"toc":92246},[92199,92202,92205,92208,92211,92214,92217,92224,92231,92238],[435,92200,92195],{"id":92201},"howto-startup-with-maemo-and-qt-46-step1-setup",[439,92203,92204],{},"As a Java developer it was not easy for me to find the right entry point for developing c++, using the trend-setting\nQt 4.6 environment and having a cute ide with rapid prototyping capabilities. After a little bit of reading and lots of\ntrials and errors i found a way for me that worked.",[439,92206,92207],{},"First of all, if you use Kubuntu 10.04 like me, you have to edit your /etc/sysctl.conf and add the following line,\notherwise the maemo-sdk installer will fail to install:",[439,92209,92210],{},"`#scratbox maemo sdk fix",[439,92212,92213],{},"vm.mmap_min_addr = 0`",[439,92215,92216],{},"I installed the following tools in the given order:",[439,92218,92219,92220],{},"SDK: For the N900 Maemo device a scratchbox with additional Nokia binaries is freely available. I Installed scratchbox\nand the nokia sdk by following the instructions on\nmaemo.org: ",[1002,92221,92222],{"href":92222,"rel":92223},"http://wiki.maemo.org/Documentation/Maemo_5_Final_SDK_Installation",[1006],[439,92225,92226,92227],{},"IDE: esbox is a complete IDE based on Eclipse. With esbox you are able to develop, run your code in an “emulator” or on\nyour mobile device, and build complete .deb packages. The completely packaged archived did it for me. Download and unzip\nthe archive to any location that you like: ",[1002,92228,92229],{"href":92229,"rel":92230},"http://esbox.garage.maemo.org/2nd_edition/installation_product.html",[1006],[439,92232,92233,92234],{},"Qt IDE integration: a graphical ui designer is available. With Qt Designer Eclipse Integration you are able to drag and\ndrop Qt widgets to your ui and preview your masks. Although Qt Designer is not specially developed for Maemo, it does a\ngood job and you get good and fast results. Just download the archive and copy the content into the plugin directory of\nyour esbox\ninstallation : ",[1002,92235,92236],{"href":92236,"rel":92237},"http://qt.nokia.com/developer/eclipse-integration",[1006],[439,92239,37673,92240,92245],{},[1002,92241,92244],{"href":92242,"rel":92243},"http://mobile.synyx.de/2010/06/howto-startup-with-maemo-and-qt-4-6-%E2%80%93-step2-configure-ide/",[1006],"next post"," i will\ndescribe howto prepare your ide for Qt developing.",{"title":469,"searchDepth":507,"depth":507,"links":92247},[],[11122,1413],"2010-04-22T14:29:47","As a Java developer it was not easy for me to find the right entry point for developing c++, using the trend-setting\\nQt 4.6 environment and having a cute ide with rapid prototyping capabilities. After a little bit of reading and lots of\\ntrials and errors i found a way for me that worked.","https://synyx.de/blog/howto-startup-with-maemo-and-qt-4-6/",{},"/blog/howto-startup-with-maemo-and-qt-4-6",{"title":92195,"description":92204},"howto-startup-with-maemo-and-qt-4-6","blog/howto-startup-with-maemo-and-qt-4-6",[85548,89448],"As a Java developer it was not easy for me to find the right entry point for developing c++, using the trend-setting Qt 4.6 environment and having a cute ide…","jDT6Llv4Nx1vlsdPC2Q8Jn5pDBFQAkRE2f8V-pqh1Cw",{"id":92261,"title":92262,"author":92263,"body":92264,"category":92484,"date":92485,"description":92486,"extension":1034,"link":92487,"meta":92488,"navigation":916,"path":92489,"seo":92490,"slug":92268,"stem":92491,"tags":92492,"teaser":92494,"__hash__":92495},"blog/blog/know-your-apis-lessons-learned-from-resourcebundle.md","Know your APIs – Lessons learned from ResourceBundle",[166],{"type":432,"value":92265,"toc":92482},[92266,92269,92272,92279,92282,92302,92319,92345,92351,92364,92377,92392,92401,92427,92430,92462,92465],[435,92267,92262],{"id":92268},"know-your-apis-lessons-learned-from-resourcebundle",[439,92270,92271],{},"Last week I spent some time hunting down an internationalization-issue that came along while developing for a recent\nproject. Let me explain what happened:",[439,92273,92274,92275,92278],{},"Message-Lookup – of course – always stands together with Locales (",[471,92276,92277],{},"java.util.Locale",") of the client the message is\nresolved for. The problem was, that messages for the English users were not resolved to the English translation, but to\nthe German one.",[439,92280,92281],{},"Within the project I am working on, there were the following message-files at that time:",[994,92283,92284,92290,92296],{},[997,92285,92286,92289],{},[471,92287,92288],{},"messages.properties"," (containing the english translation)",[997,92291,92292,92295],{},[471,92293,92294],{},"messages_de.properties"," (containing the German translation)",[997,92297,92298,92301],{},[471,92299,92300],{},"messages_it.properties"," (containing Italian translation)",[439,92303,92304,92305,92308,92309,92312,92313,92315,92316,92318],{},"Usually you provide a property-file per language containing all the translations. ",[471,92306,92307],{},"ResourceBundle"," uses a\nfallback-mechanism from the full locale down to more general ones (e.g. it first checks for ",[471,92310,92311],{},"messages_de_DE.properties","\nto ",[471,92314,92294],{}," down to ",[471,92317,92288],{}," in the end, being the overall default).",[439,92320,92321,92322,92324,92325,92328,92329,18175,92332,92335,92336,92338,92339,92341,92342,92344],{},"This actually makes much sense because in this way you can provide values for stuff relevant for all languages in\n",[471,92323,92288],{},", English language specific values in ",[471,92326,92327],{},"messages_en.properties"," and stuff that is different for\ndifferent english speaking Countries in files like ",[471,92330,92331],{},"messages_en_US.properties",[471,92333,92334],{},"messages_en_UK.properties"," and so\non. The fallback works perfect, because if you for example don’t specify US/UK specific files message-lookup for both\nen_US and en_UK result in resolving keys from ",[471,92337,92327],{},". Additionally, if ",[471,92340,92331],{},"\nwould exist but the key-lookup fails (the file does not provide a translation for the key), the key gets also looked up\nfrom ",[471,92343,92327],{}," (which may also provide the key).",[439,92346,92347,92348,92350],{},"In my concrete case, resolving of German values (coming from ",[471,92349,92294],{},") worked well, but if you change\nthe users locale to English, the keys also resolved to German values.",[439,92352,92353,92354,92356,92357,92360,92361,92363],{},"I did not create a ",[471,92355,92327],{}," because English should also be the overall fallback if no i18n for the\nrequested language was available. I thought if the user had the locale fr the system would check for\n",[471,92358,92359],{},"messages_fr.properties"," and then in ",[471,92362,92288],{},", which would display English messages for the user because no\nFrench translation is available. I debugged a while, basically within our own framework and later down to Springs i18n\nrelated classes and I could not find the mistake.",[439,92365,92366,92367,92370,92371,92376],{},"Then, when I excluded all possible mistakes on our side and inside the Spring Framework where our application is based\non, my way lead me down to ",[471,92368,92369],{},"java.util.ResourceBundle",". Since this class makes extensive use of caching (for good reasons\nof course) and static factory methods (which are almost impossible to debug) my way lead me to the API-Doc\nof ",[1002,92372,92307],{"href":92373,"rel":92374,"title":92375},"http://api.synyx.de/j2sdk6/api/java/util/ResourceBundle.html",[1006],"API-Doc of java.util.ResourceBundle",".\nHere I found the mistake that I made:",[11947,92378,92379],{},[439,92380,92381,92384,92385,92388,92389,1402],{},[471,92382,92383],{},"getBundle"," uses the base name, the specified locale, and the default locale (obtained from ",[471,92386,92387],{},"Locale.getDefault",") to\ngenerate a sequence of ",[990,92390,92391],{},"candidate bundle names",[439,92393,92394,92395,92397,92398,92400],{},"I forgot that ",[471,92396,92307],{}," also looks up files for the JVMs Default-Locale before falling back to the base-file (\n",[471,92399,92288],{},"). The Default-Locale of my JVM is de_DE, which lead to the following path:",[994,92402,92403,92408,92412,92417,92422],{},[997,92404,92405,92407],{},[471,92406,92331],{}," (not found)",[997,92409,92410,92407],{},[471,92411,92327],{},[997,92413,92414,92416],{},[471,92415,92311],{}," (from Default-Locale, not found)",[997,92418,92419,92421],{},[471,92420,92294],{}," (from Default-Locale, FOUND)",[997,92423,1483,92424,92426],{},[471,92425,92288],{}," was not checked because the key was found)",[439,92428,92429],{},"So the fix was easy. There are even several ways to fix it:",[994,92431,92432,92440,92449,92456],{},[997,92433,92434,92435,23691,92437,92439],{},"rename ",[471,92436,92288],{},[471,92438,92327],{}," (which leads to the French dude having to learn German).",[997,92441,92442,92443,23691,92445,92448],{},"copy ",[471,92444,92288],{},[471,92446,92447],{},"message_en.properties"," (this is copy paste but this could be solved within the\nbuild-process using mvn)",[997,92450,92451,92452,92455],{},"set the default-locale of the JVM to an English one by calling ",[471,92453,92454],{},"Locale.setDefault(englishLocale)"," early in\napplication-boot",[997,92457,92458,92459],{},"set the default-locale as commandline-argument of your JVM (e.g.",[471,92460,92461],{},"-Duser.language=en -Duser.country=US",[439,92463,92464],{},"And, last but not least:What do we (or better I)learn from this?",[994,92466,92467,92470,92473],{},[997,92468,92469],{},"My usual approach “first think about whats happening, if you cannot figure out what leads to the problem immediately\nattach debugger” was obviously not the best approach in this case (although starting immediately to debug has turned\nout to be a very efficient way to hunt down bugs for me in general).",[997,92471,92472],{},"Code is not the only place where bugs can be found. A big problem is also understanding the basic APIs you use.",[997,92474,92475,92476,92478,92481],{},"Even if you use high-level APIs (i18n in this case with about 5 delegates before the ResourceBundle was actually\nreached) you still need to know the very basics.",[12024,92477],{},[448,92479,92480],{},"Reading APIs"," earlier (ok, the whole hunting only took about 1-2 hours, but still)or even (in a perfect world)\nknow all your APIs you use directly or indirectly would have saved some time.",{"title":469,"searchDepth":507,"depth":507,"links":92483},[],[1030],"2010-04-21T11:36:20","Last week I spent some time hunting down an internationalization-issue that came along while developing for a recent\\nproject. Let me explain what happened:","https://synyx.de/blog/know-your-apis-lessons-learned-from-resourcebundle/",{},"/blog/know-your-apis-lessons-learned-from-resourcebundle",{"title":92262,"description":92271},"blog/know-your-apis-lessons-learned-from-resourcebundle",[29262,78169,78171,92493],"resourcebundle","Last week I spent some time hunting down an internationalization-issue that came along while developing for a recent project. Let me explain what happened: Message-Lookup – of course – always…","kXv-V49VLTNQaYwmmCOhdlmLOL3IGkxpCAHfqpbUzrA",{"id":92497,"title":92498,"author":92499,"body":92500,"category":92516,"date":92517,"description":92507,"extension":1034,"link":92518,"meta":92519,"navigation":916,"path":92520,"seo":92521,"slug":92522,"stem":92523,"tags":92524,"teaser":92525,"__hash__":92526},"blog/blog/sudoku-on-android-2.md","Sudoku on Android",[190],{"type":432,"value":92501,"toc":92514},[92502,92505,92508,92511],[435,92503,92498],{"id":92504},"sudoku-on-android",[439,92506,92507],{},"Here’s a quick preview of our upcoming sudoku app on android:",[439,92509,92510],{},"[youtube S_wjWf7V660]",[439,92512,92513],{},"The layout has yet to be polished a little, but altogether the app is nearly finished.",{"title":469,"searchDepth":507,"depth":507,"links":92515},[],[11122,3799],"2010-04-15T18:49:51","https://synyx.de/blog/sudoku-on-android-2/",{},"/blog/sudoku-on-android-2",{"title":92498,"description":92507},"sudoku-on-android-2","blog/sudoku-on-android-2",[11132,92190],"Here’s a quick preview of our upcoming sudoku app on android: [youtube S_wjWf7V660] The layout has yet to be polished a little, but altogether the app is nearly finished.","PbRacMlWqMJ1mYo5-vtz_Ey_tm6HYGFYGS059gs6njk",{"id":92528,"title":79649,"author":92529,"body":92530,"category":92564,"date":92565,"description":92566,"extension":1034,"link":92567,"meta":92568,"navigation":916,"path":92569,"seo":92570,"slug":92534,"stem":92572,"tags":92573,"teaser":92575,"__hash__":92576},"blog/blog/welcome.md",[223],{"type":432,"value":92531,"toc":92562},[92532,92535,92554],[435,92533,79649],{"id":92534},"welcome",[439,92536,92537,92538,520,92543,25806,92548,92553],{},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s ",[1002,92539,92542],{"href":92540,"rel":92541},"http://android.org",[1006],"Google’s Android",[1002,92544,92547],{"href":92545,"rel":92546},"http://apple.com/iphone",[1006],"Apple’s iPhone",[1002,92549,92552],{"href":92550,"rel":92551},"http://www.meego.com/",[1006],"MeeGo"," we have something to say about it!",[439,92555,92556,92557,1402],{},"Stay tuned and subscribe to our ",[1002,92558,92561],{"href":92559,"rel":92560},"http://mobile.synyx.de/?feed=rss2",[1006],"RSS feed",{"title":469,"searchDepth":507,"depth":507,"links":92563},[],[11122],"2010-03-26T17:24:38","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\\nor MeeGo we have something to say about it!","https://synyx.de/blog/welcome/",{},"/blog/welcome",{"title":79649,"description":92571},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\nor MeeGo we have something to say about it!","blog/welcome",[11132,78398,92574,389],"meego","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the mobile space. No matter if it’s Google’s…","fxYe2l28A3Tr4AN4j9XfZ6v3dxy5234kvArmmK3JoB4",["Reactive",92578],{"$scookieConsent":92579,"$ssite-config":92581},{"functional":92580,"analytics":92580},false,{"_priority":92582,"env":44710,"name":92586,"url":18760},{"name":92583,"env":92584,"url":92585},-10,-15,0,"nuxt-app",["Set"],["ShallowReactive",92589],{"authors":-1,"navigation":-1},"/blog"]