\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",[50,6833,6834,6861,6888,6909,6940,6945,6954,6963,6967],{"__ignoreMap":48},[53,6835,6836,6839,6842,6845,6847,6850,6853,6855,6858],{"class":55,"line":56},[53,6837,6838],{"class":82},"\u003C",[53,6840,6744],{"class":6841},"s9eBZ",[53,6843,6844],{"class":59}," action",[53,6846,390],{"class":82},[53,6848,6849],{"class":63},"\"/heroes\"",[53,6851,6852],{"class":59}," method",[53,6854,390],{"class":82},[53,6856,6857],{"class":63},"\"post\"",[53,6859,6860],{"class":82},">\n",[53,6862,6863,6866,6869,6872,6874,6877,6880,6882,6885],{"class":55,"line":86},[53,6864,6865],{"class":82}," \u003C",[53,6867,6868],{"class":6841},"input",[53,6870,6871],{"class":59}," type",[53,6873,390],{"class":82},[53,6875,6876],{"class":63},"\"text\"",[53,6878,6879],{"class":59}," name",[53,6881,390],{"class":82},[53,6883,6884],{"class":63},"\"hero\"",[53,6886,6887],{"class":82}," />\n",[53,6889,6890,6892,6894,6896,6898,6900,6902,6904,6907],{"class":55,"line":126},[53,6891,6865],{"class":82},[53,6893,6868],{"class":6841},[53,6895,6871],{"class":59},[53,6897,390],{"class":82},[53,6899,6876],{"class":63},[53,6901,6879],{"class":59},[53,6903,390],{"class":82},[53,6905,6906],{"class":63},"\"superpower\"",[53,6908,6887],{"class":82},[53,6910,6911,6913,6915,6917,6919,6922,6925,6927,6930,6933,6935,6938],{"class":55,"line":163},[53,6912,6865],{"class":82},[53,6914,6821],{"class":6841},[53,6916,6871],{"class":59},[53,6918,390],{"class":82},[53,6920,6921],{"class":63},"\"submit\"",[53,6923,6924],{"class":59}," is",[53,6926,390],{"class":82},[53,6928,6929],{"class":63},"\"ajax-submit\"",[53,6931,6932],{"class":59}," data-target",[53,6934,390],{"class":82},[53,6936,6937],{"class":63},"\"hero-container\"",[53,6939,6860],{"class":82},[53,6941,6942],{"class":55,"line":186},[53,6943,6944],{"class":82}," add new item\n",[53,6946,6947,6950,6952],{"class":55,"line":221},[53,6948,6949],{"class":82}," \u003C/",[53,6951,6821],{"class":6841},[53,6953,6860],{"class":82},[53,6955,6956,6959,6961],{"class":55,"line":242},[53,6957,6958],{"class":82},"\u003C/",[53,6960,6744],{"class":6841},[53,6962,6860],{"class":82},[53,6964,6965],{"class":55,"line":273},[53,6966,500],{"emptyLinePlaceholder":499},[53,6968,6969,6971,6973,6976,6978,6980,6983,6985],{"class":55,"line":279},[53,6970,6838],{"class":82},[53,6972,6817],{"class":6841},[53,6974,6975],{"class":59}," id",[53,6977,390],{"class":82},[53,6979,6937],{"class":63},[53,6981,6982],{"class":82},">\u003C/",[53,6984,6817],{"class":6841},[53,6986,6860],{"class":82},[43,6988,6992],{"className":6989,"code":6990,"language":6991,"meta":48,"style":48},"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",[50,6993,6994,7010,7018,7051,7062,7080,7100,7105,7110,7114,7118,7133,7144],{"__ignoreMap":48},[53,6995,6996,6999,7002,7005,7008],{"class":55,"line":56},[53,6997,6998],{"class":389},"class",[53,7000,7001],{"class":59}," AjaxSubmitButton",[53,7003,7004],{"class":389}," extends",[53,7006,7007],{"class":59}," HTMLButtonElement",[53,7009,5795],{"class":82},[53,7011,7012,7015],{"class":55,"line":86},[53,7013,7014],{"class":59}," connectedCallback",[53,7016,7017],{"class":82},"() {\n",[53,7019,7020,7023,7025,7028,7030,7033,7035,7038,7041,7044,7046,7049],{"class":55,"line":126},[53,7021,7022],{"class":89}," this",[53,7024,986],{"class":82},[53,7026,7027],{"class":59},"addEventListener",[53,7029,1067],{"class":82},[53,7031,7032],{"class":63},"\"click\"",[53,7034,99],{"class":82},[53,7036,7037],{"class":389},"async",[53,7039,7040],{"class":82}," (",[53,7042,7043],{"class":5805},"event",[53,7045,1665],{"class":82},[53,7047,7048],{"class":389},"=>",[53,7050,5795],{"class":82},[53,7052,7053,7056,7059],{"class":55,"line":163},[53,7054,7055],{"class":82}," event.",[53,7057,7058],{"class":59},"preventDefault",[53,7060,7061],{"class":82},"();\n",[53,7063,7064,7067,7070,7072,7075,7078],{"class":55,"line":186},[53,7065,7066],{"class":389}," let",[53,7068,7069],{"class":82}," html ",[53,7071,390],{"class":389},[53,7073,7074],{"class":389}," await",[53,7076,7077],{"class":59}," ajaxFormSubmit",[53,7079,7061],{"class":82},[53,7081,7082,7085,7088,7090,7092,7095,7097],{"class":55,"line":221},[53,7083,7084],{"class":82}," document.",[53,7086,7087],{"class":59},"getElementById",[53,7089,1067],{"class":82},[53,7091,3609],{"class":89},[53,7093,7094],{"class":82},".dataset.target).innerHTML ",[53,7096,390],{"class":389},[53,7098,7099],{"class":82}," html;\n",[53,7101,7102],{"class":55,"line":242},[53,7103,7104],{"class":82}," });\n",[53,7106,7107],{"class":55,"line":273},[53,7108,7109],{"class":82}," }\n",[53,7111,7112],{"class":55,"line":279},[53,7113,282],{"class":82},[53,7115,7116],{"class":55,"line":496},[53,7117,500],{"emptyLinePlaceholder":499},[53,7119,7120,7123,7126,7128,7130],{"class":55,"line":503},[53,7121,7122],{"class":82},"customElements.",[53,7124,7125],{"class":59},"define",[53,7127,1067],{"class":82},[53,7129,6929],{"class":63},[53,7131,7132],{"class":82},", AjaxSubmitButton, {\n",[53,7134,7135,7138,7141],{"class":55,"line":509},[53,7136,7137],{"class":82}," extends: ",[53,7139,7140],{"class":63},"\"button\"",[53,7142,7143],{"class":82},",\n",[53,7145,7146],{"class":55,"line":515},[53,7147,7148],{"class":82},"});\n",[18,7150,7151,7152,7155],{},"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 ",[50,7153,7154],{},"AjaxSubmitButtons"," zu erstellen.",[18,7157,7158,7159,7162,7163,7168],{},"Kommen zur Laufzeit weitere ",[50,7160,7161],{},"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, ",[585,7164,7167],{"href":7165,"rel":7166},"https://developer.mozilla.org/de/docs/Glossary/Progressive_Enhancement",[589],"Progressive Enhancement"," genannt. HTML\nbeschreibt den Inhalt, CSS macht es bunt, und zu guter Letzt verbessern wir die Benutzererfahrung mit JavaScript.",[18,7170,7171],{},"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:",[43,7173,7175],{"className":288,"code":7174,"language":290,"meta":48,"style":48},"@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",[50,7176,7177,7182,7187,7192,7197,7202,7207,7212,7216,7221,7226,7230,7235,7240,7244,7248,7253],{"__ignoreMap":48},[53,7178,7179],{"class":55,"line":56},[53,7180,7181],{},"@PostMapping(value = \"/heroes\")\n",[53,7183,7184],{"class":55,"line":86},[53,7185,7186],{},"public String addSuperhero(\n",[53,7188,7189],{"class":55,"line":126},[53,7190,7191],{}," @RequestParam String hero,\n",[53,7193,7194],{"class":55,"line":163},[53,7195,7196],{}," @RequestParam String superpower,\n",[53,7198,7199],{"class":55,"line":186},[53,7200,7201],{}," @RequestHeader(name = \"X-Requested-With\", defaultValue = \"\") String requestedWith,\n",[53,7203,7204],{"class":55,"line":221},[53,7205,7206],{}," Model model\n",[53,7208,7209],{"class":55,"line":242},[53,7210,7211],{}," ) {\n",[53,7213,7214],{"class":55,"line":273},[53,7215,500],{"emptyLinePlaceholder":499},[53,7217,7218],{"class":55,"line":279},[53,7219,7220],{}," model.addAttribute(\"hero\", hero);\n",[53,7222,7223],{"class":55,"line":496},[53,7224,7225],{}," model.addAttribute(\"superpower\", superpower);\n",[53,7227,7228],{"class":55,"line":503},[53,7229,500],{"emptyLinePlaceholder":499},[53,7231,7232],{"class":55,"line":509},[53,7233,7234],{}," if (\"ajax\".equals(requestedWith)) {\n",[53,7236,7237],{"class":55,"line":515},[53,7238,7239],{}," return \"fragments/hero-fragment :: hero-fragment\";\n",[53,7241,7242],{"class":55,"line":521},[53,7243,860],{},[53,7245,7246],{"class":55,"line":527},[53,7247,500],{"emptyLinePlaceholder":499},[53,7249,7250],{"class":55,"line":533},[53,7251,7252],{}," return \"full-page-including-the-hero-fragment\";\n",[53,7254,7255],{"class":55,"line":539},[53,7256,282],{},[2352,7258,7260],{"id":7259},"auf-dem-weg-zur-single-page-application","Auf dem Weg zur Single Page Application",[18,7262,7263],{},"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.",[18,7265,7266],{},"Kurz gesagt: Wir bauen unser Frontend ohne modernes JavaScript Framework und sind (trotzdem) glücklich.",[18,7268,7269],{},"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.",[649,7271,7273],{"id":7272},"herausforderungen-die-kommen-könnten","Herausforderungen die kommen (könnten)",[577,7275,7276],{},[580,7277,7278],{},[27,7279,7280],{},"Progressive Enhancement Denkweise",[18,7282,7283],{},"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.",[577,7285,7286],{},[580,7287,7288],{},[27,7289,7290],{},"History Handling",[18,7292,7293,7294,7299],{},"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 ",[585,7295,7298],{"href":7296,"rel":7297},"https://developer.mozilla.org/en-US/docs/Web/API/History",[589],"history API"," ändern ist im Bereich des Möglichen 😉",[577,7301,7302],{},[580,7303,7304],{},[27,7305,7306],{},"State",[18,7308,7309,7310,7315],{},"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 ",[585,7311,7314],{"href":7312,"rel":7313},"https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent",[589],"Custom Events"," ausreichen.",[607,7317,7318],{},"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":48,"searchDepth":86,"depth":86,"links":7320},[7321,7322,7323,7324],{"id":6678,"depth":86,"text":6679},{"id":6721,"depth":86,"text":6722},{"id":6794,"depth":86,"text":6785},{"id":7259,"depth":86,"text":7260,"children":7325},[7326],{"id":7272,"depth":126,"text":7273},[613,614],"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":6617,"description":6626},"frameworkless-frontend-und-trotzdem-gluecklich","blog/frameworkless-frontend-und-trotzdem-gluecklich",[7337,6991,7338],"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":7342,"title":7343,"author":7344,"body":7345,"category":7425,"date":7426,"description":7427,"extension":617,"link":6526,"meta":7428,"navigation":499,"path":7429,"seo":7430,"slug":7349,"stem":7432,"tags":7433,"teaser":7436,"__hash__":7437},"blog/blog/bewerbung-und-schnuppertag-in-zeiten-von-social-distancing.md","Bewerbung und Schnuppertag in Zeiten von Social Distancing",[5752,5192],{"type":11,"value":7346,"toc":7423},[7347,7350,7358,7361,7364,7375,7378,7381,7384,7387,7390,7395,7400,7403,7406,7409,7412,7415,7418],[14,7348,7343],{"id":7349},"bewerbung-und-schnuppertag-in-zeiten-von-social-distancing",[18,7351,7352,7353,7357],{},"Grundsätzlich ändert sich am Ablaufprozedere wenig, Du sendest Deine Bewerbung per Mail\nan ",[585,7354,7356],{"href":7355},"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.",[18,7359,7360],{},"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.",[18,7362,7363],{},"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.",[18,7365,7366,7367,3839,7371,7374],{},"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 ",[585,7368,3449],{"href":7369,"rel":7370},"https://synyx.de/dein-arbeitsplatz/",[589],[585,7372,3449],{"href":3471,"rel":7373},[589]," informieren.",[18,7376,7377],{},"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.",[18,7379,7380],{},"Um Dir das im Rahmen der Gegebenheiten zu ermöglichen, haben wir uns ein Konzept für einen gelungenen remote\nSchnuppertag überlegt.",[18,7382,7383],{},"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.",[18,7385,7386],{},"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.",[18,7388,7389],{},"Damit Du möglichst viele verschiedene Teams und die unterschiedlichen Themenfelder kennenlernst, ist der Tag in mehrere\nZeitblöcke unterteilt. Wie kann das aussehen?",[18,7391,7392],{},[1773,7393],{"alt":48,"src":7394},"https://media.synyx.de/uploads/2020/06/Tagesablauf1-768x706.jpg",[18,7396,7397],{},[1773,7398],{"alt":48,"src":7399},"https://media.synyx.de/uploads/2020/03/White.jpg",[18,7401,7402],{},"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.",[18,7404,7405],{},"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.",[18,7407,7408],{},"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.",[18,7410,7411],{},"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.",[18,7413,7414],{},"So können wir für unsere Seite differenziert beurteilen wie gut Du zu synyx passt.",[18,7416,7417],{},"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 😉",[18,7419,7420,7421],{},"Hast Du Lust bekommen, uns kennenzulernen? Dann schreib uns an ",[585,7422,7356],{"href":7355},{"title":48,"searchDepth":86,"depth":86,"links":7424},[],[614],"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":7343,"description":7431},"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",[7434,5379,6611,7435],"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":7439,"title":7440,"author":7441,"body":7442,"category":7515,"date":7516,"description":7517,"extension":617,"link":5148,"meta":7518,"navigation":499,"path":7519,"seo":7520,"slug":7446,"stem":7521,"tags":7522,"teaser":7524,"__hash__":7525},"blog/blog/welt-passwort-tag.md","Welt-Passwort-Tag",[4957],{"type":11,"value":7443,"toc":7510},[7444,7447,7450,7453,7456,7459,7463,7466,7469,7472,7475,7479,7482,7485,7488,7491,7494,7498,7501,7504,7507],[14,7445,7440],{"id":7446},"welt-passwort-tag",[18,7448,7449],{},"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.",[18,7451,7452],{},"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.",[18,7454,7455],{},"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.",[18,7457,7458],{},"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.",[2352,7460,7462],{"id":7461},"doch-wie-sollte-dieses-passwort-aussehen","Doch wie sollte dieses Passwort aussehen?",[18,7464,7465],{},"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?",[18,7467,7468],{},"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.",[18,7470,7471],{},"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?",[18,7473,7474],{},"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.",[2352,7476,7478],{"id":7477},"was-muss-ein-passwort-also-noch-erfüllen-um-sicher-zu-sein","Was muss ein Passwort also noch erfüllen, um sicher zu sein?",[18,7480,7481],{},"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.",[18,7483,7484],{},"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.",[18,7486,7487],{},"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.",[18,7489,7490],{},"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.",[18,7492,7493],{},"Daher ist unsere Empfehlung zum World Password Day: Macht euch das Leben und Gewissen leichter und wechselt auf einen\nPasswortmanager!",[2352,7495,7497],{"id":7496},"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?",[18,7499,7500],{},"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?",[18,7502,7503],{},"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",[18,7505,7506],{},"(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.",[18,7508,7509],{},"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":48,"searchDepth":86,"depth":86,"links":7511},[7512,7513,7514],{"id":7461,"depth":86,"text":7462},{"id":7477,"depth":86,"text":7478},{"id":7496,"depth":86,"text":7497},[614],"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":7440,"description":7449},"blog/welt-passwort-tag",[5179,7523],"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.","rmFxh6KzFqsLt_sY2lK-hv1FlWlfLvacv_Q58fZuRH8",{"id":7527,"title":7528,"author":7529,"body":7530,"category":7551,"date":7552,"description":7553,"extension":617,"link":7554,"meta":7555,"navigation":499,"path":7556,"seo":7557,"slug":7534,"stem":7558,"tags":7559,"teaser":7560,"__hash__":7561},"blog/blog/synyx-als-partner-der-cyberwehr-bw.md","synyx als Partner der Cyberwehr BW",[4957],{"type":11,"value":7531,"toc":7549},[7532,7535,7538,7541,7544],[14,7533,7528],{"id":7534},"synyx-als-partner-der-cyberwehr-bw",[18,7536,7537],{},"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.",[18,7539,7540],{},"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.",[18,7542,7543],{},"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.",[18,7545,7546],{},[1773,7547],{"alt":48,"src":7548},"https://media.synyx.de/uploads/2020/05/Cyberwehr-768x384.jpg",{"title":48,"searchDepth":86,"depth":86,"links":7550},[],[614],"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":7528,"description":7537},"blog/synyx-als-partner-der-cyberwehr-bw",[5179],"Was tun, wenn die Firmen-Webseite gekapert, Server übernommen oder ganze Rechnerlandschaften lahmgelegt wurden und nun der Betrieb steht?","3ywRwFWb1KLfqXKnaYgLmNOs6hju9fV_OnEN8grnkZo",{"id":7563,"title":7564,"author":7565,"body":7566,"category":8034,"date":8035,"description":8036,"extension":617,"link":8037,"meta":8038,"navigation":499,"path":8039,"seo":8040,"slug":8041,"stem":8042,"tags":8043,"teaser":8046,"__hash__":8047},"blog/blog/frauen-in-der-it.md","synyx Girls’ Week: Frauen in der IT",[5192],{"type":11,"value":7567,"toc":8029},[7568,7571,7576,7587,7591,7600,7608,7616,7619,7623,7626,7634,7638,7641,7644,7650,7653,7658,7661,7666,7669,7674,7677,7682,7685,7690,7693,7699,7702,7706,7709,7712,7716,7719,7722,7725,7729,7732,7735,7739,7742,7747,7752,7755,7758,7761,7764,7767,7770,7773,7776,7782,7785,7789,7792,7795,7798,7801,7805,7808,7811,7815,7818,7821,7824,7827,7830,7833,7837,7840,7843,7846,7849,7852,7855,7858,7861,7864,7868,7871,7874,7877,7880,7883,7889,7892,7896,7899,7908,7912,7915,7919,7922,7926,7929,7935,7938,7942,7945,7949,7952,7955,7959,7962,7965,7969,7972,7978,7981,7985,7988,7992,7995,7998,8002,8005,8008,8012,8015,8018,8021,8026],[14,7569,7564],{"id":7570},"synyx-girls-week-frauen-in-der-it",[18,7572,7573],{},[573,7574,7575],{},"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.",[18,7577,7578],{},[573,7579,7580,7581,7586],{},"Der ",[585,7582,7585],{"href":7583,"rel":7584},"https://www.girls-day.de",[589],"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.",[2352,7588,7590],{"id":7589},"rolle-der-frauen-in-der-it","Rolle der Frauen in der IT",[18,7592,7593,7594,7599],{},"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. [",[585,7595,7598],{"href":7596,"rel":7597},"https://de.statista.com/infografik/13283/frauen-in-der-tech-branche/",[589],"1","]",[18,7601,7602,7603,7599],{},"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. [",[585,7604,7607],{"href":7605,"rel":7606},"https://www.frauen-informatik-geschichte.de/index.php-id=36.htm",[589],"2",[18,7609,7610,7611,7599],{},"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. [",[585,7612,7615],{"href":7613,"rel":7614},"https://www.frauen-informatik-geschichte.de/index.php-id=62.htm",[589],"3",[18,7617,7618],{},"Die Liste an Frauen, die wesentliche Grundsteine der Computertechnik gelegt haben, ließe sich noch weiter fortführen.",[2352,7620,7622],{"id":7621},"überholte-rollenbilder-übwerwinden","Überholte Rollenbilder übwerwinden",[18,7624,7625],{},"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.",[18,7627,7628,7629,7599],{},"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. [",[585,7630,7633],{"href":7631,"rel":7632},"https://reut.rs/2Qww5k5",[589],"4",[2352,7635,7637],{"id":7636},"mit-positiven-beispielen-voran","Mit positiven Beispielen voran",[18,7639,7640],{},"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.",[18,7642,7643],{},"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.",[18,7645,7646],{},[1773,7647],{"alt":7648,"src":7649},"Isabell Duerlich","https://media.synyx.de/uploads/2019/06/duerlich_ws-768x768.jpg",[18,7651,7652],{},"Isabell",[18,7654,7655],{},[27,7656,7657],{},"Seit wann arbeitest du bei synyx und was ist dein Aufgabenfeld?",[18,7659,7660],{},"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.",[18,7662,7663],{},[27,7664,7665],{},"Was hat dein Interesse geweckt, einen Beruf in der IT zu wählen?",[18,7667,7668],{},"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.",[18,7670,7671],{},[27,7672,7673],{},"Gibt es etwas, das du an deinem Beruf besonders magst?",[18,7675,7676],{},"Die Möglichkeit in viele spannende Bereiche einzutauchen und jeden Tag etwas Neues zu lernen.",[18,7678,7679],{},[27,7680,7681],{},"Wie war dein Werdegang?",[18,7683,7684],{},"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.",[18,7686,7687],{},[27,7688,7689],{},"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?",[18,7691,7692],{},"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.",[18,7694,7695],{},[1773,7696],{"alt":7697,"src":7698},"Julia Dasch","https://media.synyx.de/uploads/2019/06/julia_d_ws-768x768.jpg",[18,7700,7701],{},"Julia",[18,7703,7704],{},[27,7705,7657],{},[18,7707,7708],{},"Ich arbeite seit Oktober 2015 bei synyx und habe ein Duales Studium absolviert.",[18,7710,7711],{},"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.",[18,7713,7714],{},[27,7715,7665],{},[18,7717,7718],{},"Mein Interesse an der IT hat eine Aufgabe im Wirtschaftsgymnasium geweckt. Wir sollten",[18,7720,7721],{},"für das Schulfach Datenverarbeitung einen Steckbrief über uns in Form einer eigenen",[18,7723,7724],{},"Webseite erstellen. Mein damaliger Freund war auch Informatiker und hat mir zu der Zeit weitere Sachen im IT-Umfeld\ngezeigt.",[18,7726,7727],{},[27,7728,7673],{},[18,7730,7731],{},"Man versucht eine Lösung für ein Problem zu finden und macht damit die Anwender der",[18,7733,7734],{},"Software glücklicher.",[18,7736,7737],{},[27,7738,7681],{},[18,7740,7741],{},"Realschule, Wirtschaftsgymnasium, Duales Studium (Informatik)",[18,7743,7744],{},[27,7745,7746],{},"Möchtest du jungen Frauen oder speziell den Mädchen beim Girls‘ Day, die",[18,7748,7749],{},[27,7750,7751],{},"sich für technische Berufe interessieren, etwas mit auf den Weg geben?",[18,7753,7754],{},"Der Beruf ist sehr vielseitig, da es nicht nur einen Weg bereitstellt, sondern viele Möglichkeiten",[18,7756,7757],{},"und Bereiche bietet:",[18,7759,7760],{},"◦ Gestaltung von Benutzeroberflächen",[18,7762,7763],{},"◦ Benutzerführung",[18,7765,7766],{},"◦ Logik und Mathematik",[18,7768,7769],{},"◦ Architekturen einer Software",[18,7771,7772],{},"◦ Probleme, Bugs finden und beheben",[18,7774,7775],{},"Es gibt unheimlich viele Optionen sich persönlich weiter zu entwickeln.",[18,7777,7778],{},[1773,7779],{"alt":7780,"src":7781},"Khrystyna Kadyietska","https://media.synyx.de/uploads/2019/08/khrystyna_ws-768x768.jpg",[18,7783,7784],{},"Khrystyna",[18,7786,7787],{},[27,7788,7657],{},[18,7790,7791],{},"Ich mache seit 2018 bei synyx meine Ausbildung zur Fachinformatikerin im Bereich Anwendungsentwicklung.",[18,7793,7794],{},"Zur Zeit bin ich damit beschäftigt zu lernen, wie man eine App für Android programmiert.",[18,7796,7797],{},"Zwei mal pro Woche gehe ich in die Berufsschule und bereite mich auf",[18,7799,7800],{},"zukünftige Prüfungen vor.",[18,7802,7803],{},[27,7804,7665],{},[18,7806,7807],{},"Ich beschäftige mich gerne mit Aufgaben am PC. Auch als Kind war ich immer",[18,7809,7810],{},"neugierig, wie Handys oder Laptops funktionieren.",[18,7812,7813],{},[27,7814,7673],{},[18,7816,7817],{},"Natürlich, sehr viel. Ich mag, dass du als Softwareentwickler immer die",[18,7819,7820],{},"Möglichkeit hast, etwas Neues zu lernen. Es ist nie langweilig. Du arbeitest nicht nur",[18,7822,7823],{},"am PC und bist alleine. Es ist sehr wichtig sich zuerst mit Kunden und Kollegen zu besprechen,",[18,7825,7826],{},"wie ein Projekt am Ende fertig aussehen soll. Man muss im Team arbeiten,",[18,7828,7829],{},"auch voneinander lernen, und sehr viel kommunizieren, damit",[18,7831,7832],{},"der Kunde am Ende bekommt, was er will.",[18,7834,7835],{},[27,7836,7681],{},[18,7838,7839],{},"Ich komme aus der Ukraine, bin dort geboren und aufgewachsen. Habe dort",[18,7841,7842],{},"auch meinen Bachelor (Englischlehrerin, Deutsch als Nebenfach) gemacht. Da ich",[18,7844,7845],{},"in dieser Zeit an einem html/css-Kurs teil genommen habe, habe ich gemerkt,",[18,7847,7848],{},"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).",[18,7850,7851],{},"Nach dem ich fertig mit dem Studium war, bin ich nach Deutschland gekommen. Zuerst um",[18,7853,7854],{},"die Sprache besser zu lernen, und dann bin ich auf diese Ausbildung gestoßen. Ich habe",[18,7856,7857],{},"mich weiter informiert, was man braucht, um Fachinformatikerin zu werden.",[18,7859,7860],{},"Danach ging alles ganz schnell: Ausbildungsbetrieb finden (synyx), Bewerbung",[18,7862,7863],{},"schreiben, Vorstellungsgespräch nicht versauen 🙂 et voilà du bist Azubi.",[18,7865,7866],{},[27,7867,7689],{},[18,7869,7870],{},"Es gibt keine Frauen- oder Männer-Berufe. Für mich ist es wichtig, dass man",[18,7872,7873],{},"herausfindet, was langfristig Spaß macht. Und ich bin der Meinung, es ist nie zu",[18,7875,7876],{},"spät etwas Neues zu lernen und sich weiter zu entwickeln.",[18,7878,7879],{},"Ich bin noch am Anfang und werde weiter gerne neue Sachen lernen",[18,7881,7882],{},"und mehr Erfahrung sammeln.",[18,7884,7885],{},[1773,7886],{"alt":7887,"src":7888},"Aljona Buchloh","https://media.synyx.de/uploads/2019/08/aljona_ws-768x768.jpg",[18,7890,7891],{},"Aljona",[18,7893,7894],{},[27,7895,7657],{},[18,7897,7898],{},"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.",[18,7900,7901,7902,7907],{},"Schau doch auch mal in den",[585,7903,7906],{"href":7904,"rel":7905},"https://synyx.de/blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick/",[589],"Rückblick"," auf meine\nZeit bei synyx rein!",[18,7909,7910],{},[27,7911,7665],{},[18,7913,7914],{},"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.",[18,7916,7917],{},[27,7918,7673],{},[18,7920,7921],{},"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.",[18,7923,7924],{},[27,7925,7689],{},[18,7927,7928],{},"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.",[18,7930,7931],{},[1773,7932],{"alt":7933,"src":7934},"Petra Scherer","https://media.synyx.de/uploads/2019/06/lila_ws-768x768.jpg",[18,7936,7937],{},"Lila",[18,7939,7940],{},[27,7941,7657],{},[18,7943,7944],{},"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.",[18,7946,7947],{},[27,7948,7665],{},[18,7950,7951],{},"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.",[18,7953,7954],{},"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.",[18,7956,7957],{},[27,7958,7673],{},[18,7960,7961],{},"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.",[18,7963,7964],{},"Außerdem kann man immer etwas Neues lernen. Alles entwickelt sich und bleibt daher spannend.",[18,7966,7967],{},[27,7968,7689],{},[18,7970,7971],{},"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.",[18,7973,7974],{},[1773,7975],{"alt":7976,"src":7977},"Daniela Grammlich","https://media.synyx.de/uploads/2024/01/dani-2023-300x300.jpg",[18,7979,7980],{},"Dani",[18,7982,7983],{},[27,7984,7657],{},[18,7986,7987],{},"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.",[18,7989,7990],{},[27,7991,7665],{},[18,7993,7994],{},"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.",[18,7996,7997],{},"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.",[18,7999,8000],{},[27,8001,7673],{},[18,8003,8004],{},"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.",[18,8006,8007],{},"Außerdem mag ich dass ich von überall arbeiten kann, wenn ich das möchte.",[18,8009,8010],{},[27,8011,7681],{},[18,8013,8014],{},"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.",[18,8016,8017],{},"Dazwischen ein abgebrochenes Studium an der Uni in Informatik – ich bin einfach keine Theoretikerin.",[18,8019,8020],{},"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.",[18,8022,8023],{},[27,8024,8025],{},"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?",[18,8027,8028],{},"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":48,"searchDepth":86,"depth":86,"links":8030},[8031,8032,8033],{"id":7589,"depth":86,"text":7590},{"id":7621,"depth":86,"text":7622},{"id":7636,"depth":86,"text":7637},[614],"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":7564,"description":7575},"frauen-in-der-it","blog/frauen-in-der-it",[8044,8045],"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":8049,"title":8050,"author":8051,"body":8054,"category":8201,"date":8202,"description":8203,"extension":617,"link":8204,"meta":8205,"navigation":499,"path":8206,"seo":8207,"slug":8209,"stem":8210,"tags":8211,"teaser":8213,"__hash__":8214},"blog/blog/die-oop-2020-in-muenchen.md","Die OOP 2020 in München",[8052,8053],"mennerich","meseck",{"type":11,"value":8055,"toc":8195},[8056,8059,8066,8081,8085,8095,8099,8121,8125,8152,8155,8159,8182,8192],[14,8057,8050],{"id":8058},"die-oop-2020-in-münchen",[18,8060,8061,8062,8065],{},"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 ",[27,8063,8064],{},"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.",[18,8067,8068,8069,8072,8073,8080],{},"Von daher war es uns eine große Freude, im Rahmen des Tracks ",[27,8070,8071],{},"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",[573,8074,8075],{},[585,8076,8079],{"href":8077,"rel":8078},"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",[589],"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. 😉",[649,8082,8084],{"id":8083},"keynote-von-hannah-fry","Keynote von Hannah Fry",[18,8086,8087,8088],{},"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 ",[585,8089,8092],{"href":8090,"rel":8091},"https://www.chbeck.de/fry-hello-world/product/26909221",[589],[573,8093,8094],{},"Hello World – Being Humanin the Age of\nAlgorithms.",[649,8096,8098],{"id":8097},"session-von-gunter-dueck","Session von Gunter Dueck",[18,8100,8101,8102,8105,8106,8109,8110,8113,8114],{},"Gunter Dueck aka ",[573,8103,8104],{},"Wild Dueck","(@wilddueck) zuzuhören, ist immer ein Erlebnis und seit langem ist er fest im Programm der\nOOP verankert. Er sprach über ",[27,8107,8108],{},"Lähmungen im Angesicht des Unbekannten",", von Digitalisierung und der McDonaldisierung\ndes Alten. Den ",[27,8111,8112],{},"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 ",[585,8115,8118],{"href":8116,"rel":8117},"https://www.campus.de/buecher-campus-verlag/wirtschaft-gesellschaft/wirtschaft/heute_schon_einen_prozess_optimiert-15859.html",[589],[573,8119,8120],{},"Heute schon einen\nProzess optimiert?: Das Management frisst seine\nMitarbeiter.",[649,8122,8124],{"id":8123},"nachtschule-von-uns","Nachtschule von uns 😉",[18,8126,8127,8128,8131,8132,8135,8136,8143,8144,8151],{},"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 ",[27,8129,8130],{},"Konstruktivismus","und ",[27,8133,8134],{},"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",[573,8137,8138],{},[585,8139,8142],{"href":8140,"rel":8141},"https://www.informatik-aktuell.de/management-und-recht/projektmanagement/systemisch-agile-sofwareentwicklung.html",[589],"Informatik Aktuell","\nzu Gemüte führen, oder auch eine Aufnahme eines ähnlichen Vortrags von uns auf der ",[585,8145,8148],{"href":8146,"rel":8147},"https://www.youtube.com/watch?v=FAA31oVBrBI",[589],[573,8149,8150],{},"JUG\nKarlsruhe"," im Dezember 2019 ansehen.",[18,8153,8154],{},"Der Freitag war vertiefenden Workshops gewidmet.",[649,8156,8158],{"id":8157},"workshop-dave-farley","Workshop Dave Farley",[18,8160,8161,8162,8165,8166,8173,8174,8181],{},"Dave Farley von Continuous Delivery Ltd. gab einen Ganztagesworkshop zum Thema ",[27,8163,8164],{},"Continuous Delivery Advanced Deployment\nPipelines",". Als Mitautor des Buches ",[585,8167,8170],{"href":8168,"rel":8169},"https://www.amazon.de/Continuous-Delivery-Deployment-Automation-Addison-Wesley/dp/0321601912",[589],[573,8171,8172],{},"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 ",[585,8175,8178],{"href":8176,"rel":8177},"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",[589],[573,8179,8180],{},"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.",[18,8183,8184,8185],{},"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 ",[585,8186,8189],{"href":8187,"rel":8188},"https://online-training.jbrains.ca/p/wbitdd-01",[589],[573,8190,8191],{},"TDD Done\nRight.",[18,8193,8194],{},"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":48,"searchDepth":86,"depth":86,"links":8196},[8197,8198,8199,8200],{"id":8083,"depth":126,"text":8084},{"id":8097,"depth":126,"text":8098},{"id":8123,"depth":126,"text":8124},{"id":8157,"depth":126,"text":8158},[614],"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":8050,"description":8208},"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",[4221,3491,8212],"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":8216,"title":8217,"author":8218,"body":8221,"category":8311,"date":8312,"description":8313,"extension":617,"link":8314,"meta":8315,"navigation":499,"path":8316,"seo":8317,"slug":8225,"stem":8318,"tags":8319,"teaser":8322,"__hash__":8323},"blog/blog/kandddinsky-2019-in-berlin.md","KanDDDinsky 2019 in Berlin",[9,8219,8220],"heib","contargo_poetzsch",{"type":11,"value":8222,"toc":8309},[8223,8226,8229,8234,8241,8246,8249,8252,8255,8260,8274,8279,8282,8287,8290,8295,8298,8303,8306],[14,8224,8217],{"id":8225},"kandddinsky-2019-in-berlin",[18,8227,8228],{},"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.",[18,8230,8231],{},[27,8232,8233],{},"Workshop: Thomas Coopman — Event Storming",[18,8235,8236,8237,8240],{},"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 ",[573,8238,8239],{},"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.",[18,8242,8243],{},[27,8244,8245],{},"Workshop: Marco Heimeshoff, Roman Sachse — Why are words, how do they mean?",[18,8247,8248],{},"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.",[18,8250,8251],{},"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.",[18,8253,8254],{},"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.",[18,8256,8257],{},[27,8258,8259],{},"Vortrag: Roman Sachse — Is Maybe an Option",[18,8261,8262,8263,8266,8267,1628,8270,8273],{},"Im Vortrag “Is Maybe an Option” hat sich Roman Sachse an ein grundlegendes Problem gewagt, das in DDD meist\nstillschweigend umgangen wird: ",[573,8264,8265],{},"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",[573,8268,8269],{},"Option",[573,8271,8272],{},"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.",[18,8275,8276],{},[27,8277,8278],{},"Vortrag: Dennis Doomen — A practical introduction to DDD, CQRS…",[18,8280,8281],{},"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",[18,8283,8284],{},[27,8285,8286],{},"Vortrag: Michael Plöd — Pitching DDD to the management",[18,8288,8289],{},"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.",[18,8291,8292],{},[27,8293,8294],{},"Vortrag: Philipp Krenn — Building Distributeted Systems in Distributed Team",[18,8296,8297],{},"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.",[18,8299,8300],{},[27,8301,8302],{},"PARTY SESSION — So You Want to be a Rockstar Developer?",[18,8304,8305],{},"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.",[18,8307,8308],{},"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":48,"searchDepth":86,"depth":86,"links":8310},[],[613,3481,614],"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":8217,"description":8228},"blog/kandddinsky-2019-in-berlin",[8320,3491,3624,7337,8321,4231],"berlin","domain-driven-development","Erfahrungsbericht der KanDDDinsky Konferenz 2019","LkddH4oMqPym1394NkfmhIu5bWj7Mx9y4njBxfheu0A",{"id":8325,"title":8326,"author":8327,"body":8329,"category":8470,"date":8471,"description":8472,"extension":617,"link":8473,"meta":8474,"navigation":499,"path":8475,"seo":8476,"slug":8333,"stem":8478,"tags":8479,"teaser":8484,"__hash__":8485},"blog/blog/mein-erster-besuch-auf-dem-barcamp-pforzheim.md","Mein erster Besuch auf dem BarCamp Pforzheim",[8328],"buchloh",{"type":11,"value":8330,"toc":8460},[8331,8334,8343,8347,8354,8363,8367,8370,8373,8378,8383,8387,8390,8393,8397,8400,8403,8406,8409,8422,8426,8429,8433,8447,8451,8457],[14,8332,8326],{"id":8333},"mein-erster-besuch-auf-dem-barcamp-pforzheim",[18,8335,8336,8337,8342],{},"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 ",[585,8338,8341],{"href":8339,"rel":8340},"https://www.barcamp-pforzheim.de/",[589],"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.",[2352,8344,8346],{"id":8345},"was-ist-so-ein-barcamp-eigentlich","Was ist so ein BarCamp eigentlich?",[18,8348,8349,8350,8353],{},"Ein BarCamp hat kein festes Programm, sondern lebt von den Teilnehmenden. Zeitslots und Räume geben zwar einen Rahmen\nvor, aber ",[27,8351,8352],{},"wie"," diese gefüllt werden, hängt völlig von den Teilnehmenden ab.",[18,8355,8356,8357,8362],{},"Nach einem leckeren Frühstück (“Endlich Kaffee!”) zum ersten Eingrooven in\nder ",[585,8358,8361],{"href":8359,"rel":8360},"https://www.emma-pf.de/",[589],"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.",[649,8364,8366],{"id":8365},"und-dann-begann-auch-schon-die-sessionplanung-für-den-tag","Und dann begann auch schon die Sessionplanung für den Tag.",[18,8368,8369],{},"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.",[18,8371,8372],{},"Eine Session muss übrigens kein Frontalvortrag sein, sondern kann auch ein Mitmach-Workshop oder einfach eine lockere\nAustauschrunde zu einer bestimmten Fragestellung sein.",[18,8374,8375],{},[1773,8376],{"alt":48,"src":8377},"https://media.synyx.de/uploads/2019/10/barcamppf19sessions-768x576.jpeg",[18,8379,8380],{},[1773,8381],{"alt":48,"src":8382},"https://media.synyx.de/uploads/2019/10/barcamppf19d4k-768x576.jpeg",[649,8384,8386],{"id":8385},"ganz-schön-schwer-sich-zu-entscheiden","Ganz schön schwer, sich zu entscheiden.",[18,8388,8389],{},"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.",[18,8391,8392],{},"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.",[649,8394,8396],{"id":8395},"eine-session-jagt-die-nächste","Eine Session jagt die nächste.",[18,8398,8399],{},"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.",[18,8401,8402],{},"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.",[18,8404,8405],{},"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.",[18,8407,8408],{},"Schnell noch dem Sessionverantwortlichen ein Kompliment ausgesprochen und dann ab in die spannende Session “Systemisches\nKonsensieren”, die vorbeiging wie im Flug.",[18,8410,8411,8412,8415,8416,8421],{},"Zack, schon brach die letzte Session an – “",[573,8413,8414],{},"Oh, wir sind ja jetzt dran.","” – in der wir über\nunsere ",[585,8417,8420],{"href":8418,"rel":8419},"https://github.com/synyx/urlaubsverwaltung",[589],"Open Source Urlaubsverwaltung"," plauderten und uns wertvolle Gedanken\naußerhalb unserer Techie-Blase einholen konnten.",[649,8423,8425],{"id":8424},"die-session-nach-den-sessions","Die Session nach den Sessions.",[18,8427,8428],{},"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.",[649,8430,8432],{"id":8431},"noch-mehr-einblick","Noch mehr Einblick?",[18,8434,8435,8440,8441,8446],{},[585,8436,8439],{"href":8437,"rel":8438},"https://app.konferenz.guide/index.html?seite=sessionList&event=bcpf19",[589],"Hier"," findet sich der komplette Sessionplan der\nzwei Tage. Mehr Eindrücke und Photos lassen sich beispielsweise auf\ndem ",[585,8442,8445],{"href":8443,"rel":8444},"https://twitter.com/barcamppf",[589],"Twitter-Account des BarCamp Pforzheim"," finden.",[2352,8448,8450],{"id":8449},"nach-dem-barcamp-ist-vor-dem-barcamp","Nach dem BarCamp ist vor dem BarCamp.",[18,8452,8453,8454,986],{},"Der Termin für nächstes Jahr steht bereits fest: ",[27,8455,8456],{},"25. und 26. September 2020",[18,8458,8459],{},"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":48,"searchDepth":86,"depth":86,"links":8461},[8462,8469],{"id":8345,"depth":86,"text":8346,"children":8463},[8464,8465,8466,8467,8468],{"id":8365,"depth":126,"text":8366},{"id":8385,"depth":126,"text":8386},{"id":8395,"depth":126,"text":8396},{"id":8424,"depth":126,"text":8425},{"id":8431,"depth":126,"text":8432},{"id":8449,"depth":86,"text":8450},[3481,614],"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":8326,"description":8477},"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",[8480,8481,8482,8483],"barcamp","bcpf19","pforzheim","unkonferenz","Ein kleiner Erfahrungsbericht von meinem ersten Besuch auf dem BarCamp Pforzheim 2019 #bcpf19","8d_-lkp6gdHsdaEp7xXUQg8eNMU9SldwK0g7oJuimlM",{"id":8487,"title":8488,"author":8489,"body":8490,"category":8624,"date":8625,"description":8626,"extension":617,"link":7904,"meta":8627,"navigation":499,"path":8628,"seo":8629,"slug":8630,"stem":8631,"tags":8632,"teaser":8635,"__hash__":8636},"blog/blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick.md","8 Jahre synyx – Ein abwechslungsreicher Rückblick",[8328],{"type":11,"value":8491,"toc":8618},[8492,8495,8498,8503,8509,8511,8515,8518,8527,8538,8547,8550,8554,8557,8563,8569,8575,8578,8582,8585,8591,8596,8599,8602,8605,8609,8612],[14,8493,8488],{"id":8494},"_8-jahre-synyx-ein-abwechslungsreicher-rückblick",[18,8496,8497],{},"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.",[18,8499,8500],{},[1773,8501],{"alt":48,"src":8502},"https://media.synyx.de/uploads/2019/08/P1080729-768x576.jpg",[18,8504,8505],{},[1773,8506],{"alt":8507,"src":8508},"Altes Büro in der Karlstraße 68","https://media.synyx.de/uploads/2019/08/P1080060-768x576.jpg",[18,8510,8507],{},[2352,8512,8514],{"id":8513},"start-ins-abenteuer-ausbildung","Start ins Abenteuer Ausbildung",[18,8516,8517],{},"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.",[18,8519,8520,8521,8526],{},"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. (",[585,8522,8525],{"href":8523,"rel":8524},"https://synyx.de/blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts/",[589],"Zur Geschichte der Urlaubsverwaltung",")",[18,8528,8529,8530,8533,8534,8537],{},"Nach einigen Monaten eigenständiger Hackerei und erfolgreichem Live-Gang der Urlaubsverwaltung wurde es dann ernst für\nmich. Ich kam in ein ",[573,8531,8532],{},"richtiges"," Team und arbeitete plötzlich an einem ",[573,8535,8536],{},"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.",[18,8539,8540,8541,8546],{},"Gegen Ende meiner Ausbildung durfte ich in die dunkle äh andere Seite der IT-Welt eintauchen und einige Monate bei den\nAdmins verbringen. (",[585,8542,8545],{"href":8543,"rel":8544},"https://synyx.de/blog/devoooops-azubitausch-bei-synyx/",[589],"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.",[18,8548,8549],{},"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.",[2352,8551,8553],{"id":8552},"hallo-arbeitsleben","Hallo Arbeitsleben!",[18,8555,8556],{},"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.",[18,8558,8559,8560,8562],{},"Im Herbst 2014 war es bei synyx vorbei mit dem Flair der Altbauwohnung, denn der Umzug in ein ",[573,8561,8532],{}," 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 🙂",[18,8564,8565],{},[1773,8566],{"alt":8567,"src":8568},"raum_memory_leak","https://media.synyx.de/uploads/2019/08/IMG_4441-Kopie-768x512.jpg",[18,8570,8571],{},[1773,8572],{"alt":8573,"src":8574},"Blick ins synyx Wohnzimmer","https://media.synyx.de/uploads/2019/08/D9AYt99WkAYnKO0-768x512.jpeg",[18,8576,8577],{},"Impressionen aus dem aktuellen synyx Büro in der Gartenstraße 67",[2352,8579,8581],{"id":8580},"ein-neues-abenteuer-kommt-selten-allein","Ein neues Abenteuer kommt selten allein…",[18,8583,8584],{},"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.",[18,8586,8587],{},[1773,8588],{"alt":8589,"src":8590},"synyx_fruehstueck","https://media.synyx.de/uploads/2019/08/IMG_9055-768x512.jpg",[18,8592,8593],{},[1773,8594],{"alt":48,"src":8595},"https://media.synyx.de/uploads/2019/08/IMG_7865-768x512.jpg",[18,8597,8598],{},"Das synyx Frühstück – Hier geht's um mehr als Essen",[18,8600,8601],{},"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 🙂",[18,8603,8604],{},"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.",[2352,8606,8608],{"id":8607},"bock-mitzumachen","Bock mitzumachen?",[18,8610,8611],{},"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.",[18,8613,8614,8615,8617],{},"Du hast Bock, eigenverantwortlich an spannenden Projekten mit modernen Technologien mitzuwirken? Dann melde Dich bei uns\nunter ",[585,8616,7356],{"href":7355}," 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":48,"searchDepth":86,"depth":86,"links":8619},[8620,8621,8622,8623],{"id":8513,"depth":86,"text":8514},{"id":8552,"depth":86,"text":8553},{"id":8580,"depth":86,"text":8581},{"id":8607,"depth":86,"text":8608},[614],"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":8488,"description":8497},"8-jahre-synyx-ein-abwechslungsreicher-rueckblick","blog/8-jahre-synyx-ein-abwechslungsreicher-rueckblick",[5739,8633,8634],"jobs","rueckblick","Von wegen Eintönigkeit. 8 Jahre beim gleichen Unternehmen zu sein, kann ganz schön abwechslungsreich sein.","TSFTxvrimS0wxRsZIxYadrK86y5fBc7c12YlEh6FgZI",{"id":8638,"title":8639,"author":8640,"body":8642,"category":8767,"date":8768,"description":8649,"extension":617,"link":8769,"meta":8770,"navigation":499,"path":8771,"seo":8772,"slug":8646,"stem":8773,"tags":8774,"teaser":8776,"__hash__":8777},"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",[8641],"jayasinghe",{"type":11,"value":8643,"toc":8765},[8644,8647,8650,8676,8682,8691,8694,8697,8703,8706,8709,8712,8715,8718,8721,8727,8730,8736,8751,8754,8762],[14,8645,8639],{"id":8646},"ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete",[18,8648,8649],{},"Zuallererst möchte ich mich bedanken:",[577,8651,8652,8658,8664,8670],{},[580,8653,8654,8657],{},[27,8655,8656],{},"Bei der synyx:"," für ein modernes, eigenverantwortliches Weiterbildungskonzept, das es mir ermöglicht ohne große\nDiskussionen an so einer Veranstaltung teilzunehmen!",[580,8659,8660,8663],{},[27,8661,8662],{},"Bei den Disorganizern der JCrete",": dafür, dass ich dabei sein durfte!",[580,8665,8666,8669],{},[27,8667,8668],{},"Bei meinen Kindern",": weil sie so super mitgemacht haben und nicht verloren gegangen sind!",[580,8671,8672,8675],{},[27,8673,8674],{},"Bei meiner Frau:"," weil sie darauf vertraut hat, dass mir die Kinder nicht verloren gehen! 😉",[18,8677,8678],{},[1773,8679],{"alt":8680,"src":8681},"jCrete: Dinner mit Meerblick","https://media.synyx.de/uploads/2019/07/IMG_20190715_075755-768x576.jpg",[18,8683,8684,8685,8690],{},"Wenn man als Unterkunft eins der Zimmer in der OAC (",[585,8686,8689],{"href":8687,"rel":8688},"https://www.oac.gr/en/",[589],"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.",[18,8692,8693],{},"Ich habe diesmal Kinder dabei und schaue den Läufern vom Balkon aus zu. 😉",[18,8695,8696],{},"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.",[18,8698,8699],{},[1773,8700],{"alt":8701,"src":8702},"jCrete: Beach-Programm-Slot","https://media.synyx.de/uploads/2019/07/Image-from-iOS-768x576.jpg",[18,8704,8705],{},"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.",[18,8707,8708],{},"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.",[18,8710,8711],{},"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.",[18,8713,8714],{},"In der Pause wieder Kaffe, kurze Gespräche mit anderen Teilnehmern und Durchzählen der Kinder. 1 .. 2, alle noch da und\nnicht verhungert.",[18,8716,8717],{},"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!",[18,8719,8720],{},"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.",[18,8722,8723],{},[1773,8724],{"alt":8725,"src":8726},"Foodporn bei der jCrete","https://media.synyx.de/uploads/2019/07/IMG_20190718_130038-768x576.jpg",[18,8728,8729],{},"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.",[18,8731,8732],{},[1773,8733],{"alt":8734,"src":8735},"Foodtester Vivian war begeistert!","https://media.synyx.de/uploads/2019/07/IMG_20190715_202536-768x576.jpg",[18,8737,8738,8739,8744,8745,8750],{},"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",[585,8740,8743],{"href":8741,"rel":8742},"https://www.tripadvisor.com/Restaurant_Review-g1191160-d4605253-Reviews-Panorama_Tavern_Falasarna-Falassarna_Chania_Prefecture_Crete.html",[589],"Panorama Tavern in Falassarna","\nund\ndie ",[585,8746,8749],{"href":8747,"rel":8748},"https://www.tripadvisor.com/Restaurant_Review-g1028265-d12957049-Reviews-Fish_Tavern_1960-Kissamos_Chania_Prefecture_Crete.html",[589],"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.",[18,8752,8753],{},"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.",[18,8755,8756,8761],{},[585,8757,8760],{"href":8758,"rel":8759},"https://youtu.be/6-zF8JrWlt4",[589],"Video auf YouTube ansehen"," (Mit dem Laden des Videos akzeptieren Sie die\nDatenschutzerklärung von YouTube.)",[18,8763,8764],{},"Heinz und Kirk erklären, wie die JCrete geboren wurde und was diese Unconference so besonders macht.",{"title":48,"searchDepth":86,"depth":86,"links":8766},[],[8480,3481,614],"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":8639,"description":8649},"blog/ist-das-jetzt-urlaub-oder-arbeit-ein-typischer-tag-auf-der-jcrete",[8775],"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":8779,"title":8780,"author":8781,"body":8783,"category":8837,"date":8838,"description":8790,"extension":617,"link":8839,"meta":8840,"navigation":499,"path":8841,"seo":8842,"slug":8843,"stem":8844,"tags":8845,"teaser":8848,"__hash__":8849},"blog/blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen.md","Experiment JavaScript – Ein synyx Entwickler erzählt von seinen Anfängen",[8782],"lange",{"type":11,"value":8784,"toc":8832},[8785,8788,8791,8795,8802,8805,8808,8811,8815,8818,8821,8823,8826],[14,8786,8780],{"id":8787},"experiment-javascript-ein-synyx-entwickler-erzählt-von-seinen-anfängen",[18,8789,8790],{},"Alles ging vor knapp drei Jahren mit einer ganz harmlosen Frage los:",[2352,8792,8794],{"id":8793},"kannst-du-dir-vorstellen-in-einem-javascript-projekt-zu-arbeiten","„Kannst Du Dir vorstellen, in einem JavaScript-Projekt zu arbeiten?“",[18,8796,8797,8798,8801],{},"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 ",[573,8799,8800],{},"richtige"," Programmiersprache bezeichnet. Doch mein Interesse war geweckt und ich ließ mich auf das Experiment JavaScript ein.",[18,8803,8804],{},"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.",[18,8806,8807],{},"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.",[18,8809,8810],{},"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.",[2352,8812,8814],{"id":8813},"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?",[18,8816,8817],{},"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.",[18,8819,8820],{},"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.",[2352,8822,8608],{"id":8607},[18,8824,8825],{},"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.",[18,8827,8828,8829,8831],{},"Melde Dich einfach bei uns unter ",[585,8830,7356],{"href":7355}," 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":48,"searchDepth":86,"depth":86,"links":8833},[8834,8835,8836],{"id":8793,"depth":86,"text":8794},{"id":8813,"depth":86,"text":8814},{"id":8607,"depth":86,"text":8608},[614],"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":8780,"description":8790},"experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen","blog/experiment-javascript-ein-synyx-entwickler-erzaehlt-von-seinen-anfaengen",[5739,8846,8847,6991,8633],"developer","entwickler","Werdegang des JavaScript Developers Christian Lange bei synyx.","AqRd7fJ4-roSV9bB721fTkY674GmJh6n6gpoi-HeEUI",{"id":8851,"title":8852,"author":8853,"body":8854,"category":8983,"date":8984,"description":8985,"extension":617,"link":8986,"meta":8987,"navigation":499,"path":8988,"seo":8989,"slug":8991,"stem":8992,"tags":8993,"teaser":8994,"__hash__":8995},"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",[8328],{"type":11,"value":8855,"toc":8977},[8856,8859,8868,8873,8876,8882,8886,8889,8892,8895,8899,8906,8918,8921,8947,8953,8957,8960,8966,8968,8971],[14,8857,8852],{"id":8858},"von-schlipsträgern-raketenwissenschaften-und-einhörnern-die-geschichte-eines-entwicklers-bei-synyx",[18,8860,8861,8862,8867],{},"Ein dickes Grinsen steht dem Vorlesenden ins Gesicht, als er zum Abschluss des Dailys den Spruch des Tages aus\ndem ",[585,8863,8866],{"href":8864,"rel":8865},"http://www.der-falsche-kalender.de/",[589],"Falschen Kalender"," von Marc-Uwe Kling präsentiert:",[5221,8869,8870],{},[18,8871,8872],{},"“Glück ist ganz einfach. Gute Gesundheit und ein schlechtes Gedächtnis.\" — Dorie",[18,8874,8875],{},"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.",[18,8877,8878],{},[1773,8879],{"alt":8880,"src":8881},"Seb am Arbeiten","https://media.synyx.de/uploads/2019/02/seb1-768x512.jpg",[2352,8883,8885],{"id":8884},"von-schlipsträgern-und-defekten-live-cds","Von Schlipsträgern und defekten Live-CDs",[18,8887,8888],{},"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.",[18,8890,8891],{},"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.",[18,8893,8894],{},"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.",[2352,8896,8898],{"id":8897},"von-hölzernen-usb-sticks-und-raketenwissenschaften","Von hölzernen USB-Sticks und Raketenwissenschaften",[18,8900,8901,8902,8905],{},"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 ",[585,8903,3646],{"href":3644,"rel":8904},[589]," begann.",[18,8907,8908,8911,8912,8917],{},[573,8909,8910],{},"“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 ",[585,8913,8916],{"href":8914,"rel":8915},"https://synyx.de/was/codeclinic",[589],"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.",[18,8919,8920],{},"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.",[18,8922,8923,8924,8927,8928,8931,8932,8937,8938,8943,8944],{},"Team Rocket ist zuhause in einer verteilten System-Landschaft. Manch einer würde hier vielleicht den Begriff\n",[573,8925,8926],{},"“Microservices”"," raushauen. Doch da das einen gewissen Beigeschmack von ",[573,8929,8930],{},"“Wir wollen voll hipp und trendy wirken”","\nerregen könnte, spricht der Seb lieber von vielfältigen Projekten, meist basierend\nauf ",[585,8933,8936],{"href":8934,"rel":8935},"http://spring.io/projects/spring-boot",[589],"Spring Boot",", die über ",[585,8939,8942],{"href":8940,"rel":8941},"https://www.amqp.org/",[589],"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: ",[573,8945,8946],{},"“Das war mal wieder ein Schuss in den Ofen!”",[18,8948,8949],{},[1773,8950],{"alt":8951,"src":8952},"Kalender zitat: Um Jahre voraus","https://media.synyx.de/uploads/2019/02/seb2-768x512.jpg",[2352,8954,8956],{"id":8955},"von-code-monkeys-und-einhörnern","Von Code Monkeys und Einhörnern",[18,8958,8959],{},"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.",[18,8961,8962,8965],{},[573,8963,8964],{},"“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.",[2352,8967,8608],{"id":8607},[18,8969,8970],{},"Du ziehst Techies aus Leidenschaft Schlipsträgern vor und hast Bock, eigenverantwortlich an spannenden Projekten mit\nmodernen Technologien mitzuwirken?",[18,8972,8973,8974,8976],{},"Dann melde Dich bei uns unter ",[585,8975,7356],{"href":7355}," 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":48,"searchDepth":86,"depth":86,"links":8978},[8979,8980,8981,8982],{"id":8884,"depth":86,"text":8885},{"id":8897,"depth":86,"text":8898},{"id":8955,"depth":86,"text":8956},{"id":8607,"depth":86,"text":8608},[614],"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":8852,"description":8990},"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",[5739,8846,8847,8633],"Ein ganz normaler Morgen. Ein ganz normales Daily. Die versammelte Mannschaft.","OEqZ98Gd5nLrPjyxiPm6Kxbmyf8khdQ3bhmWOwdZYUc",{"id":8997,"title":8998,"author":8999,"body":9000,"category":9133,"date":9134,"description":48,"extension":617,"link":9135,"meta":9136,"navigation":499,"path":9137,"seo":9138,"slug":9139,"stem":9140,"tags":9141,"teaser":9145,"__hash__":9146},"blog/blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews.md","Nachfragen statt urteilen: 4 Gedankenanstöße für effektive Code Reviews",[8328],{"type":11,"value":9001,"toc":9126},[9002,9005,9010,9013,9016,9019,9023,9028,9031,9036,9039,9042,9046,9050,9053,9057,9060,9063,9067,9071,9074,9078,9081,9084,9087,9091,9095,9098,9102,9105,9108,9112,9115,9118,9123],[14,9003,8998],{"id":9004},"nachfragen-statt-urteilen-4-gedankenanstöße-für-effektive-code-reviews",[5221,9006,9007],{},[18,9008,9009],{},"My code is guaranteed 100% mistrake free.",[18,9011,9012],{},"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.",[18,9014,9015],{},"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.",[18,9017,9018],{},"Fernab von Technik und Tools möchte ich im Folgenden vier Gedankenanstöße für effektive Code Reviews liefern.",[2352,9020,9022],{"id":9021},"_1-eine-bewusste-entscheidung","1. Eine bewusste Entscheidung?",[18,9024,9025],{},[27,9026,9027],{},"Gut:",[18,9029,9030],{},"“Diese Variable sollte nicht public sein.”",[18,9032,9033],{},[27,9034,9035],{},"Besser:",[18,9037,9038],{},"“Wieso hast du dich dafür entschieden, diese Variable public zu machen?”",[18,9040,9041],{},"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.",[2352,9043,9045],{"id":9044},"_2-zu-kurz-gedacht","2. Zu kurz gedacht?",[18,9047,9048],{},[27,9049,9027],{},[18,9051,9052],{},"“Das funktioniert so nicht.”",[18,9054,9055,2304],{},[27,9056,9035],{},[18,9058,9059],{},"“Wieso hast du das Problem auf genau diese Weise gelöst und nicht anders? Hast du Edge Case XY eigentlich bedacht?”",[18,9061,9062],{},"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.",[2352,9064,9066],{"id":9065},"_3-wie-soll-ein-junior-einem-senior-schon-helfen","3. Wie soll ein Junior einem Senior schon helfen?",[18,9068,9069],{},[27,9070,9027],{},[18,9072,9073],{},"“Puh, das verstehe ich jetzt nicht wirklich. Naja, wird schon passen.”",[18,9075,9076],{},[27,9077,9035],{},[18,9079,9080],{},"“Ich verstehe diese Stelle im Code nicht so ganz. Kannst du mir das genauer erklären?”",[18,9082,9083],{},"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.",[18,9085,9086],{},"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.",[2352,9088,9090],{"id":9089},"_4-über-geschmack-lässt-sich-nicht-streiten","4. Über Geschmack lässt sich (nicht) streiten?",[18,9092,9093],{},[27,9094,9027],{},[18,9096,9097],{},"“Ich kann While-Schleifen nicht ausstehen.”",[18,9099,9100],{},[27,9101,9035],{},[18,9103,9104],{},"“Wieso hast du hier eine While-Schleife benutzt und keine For-Schleife?”",[18,9106,9107],{},"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.",[2352,9109,9111],{"id":9110},"nachfragen-statt-urteilen","Nachfragen statt urteilen!",[18,9113,9114],{},"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.",[18,9116,9117],{},"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.",[18,9119,9120],{},[27,9121,9122],{},"Vielleicht ist ja alles ganz anders, als du denkst 😉",[18,9124,9125],{},"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":48,"searchDepth":86,"depth":86,"links":9127},[9128,9129,9130,9131,9132],{"id":9021,"depth":86,"text":9022},{"id":9044,"depth":86,"text":9045},{"id":9065,"depth":86,"text":9066},{"id":9089,"depth":86,"text":9090},{"id":9110,"depth":86,"text":9111},[614],"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":8998,"description":48},"nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews","blog/nachfragen-statt-urteilen-4-gedankenanstoesse-fuer-effektive-code-reviews",[9142,6608,9143,9144],"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":9148,"title":9149,"author":9150,"body":9152,"category":9490,"date":9491,"description":9492,"extension":617,"link":9493,"meta":9494,"navigation":499,"path":9495,"seo":9496,"slug":9156,"stem":9498,"tags":9499,"teaser":9501,"__hash__":9502},"blog/blog/konferenz-logbuch-bedcon-2018.md","Konferenz-Logbuch: BEDCon 2018",[9151],"dfuchs",{"type":11,"value":9153,"toc":9480},[9154,9157,9178,9184,9187,9190,9196,9199,9202,9208,9210,9214,9218,9224,9226,9229,9233,9255,9259,9274,9280,9283,9287,9298,9302,9305,9309,9328,9331,9342,9346,9360,9363,9383,9392,9396,9408,9411,9422,9425,9428,9439,9443,9446,9449,9457,9460,9462,9471,9474],[14,9155,9149],{"id":9156},"konferenz-logbuch-bedcon-2018",[18,9158,9159,9160,9165,9166,9171,9172,9177],{},"Bei der diesjährigen ",[585,9161,9164],{"href":9162,"rel":9163},"http://www.bed-con.org/2018/",[589],"BEDCon"," ist synyx gleich doppelt vertreten gewesen. Nicht nur der\nTalk “Observability in einer Microservice Welt” von Andreas Weigel (",[585,9167,9170],{"href":9168,"rel":9169},"https://twitter.com/aendi",[589],"@aendi",") und Jakob\nFels (",[585,9173,9176],{"href":9174,"rel":9175},"https://twitter.com/JakobFels",[589],"@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.",[18,9179,9180],{},[1773,9181],{"alt":9182,"src":9183},"Thomas Kraft am Bedcon Stand","https://media.synyx.de/uploads/2019/04/bedcon_stand-768x865.png",[18,9185,9186],{},"Zwar nicht der größte Stand, dafür aber komplett in der Bahn transportiert.",[18,9188,9189],{},"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.",[18,9191,9192],{},[1773,9193],{"alt":9194,"src":9195},"Getting started Bedcon","https://media.synyx.de/uploads/2018/09/bedcon_getting-started-768x520.jpg",[18,9197,9198],{},"Gut vorbereitet aber was fehlt?",[18,9200,9201],{},"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.",[18,9203,9204],{},[1773,9205],{"alt":9206,"src":9207},"Feilen bis zur letzen Minute","https://media.synyx.de/uploads/2018/09/bedcon_wip-768x531.jpg",[18,9209,9206],{},[2352,9211,9213],{"id":9212},"talks","Talks",[649,9215,9217],{"id":9216},"observability-in-einer-microservice-welt","Observability in einer Microservice Welt",[18,9219,9220],{},[1773,9221],{"alt":9222,"src":9223},"#somethingwithbananas","https://media.synyx.de/uploads/2018/09/bedcon_banane-768x768.png",[18,9225,9222],{},[18,9227,9228],{},"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.",[1217,9230,9232],{"id":9231},"kernaussagen","Kernaussagen",[577,9234,9235,9238,9252],{},[580,9236,9237],{},"Unterscheidung zwischen verschiedenen Sichten auf verteilte Systeme mit Logging, Metriken und Tracing",[580,9239,9240,9241],{},"Livedemo mit entsprechenden Umsetzungen:\n",[577,9242,9243,9246,9249],{},[580,9244,9245],{},"Logging via Logstash in Elastic Search und Visualisierung mit Kibana",[580,9247,9248],{},"Metriken mit dem Micrometer Framework als Metrikenfassade und Influx und Grafana zur Speicherung und\nVisualisierung",[580,9250,9251],{},"Tracing mit Spring Cloud Sleuth und Zipkin",[580,9253,9254],{},"DevOps-Kulturwandel bringt Chancen zur Verbesserung der Zusammenarbeit: Weg von Schuldzuweisungen hin zu\nverantwortungsbewussteren Handeln.",[649,9256,9258],{"id":9257},"offline-first-kein-netz-kein-fehler-zufriedene-nutzer","Offline First – kein Netz, kein Fehler, zufriedene Nutzer",[18,9260,9261,9262,9267,9268,9273],{},"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 ",[585,9263,9266],{"href":9264,"rel":9265},"https://de.slideshare.net/devday-dd/devday-2018-ulrich-deiters-offline-first-kein-netz-kein-fehler-zufriedene-nutzer",[589],"“Offline First – kein Netz, kein Fehler, zufriedene Nutzer”","\nvon ",[585,9269,9272],{"href":9270,"rel":9271},"https://twitter.com/ulid000",[589],"Ulrich Deiters",", der damit viele wichtige Grundsätze und Denkanstöße lieferte.",[18,9275,9276],{},[1773,9277],{"alt":9278,"src":9279},"Disconnected-Dino","https://media.synyx.de/uploads/2018/09/offline-dino.png",[18,9281,9282],{},"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.",[1217,9284,9286],{"id":9285},"ansätze-zum-handeln","Ansätze zum Handeln",[577,9288,9289,9292,9295],{},[580,9290,9291],{},"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.",[580,9293,9294],{},"Klare Aussage über den aktuellen Zustand der Netzwerkverbindung treffen, nicht unbedingt nur wenn der Nutzer offline\nist.",[580,9296,9297],{},"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.",[1217,9299,9301],{"id":9300},"das-credo","Das Credo",[18,9303,9304],{},"“Kein Netz” ist nicht zwingend ein Fehler!",[649,9306,9308],{"id":9307},"self-contained-integrationstests-mit-docker-und-testcontainers","Self-Contained Integrationstests mit Docker und Testcontainers",[18,9310,9311,9312,9267,9317,9322,9323,9327],{},"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 ",[585,9313,9316],{"href":9314,"rel":9315},"https://www.innoq.com/en/talks/2018/09/self-contained-integrationstests-mit-docker-und-testcontainers/",[589],"“Self-Contained Integrationstests mit Docker und Testcontainers”",[585,9318,9321],{"href":9319,"rel":9320},"https://twitter.com/michaelvitz",[589],"Michael Vitz"," vorgestellten ",[585,9324,757],{"href":9325,"rel":9326},"http://testcontainers.org",[589]," ein\nsinnvoller Ansatz um die Herausforderung zu meistern.",[18,9329,9330],{},"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:",[577,9332,9333,9336,9339],{},[580,9334,9335],{},"mysql, postgresql oder oracle-xe",[580,9337,9338],{},"selenium",[580,9340,9341],{},"nginx",[649,9343,9345],{"id":9344},"test-the-architecture","Test the Architecture",[18,9347,9348,9349,9267,9354,9359],{},"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 ",[585,9350,9353],{"href":9351,"rel":9352},"https://speakerdeck.com/dennisrippinger/test-the-architecture",[589],"“Test the Architecture”",[585,9355,9358],{"href":9356,"rel":9357},"https://twitter.com/dennisrippinger",[589],"Dennis Rippinger"," an und zeigt Möglichkeiten wie man getroffene Entscheidungen\nnachhaltig automatisiert festzuhalten.",[18,9361,9362],{},"Während des Talks wurden anhand von Code Beispielen verschiedene Tools vorgestellt die Architekturen in Regeln und Tests\ngießen lassen:",[577,9364,9365,9371,9377],{},[580,9366,9367,9370],{},[27,9368,9369],{},"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.",[580,9372,9373,9376],{},[27,9374,9375],{},"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.",[580,9378,9379,9382],{},[27,9380,9381],{},"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.",[18,9384,9385,9386,9391],{},"Danke für die vielen Beispiele die dazu auf ",[585,9387,9390],{"href":9388,"rel":9389},"https://github.com/DennisRippinger/test-the-architecture",[589],"GitHub"," zu finden\nsind.",[649,9393,9395],{"id":9394},"wie-werde-ich-ein-erfolgreicher-software-architekt","Wie werde ich ein erfolgreicher Software-Architekt?",[18,9397,9398,9399,9267,9404,9407],{},"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 ",[585,9400,9403],{"href":9401,"rel":9402},"https://www.innoq.com/de/talks/2018/09/wie-werde-ich-ein-erfolgreicher-software-architekt-bedcon/",[589],"“Wie werde ich ein erfolgreicher Software-Architekt?”",[585,9405,3410],{"href":3408,"rel":9406},[589],", in dem die Rolle des klassischen Softwarearchitekten mit einer\nmoderneren Interpretation anhand von unterschiedlichen Aspekten vorgestellt wurde.",[18,9409,9410],{},"Generische Architekturaspekte wie",[577,9412,9413,9416,9419],{},[580,9414,9415],{},"Saubere Architektur und hohe Code Qualität",[580,9417,9418],{},"Skalierbarkeit",[580,9420,9421],{},"Favorisiertes Framework/Sprache",[18,9423,9424],{},"lösen generell erstmal keine Probleme.",[18,9426,9427],{},"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:",[577,9429,9430,9433,9436],{},[580,9431,9432],{},"Keiner kann alleine gewinnen",[580,9434,9435],{},"Jeder übernimmt eine gewisse Rolle",[580,9437,9438],{},"Gute Kommunikation ist eine sehr wichtige Komponente",[1217,9440,9442],{"id":9441},"das-fazit-des-talks","Das Fazit des Talks",[18,9444,9445],{},"“Aufgabe eines Architekten ist es kontinuierlich Entscheidungen ohne genug Informationen zu treffen”",[18,9447,9448],{},"Dabei gilt:",[577,9450,9451,9454],{},[580,9452,9453],{},"Die besten Entscheidungen sind solche die man später treffen kann",[580,9455,9456],{},"Oft können Entscheidungen nur auf Grundlage von wenig oder unzureichender Informationslage getroffen werden und das\nist auch OK so.",[18,9458,9459],{},"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.",[2352,9461,969],{"id":968},[18,9463,9464,9465,9470],{},"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 ",[585,9466,9469],{"href":9467,"rel":9468},"https://twitter.com/aendi/status/1037749829622005760",[589],"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 😉",[18,9472,9473],{},"In diesem Sinne:",[18,9475,9476],{},[1773,9477],{"alt":9478,"src":9479},"Code-with-Attitude-Bier","https://media.synyx.de/uploads/2018/09/bedcon_beerwithattitude-768x1121.jpg",{"title":48,"searchDepth":86,"depth":86,"links":9481},[9482,9489],{"id":9212,"depth":86,"text":9213,"children":9483},[9484,9485,9486,9487,9488],{"id":9216,"depth":126,"text":9217},{"id":9257,"depth":126,"text":9258},{"id":9307,"depth":126,"text":9308},{"id":9344,"depth":126,"text":9345},{"id":9394,"depth":126,"text":9395},{"id":968,"depth":86,"text":969},[614],"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":9149,"description":9497},"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",[9500,8320,3491,4231],"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":9504,"title":9505,"author":9506,"body":9508,"category":9675,"date":9676,"description":9677,"extension":617,"link":9678,"meta":9679,"navigation":499,"path":9680,"seo":9681,"slug":9512,"stem":9682,"tags":9683,"teaser":9687,"__hash__":9688},"blog/blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like.md","Breakout Session – how to prototype your enterprise project hackathon-like",[9507],"franke",{"type":11,"value":9509,"toc":9668},[9510,9513,9516,9522,9526,9533,9536,9540,9543,9546,9549,9552,9558,9561,9564,9567,9573,9577,9580,9586,9589,9592,9595,9598,9601,9604,9610,9614,9617,9637,9643,9648,9651,9654,9658,9665],[14,9511,9505],{"id":9512},"breakout-session-how-to-prototype-your-enterprise-project-hackathon-like",[18,9514,9515],{},"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.",[18,9517,9518],{},[1773,9519],{"alt":9520,"src":9521},"Logo Hack Your Office","https://media.synyx.de/uploads/2019/04/HACKYOUROFFICE-768x1024.jpg",[2352,9523,9525],{"id":9524},"the-vision","The Vision",[18,9527,9528,9529,9532],{},"The vision was plain and simple: Create a working POC-like product that ",[27,9530,9531],{},"adds real value and runs in production","\nwithin a single day while working in a hackathon-like atmosphere.",[18,9534,9535],{},"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.",[2352,9537,9539],{"id":9538},"preparation-for-bonbon","Preparation for BonBon",[18,9541,9542],{},"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.",[18,9544,9545],{},"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.",[18,9547,9548],{},"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.",[18,9550,9551],{},"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.",[18,9553,9554],{},[1773,9555],{"alt":9556,"src":9557},"Code Beispiel","https://media.synyx.de/uploads/2019/04/json.jpg",[18,9559,9560],{},"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.",[18,9562,9563],{},"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.",[18,9565,9566],{},"The discussion about the upcoming solution’s name was held some day during lunch. We called it BonBon.",[18,9568,9569],{},[1773,9570],{"alt":9571,"src":9572},"Bonbon","https://media.synyx.de/uploads/2019/04/bonbon-768x512.jpg",[2352,9574,9576],{"id":9575},"the-session","The Session",[18,9578,9579],{},"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.",[18,9581,9582],{},[1773,9583],{"alt":9584,"src":9585},"Whiteboard mit Post-its","https://media.synyx.de/uploads/2019/04/board_gimp2.jpg",[18,9587,9588],{},"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.",[18,9590,9591],{},"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.",[18,9593,9594],{},"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.",[18,9596,9597],{},"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!",[18,9599,9600],{},"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!",[18,9602,9603],{},"Unfortunately we missed the opportunity to take a picture of this moment but the receipt in the final app prototype\nlooked like this:",[18,9605,9606],{},[1773,9607],{"alt":9608,"src":9609},"App-Beispiel","https://media.synyx.de/uploads/2018/04/app.jpg",[2352,9611,9613],{"id":9612},"hacking-around-problems","Hacking around problems",[18,9615,9616],{},"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:",[577,9618,9619,9622,9625,9628,9631,9634],{},[580,9620,9621],{},"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.",[580,9623,9624],{},"Deployment was done by copying the jar artifact to the production vm “on foot”.",[580,9626,9627],{},"The receipt data was stored in memory – without backup. We just left out the persistence layer to save time.",[580,9629,9630],{},"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.",[580,9632,9633],{},"Debugging problems by frantically adding new logs on the new production system was common practice",[580,9635,9636],{},"… and some more",[18,9638,9639],{},[1773,9640],{"alt":9641,"src":9642},"Teamarbeit","https://media.synyx.de/uploads/2018/04/team.jpg",[18,9644,9645],{},[1773,9646],{"alt":48,"src":9647},"https://synyx.de/blog/2018-04-17-breakout-session/team.jpg",[18,9649,9650],{},"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.",[18,9652,9653],{},"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.",[2352,9655,9657],{"id":9656},"the-aftermath","The aftermath",[18,9659,9660,9661,9664],{},"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 (",[573,9662,9663],{},"cough",") positive nature.",[18,9666,9667],{},"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":48,"searchDepth":86,"depth":86,"links":9669},[9670,9671,9672,9673,9674],{"id":9524,"depth":86,"text":9525},{"id":9538,"depth":86,"text":9539},{"id":9575,"depth":86,"text":9576},{"id":9612,"depth":86,"text":9613},{"id":9656,"depth":86,"text":9657},[4221,613,614],"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":9505,"description":9515},"blog/breakout-session-how-to-prototype-your-enterprise-project-hackathon-like",[4221,9684,9685,9686],"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.","Blcw4uT2FNpf2rgpclc4clsllmntm5TBcVUMzvt5oRY",{"id":9690,"title":9691,"author":9692,"body":9694,"category":10137,"date":10138,"description":10139,"extension":617,"link":10140,"meta":10141,"navigation":499,"path":10142,"seo":10143,"slug":9698,"stem":10145,"tags":10146,"teaser":10149,"__hash__":10150},"blog/blog/an-image-slideshow-shortcode-for-hugo.md","An Image Slideshow Shortcode For Hugo",[9693],"sommer",{"type":11,"value":9695,"toc":10135},[9696,9699,9708,9711,9714,9717,9722,9725,9728,9772,9775,9938,9941,9944,9947,9950,10083,10086,10092,10098,10104,10110,10116,10122,10128,10133],[14,9697,9691],{"id":9698},"an-image-slideshow-shortcode-for-hugo",[18,9700,9701,9702,9707],{},"Creating static web sites with ",[585,9703,9706],{"href":9704,"rel":9705},"http://gohugo.io/",[589],"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!",[18,9709,9710],{},"Shortcodes are Hugo template snippets which can be used inside a markdown document with optional named or unamed\nparameters.",[18,9712,9713],{},"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.",[18,9715,9716],{},"To slide some images we need to know the folder’s location. So the shortcode in the markdown document will look like\nthis:",[18,9718,9719],{},[50,9720,9721],{},"{{``\u003C slide \"/assets/img/\">``}}",[18,9723,9724],{},"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.",[18,9726,9727],{},"Read the folder, count the number of files in it and calculate the duration of the whole css animation.",[43,9729,9731],{"className":288,"code":9730,"language":290,"meta":48,"style":48},"{{ $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",[50,9732,9733,9738,9743,9748,9753,9757,9762,9767],{"__ignoreMap":48},[53,9734,9735],{"class":55,"line":56},[53,9736,9737],{},"{{ $fadein := 2 }}\n",[53,9739,9740],{"class":55,"line":86},[53,9741,9742],{},"{{ $visible := 4 }}\n",[53,9744,9745],{"class":55,"line":126},[53,9746,9747],{},"{{ $param := .Get 0 }}\n",[53,9749,9750],{"class":55,"line":163},[53,9751,9752],{},"{{ $localFolder := printf \"/static%s/\" $param }}\n",[53,9754,9755],{"class":55,"line":186},[53,9756,500],{"emptyLinePlaceholder":499},[53,9758,9759],{"class":55,"line":221},[53,9760,9761],{},"{{ $files := sort (readDir $localFolder) }}\n",[53,9763,9764],{"class":55,"line":242},[53,9765,9766],{},"{{ $numberOfFiles := len $files }}\n",[53,9768,9769],{"class":55,"line":273},[53,9770,9771],{},"{{ $animationDuration := mul (add $fadein $visible) $numberOfFiles }}\n",[18,9773,9774],{},"Now create the slider div, iterate through the files and generate an img tag for each file.",[43,9776,9778],{"className":288,"code":9777,"language":290,"meta":48,"style":48}," \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",[50,9779,9780,9785,9790,9795,9800,9805,9810,9814,9819,9823,9828,9833,9838,9843,9847,9851,9856,9861,9866,9871,9876,9880,9885,9890,9895,9900,9905,9909,9913,9918,9923,9928,9933],{"__ignoreMap":48},[53,9781,9782],{"class":55,"line":56},[53,9783,9784],{}," \u003Cstyle>\n",[53,9786,9787],{"class":55,"line":86},[53,9788,9789],{}," .slider {\n",[53,9791,9792],{"class":55,"line":126},[53,9793,9794],{}," padding-bottom: 70%;\n",[53,9796,9797],{"class":55,"line":163},[53,9798,9799],{}," width: 100%;\n",[53,9801,9802],{"class":55,"line":186},[53,9803,9804],{}," height: 0;\n",[53,9806,9807],{"class":55,"line":221},[53,9808,9809],{}," position: relative;\n",[53,9811,9812],{"class":55,"line":242},[53,9813,860],{},[53,9815,9816],{"class":55,"line":273},[53,9817,9818],{}," .slider img {\n",[53,9820,9821],{"class":55,"line":279},[53,9822,9799],{},[53,9824,9825],{"class":55,"line":496},[53,9826,9827],{}," height: auto;\n",[53,9829,9830],{"class":55,"line":503},[53,9831,9832],{}," position: absolute;\n",[53,9834,9835],{"class":55,"line":509},[53,9836,9837],{}," opacity: 0;\n",[53,9839,9840],{"class":55,"line":515},[53,9841,9842],{}," animation: slide infinite {{$animationDuration}}s;\n",[53,9844,9845],{"class":55,"line":521},[53,9846,860],{},[53,9848,9849],{"class":55,"line":527},[53,9850,500],{"emptyLinePlaceholder":499},[53,9852,9853],{"class":55,"line":533},[53,9854,9855],{}," {{ $x := div 100.0 $animationDuration }}\n",[53,9857,9858],{"class":55,"line":539},[53,9859,9860],{}," {{ $p0 := 0 }}\n",[53,9862,9863],{"class":55,"line":545},[53,9864,9865],{}," {{ $p1 := mul $x $fadein }}\n",[53,9867,9868],{"class":55,"line":2070},[53,9869,9870],{}," {{ $p2 := mul $x (add $fadein $visible) }}\n",[53,9872,9873],{"class":55,"line":2075},[53,9874,9875],{}," {{ $p3 := mul $x (add (add $fadein $visible) $fadein) }}\n",[53,9877,9878],{"class":55,"line":2081},[53,9879,500],{"emptyLinePlaceholder":499},[53,9881,9882],{"class":55,"line":2087},[53,9883,9884],{}," @keyframes slide {\n",[53,9886,9887],{"class":55,"line":2092},[53,9888,9889],{}," {{ $p0 }}% { opacity: 0; }\n",[53,9891,9892],{"class":55,"line":2097},[53,9893,9894],{}," {{ $p1 }}% { opacity: 1; }\n",[53,9896,9897],{"class":55,"line":2103},[53,9898,9899],{}," {{ $p2 }}% { opacity: 1; }\n",[53,9901,9902],{"class":55,"line":2109},[53,9903,9904],{}," {{ $p3 }}% { opacity: 0; }\n",[53,9906,9907],{"class":55,"line":2115},[53,9908,860],{},[53,9910,9911],{"class":55,"line":2120},[53,9912,500],{"emptyLinePlaceholder":499},[53,9914,9915],{"class":55,"line":2946},[53,9916,9917],{}," {{ range $index, $value := $files }}\n",[53,9919,9920],{"class":55,"line":2952},[53,9921,9922],{}," {{ $delay := mul (add $fadein $visible) $index }}\n",[53,9924,9925],{"class":55,"line":2964},[53,9926,9927],{}," .slider img:nth-child({{add $index 1}}){animation-delay:{{$delay}}s;}\n",[53,9929,9930],{"class":55,"line":2973},[53,9931,9932],{}," {{ end }}\n",[53,9934,9935],{"class":55,"line":2979},[53,9936,9937],{},"\u003C/style>\n",[18,9939,9940],{},"The last part is the css animation.",[18,9942,9943],{},"All images will get an opacity of 0 and an infinite animation with a keyframe rule binding.",[18,9945,9946],{},"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.",[18,9948,9949],{},"The individual animation delay for every image is the last step to fade one image into the next one.",[43,9951,9953],{"className":288,"code":9952,"language":290,"meta":48,"style":48},"\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",[50,9954,9955,9959,9963,9967,9971,9975,9979,9983,9987,9991,9995,9999,10003,10007,10011,10015,10019,10023,10027,10031,10035,10039,10043,10047,10051,10055,10059,10063,10067,10071,10075,10079],{"__ignoreMap":48},[53,9956,9957],{"class":55,"line":56},[53,9958,500],{"emptyLinePlaceholder":499},[53,9960,9961],{"class":55,"line":86},[53,9962,9789],{},[53,9964,9965],{"class":55,"line":126},[53,9966,9794],{},[53,9968,9969],{"class":55,"line":163},[53,9970,9799],{},[53,9972,9973],{"class":55,"line":186},[53,9974,9804],{},[53,9976,9977],{"class":55,"line":221},[53,9978,9809],{},[53,9980,9981],{"class":55,"line":242},[53,9982,860],{},[53,9984,9985],{"class":55,"line":273},[53,9986,9818],{},[53,9988,9989],{"class":55,"line":279},[53,9990,9799],{},[53,9992,9993],{"class":55,"line":496},[53,9994,9827],{},[53,9996,9997],{"class":55,"line":503},[53,9998,9832],{},[53,10000,10001],{"class":55,"line":509},[53,10002,9837],{},[53,10004,10005],{"class":55,"line":515},[53,10006,9842],{},[53,10008,10009],{"class":55,"line":521},[53,10010,860],{},[53,10012,10013],{"class":55,"line":527},[53,10014,500],{"emptyLinePlaceholder":499},[53,10016,10017],{"class":55,"line":533},[53,10018,9855],{},[53,10020,10021],{"class":55,"line":539},[53,10022,9860],{},[53,10024,10025],{"class":55,"line":545},[53,10026,9865],{},[53,10028,10029],{"class":55,"line":2070},[53,10030,9870],{},[53,10032,10033],{"class":55,"line":2075},[53,10034,9875],{},[53,10036,10037],{"class":55,"line":2081},[53,10038,500],{"emptyLinePlaceholder":499},[53,10040,10041],{"class":55,"line":2087},[53,10042,9884],{},[53,10044,10045],{"class":55,"line":2092},[53,10046,9889],{},[53,10048,10049],{"class":55,"line":2097},[53,10050,9894],{},[53,10052,10053],{"class":55,"line":2103},[53,10054,9899],{},[53,10056,10057],{"class":55,"line":2109},[53,10058,9904],{},[53,10060,10061],{"class":55,"line":2115},[53,10062,860],{},[53,10064,10065],{"class":55,"line":2120},[53,10066,500],{"emptyLinePlaceholder":499},[53,10068,10069],{"class":55,"line":2946},[53,10070,9917],{},[53,10072,10073],{"class":55,"line":2952},[53,10074,9922],{},[53,10076,10077],{"class":55,"line":2964},[53,10078,9927],{},[53,10080,10081],{"class":55,"line":2973},[53,10082,9932],{},[18,10084,10085],{},"The following shortcode in action has an additional JavaScript navigation and some prefixed css properties to support a\nwider range of browsers.",[18,10087,10088],{},[1773,10089],{"alt":10090,"src":10091},"Synema Logo Leinwand","https://media.synyx.de/uploads/2019/03/01_synema-768x512.jpg",[18,10093,10094],{},[1773,10095],{"alt":10096,"src":10097},"Synema Buffet","https://media.synyx.de/uploads/2019/03/IMG_5784-768x512.jpg",[18,10099,10100],{},[1773,10101],{"alt":10102,"src":10103},"Snapschot vom Synema event","https://media.synyx.de/uploads/2019/03/IMG_5834-768x512.jpg",[18,10105,10106],{},[1773,10107],{"alt":10108,"src":10109},"Synema Kino eingang","https://media.synyx.de/uploads/2019/03/IMG_5835-768x512.jpg",[18,10111,10112],{},[1773,10113],{"alt":10114,"src":10115},"Synema Star Wars Impression","https://media.synyx.de/uploads/2019/03/IMG_5862-768x512.jpg",[18,10117,10118],{},[1773,10119],{"alt":10120,"src":10121},"Publikum beim Synema event","https://media.synyx.de/uploads/2019/03/IMG_5847-768x512.jpg",[18,10123,10124],{},[1773,10125],{"alt":10126,"src":10127},"Thomas Kraft beim Synema event","https://media.synyx.de/uploads/2019/03/IMG_5954-768x512.jpg",[18,10129,10130],{},[1773,10131],{"alt":48,"src":10132},"https://media.synyx.de/uploads/2019/03/IMG_6016-768x512.jpg",[607,10134,989],{},{"title":48,"searchDepth":86,"depth":86,"links":10136},[],[613,614],"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":9691,"description":10144},"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",[10147,10148],"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":10152,"title":10153,"author":10154,"body":10156,"category":10966,"date":10967,"description":10968,"extension":617,"link":10969,"meta":10970,"navigation":499,"path":10971,"seo":10972,"slug":10160,"stem":10973,"tags":10974,"teaser":10980,"__hash__":10981},"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",[10155],"larrasz",{"type":11,"value":10157,"toc":10955},[10158,10161,10164,10168,10181,10206,10212,10242,10267,10447,10451,10458,10512,10516,10527,10531,10559,10651,10655,10662,10709,10728,10741,10745,10751,10762,10775,10860,10864,10867,10873,10907,10914,10917,10920,10953],[14,10159,10153],{"id":10160},"using-travis-ci-to-deploy-to-maven-repositories-and-github-releases",[18,10162,10163],{},"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.",[2352,10165,10167],{"id":10166},"preparing-github-releases","Preparing GitHub Releases",[18,10169,10170,10171,10176,10177,10180],{},"Sergey Mashkov has written a ",[585,10172,10175],{"href":10173,"rel":10174},"https://github.com/cy6erGn0m/github-release-plugin",[589],"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 ",[50,10178,10179],{},"pom.xml"," in order to use this plugin.",[18,10182,10183,10184,10187,10188,10191,10192,10195,10196,10199,10200,10205],{},"The plugin uses the ",[50,10185,10186],{},"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",[50,10189,10190],{},"scm:git:git@github.com:...",". Neither ",[50,10193,10194],{},"scm:git:https://github.com/...","nor ",[50,10197,10198],{},"scm:git:ssh://git@github.com/...","work, but\na ",[585,10201,10204],{"href":10202,"rel":10203},"https://github.com/cy6erGn0m/github-release-plugin/pull/2",[589],"pull request"," has been created that adds this\nfunctionality.",[18,10207,10208,10209,10211],{},"So add an ",[50,10210,10186],{},"section to your pom that looks like this:",[43,10213,10215],{"className":1980,"code":10214,"language":1982,"meta":48,"style":48},"\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",[50,10216,10217,10222,10227,10232,10237],{"__ignoreMap":48},[53,10218,10219],{"class":55,"line":56},[53,10220,10221],{},"\u003Cscm>\n",[53,10223,10224],{"class":55,"line":86},[53,10225,10226],{}," \u003Curl>https://github.com/example/project\u003C/url>\n",[53,10228,10229],{"class":55,"line":126},[53,10230,10231],{}," \u003Cconnection>scm:git:git@github.com:example/project.git\u003C/connection>\n",[53,10233,10234],{"class":55,"line":163},[53,10235,10236],{}," \u003CdeveloperConnection>scm:git:git@github.com:example/project.git\u003C/developerConnection>\n",[53,10238,10239],{"class":55,"line":186},[53,10240,10241],{},"\u003C/scm>\n",[18,10243,10244,10245,10250,10251,10254,10255,10260,10261,10266],{},"The second step is to include the plugin in our pom. Right now the plugin is only available\nfrom ",[585,10246,10249],{"href":10247,"rel":10248},"http://dl.bintray.com/cy6ergn0m/maven",[589],"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 ",[50,10252,10253],{},"-Prelease","and only if the deploy\ngoal is invoked. For more information on how to configure the plugin options please refer to\nits ",[585,10256,10259],{"href":10257,"rel":10258},"https://github.com/cy6erGn0m/github-release-plugin#plugin-configuration-options",[589],"documentation"," and\nthe ",[585,10262,10265],{"href":10263,"rel":10264},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#pitfalls",[589],"pitfalls"," below.",[43,10268,10270],{"className":1980,"code":10269,"language":1982,"meta":48,"style":48},"\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",[50,10271,10272,10277,10281,10286,10291,10296,10301,10306,10311,10316,10321,10326,10331,10336,10341,10346,10351,10356,10361,10366,10371,10376,10381,10386,10391,10396,10401,10406,10411,10416,10421,10426,10431,10436,10441],{"__ignoreMap":48},[53,10273,10274],{"class":55,"line":56},[53,10275,10276],{},"\u003Cprofiles>\n",[53,10278,10279],{"class":55,"line":86},[53,10280,322],{},[53,10282,10283],{"class":55,"line":126},[53,10284,10285],{}," \u003Cprofile>\n",[53,10287,10288],{"class":55,"line":163},[53,10289,10290],{}," \u003Cid>release\u003C/id>\n",[53,10292,10293],{"class":55,"line":186},[53,10294,10295],{}," \u003CpluginRepositories>\n",[53,10297,10298],{"class":55,"line":221},[53,10299,10300],{}," \u003CpluginRepository>\n",[53,10302,10303],{"class":55,"line":242},[53,10304,10305],{}," \u003Cid>bintray-cy6ergn0m-maven\u003C/id>\n",[53,10307,10308],{"class":55,"line":273},[53,10309,10310],{}," \u003Cname>bintray-plugins\u003C/name>\n",[53,10312,10313],{"class":55,"line":279},[53,10314,10315],{}," \u003Curl>http://dl.bintray.com/cy6ergn0m/maven\u003C/url>\n",[53,10317,10318],{"class":55,"line":496},[53,10319,10320],{}," \u003C/pluginRepository>\n",[53,10322,10323],{"class":55,"line":503},[53,10324,10325],{}," \u003C/pluginRepositories>\n",[53,10327,10328],{"class":55,"line":509},[53,10329,10330],{}," \u003Cbuild>\n",[53,10332,10333],{"class":55,"line":515},[53,10334,10335],{}," \u003Cplugins>\n",[53,10337,10338],{"class":55,"line":521},[53,10339,10340],{}," \u003Cplugin>\n",[53,10342,10343],{"class":55,"line":527},[53,10344,10345],{}," \u003CgroupId>cy.github\u003C/groupId>\n",[53,10347,10348],{"class":55,"line":533},[53,10349,10350],{}," \u003CartifactId>github-release-plugin\u003C/artifactId>\n",[53,10352,10353],{"class":55,"line":539},[53,10354,10355],{}," \u003Cversion>0.5.1\u003C/version>\n",[53,10357,10358],{"class":55,"line":545},[53,10359,10360],{}," \u003Cconfiguration>\n",[53,10362,10363],{"class":55,"line":2070},[53,10364,10365],{}," \u003CtagName>${project.version}\u003C/tagName>\n",[53,10367,10368],{"class":55,"line":2075},[53,10369,10370],{}," \u003CreleaseTitle>${project.artifactId}-${project.version}\u003C/releaseTitle>\n",[53,10372,10373],{"class":55,"line":2081},[53,10374,10375],{}," \u003CserverId>github\u003C/serverId>\n",[53,10377,10378],{"class":55,"line":2087},[53,10379,10380],{}," \u003C/configuration>\n",[53,10382,10383],{"class":55,"line":2092},[53,10384,10385],{}," \u003Cexecutions>\n",[53,10387,10388],{"class":55,"line":2097},[53,10389,10390],{}," \u003Cexecution>\n",[53,10392,10393],{"class":55,"line":2103},[53,10394,10395],{}," \u003Cgoals>\n",[53,10397,10398],{"class":55,"line":2109},[53,10399,10400],{}," \u003Cgoal>gh-upload\u003C/goal>\n",[53,10402,10403],{"class":55,"line":2115},[53,10404,10405],{}," \u003C/goals>\n",[53,10407,10408],{"class":55,"line":2120},[53,10409,10410],{}," \u003Cphase>deploy\u003C/phase>\n",[53,10412,10413],{"class":55,"line":2946},[53,10414,10415],{}," \u003C/execution>\n",[53,10417,10418],{"class":55,"line":2952},[53,10419,10420],{}," \u003C/executions>\n",[53,10422,10423],{"class":55,"line":2964},[53,10424,10425],{}," \u003C/plugin>\n",[53,10427,10428],{"class":55,"line":2973},[53,10429,10430],{}," \u003C/plugins>\n",[53,10432,10433],{"class":55,"line":2979},[53,10434,10435],{}," \u003C/build>\n",[53,10437,10438],{"class":55,"line":2990},[53,10439,10440],{}," \u003C/profile>\n",[53,10442,10444],{"class":55,"line":10443},35,[53,10445,10446],{},"\u003C/profiles>\n",[2352,10448,10450],{"id":10449},"preparing-for-maven-releases","Preparing for Maven releases",[18,10452,10453,10454,10457],{},"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 ",[50,10455,10456],{},"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.",[43,10459,10461],{"className":1980,"code":10460,"language":1982,"meta":48,"style":48},"\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",[50,10462,10463,10468,10473,10478,10483,10488,10493,10497,10502,10507],{"__ignoreMap":48},[53,10464,10465],{"class":55,"line":56},[53,10466,10467],{},"\u003CdistributionManagement>\n",[53,10469,10470],{"class":55,"line":86},[53,10471,10472],{}," \u003Crepository>\n",[53,10474,10475],{"class":55,"line":126},[53,10476,10477],{}," \u003Cid>oss\u003C/id>\n",[53,10479,10480],{"class":55,"line":163},[53,10481,10482],{}," \u003Curl>https://nexus.example.com/content/repositories/oss-releases\u003C/url>\n",[53,10484,10485],{"class":55,"line":186},[53,10486,10487],{}," \u003C/repository>\n",[53,10489,10490],{"class":55,"line":221},[53,10491,10492],{}," \u003CsnapshotRepository>\n",[53,10494,10495],{"class":55,"line":242},[53,10496,10477],{},[53,10498,10499],{"class":55,"line":273},[53,10500,10501],{}," \u003Curl>https://nexus.example.com/content/repositories/oss-snapshots\u003C/url>\n",[53,10503,10504],{"class":55,"line":279},[53,10505,10506],{}," \u003C/snapshotRepository>\n",[53,10508,10509],{"class":55,"line":496},[53,10510,10511],{},"\u003C/distributionManagement>\n",[2352,10513,10515],{"id":10514},"putting-it-all-together","Putting it all together",[18,10517,10518,10519,10522,10523,10526],{},"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 ",[50,10520,10521],{},"settings.xml","for use with\nMaven, a ",[50,10524,10525],{},".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.",[649,10528,10530],{"id":10529},"maven-settings","Maven settings",[18,10532,10533,10534,10536,10537,10540,10541,10544,10545,10548,10549,10552,10553,10558],{},"Create a new ",[50,10535,10521],{},"file in your repository, e.g in a ",[50,10538,10539],{},".travis/","directory. The content of this file should look\nlike the following snippet. The ",[50,10542,10543],{},"\u003Cserver>","ids have to match the ids in ",[50,10546,10547],{},"\u003CdistributionManagement>","and the ",[50,10550,10551],{},"\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 ",[585,10554,10557],{"href":10555,"rel":10556},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#configuring-travis-ci-itself",[589],"environment variables","\nwill be configured soon.",[43,10560,10562],{"className":1980,"code":10561,"language":1982,"meta":48,"style":48},"\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",[50,10563,10564,10569,10574,10579,10584,10589,10594,10599,10604,10609,10614,10618,10623,10628,10633,10637,10642,10646],{"__ignoreMap":48},[53,10565,10566],{"class":55,"line":56},[53,10567,10568],{},"\u003Csettings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n",[53,10570,10571],{"class":55,"line":86},[53,10572,10573],{}," xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[53,10575,10576],{"class":55,"line":126},[53,10577,10578],{}," xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n",[53,10580,10581],{"class":55,"line":163},[53,10582,10583],{}," http://maven.apache.org/xsd/settings-1.0.0.xsd\">\n",[53,10585,10586],{"class":55,"line":186},[53,10587,10588],{}," \u003Cservers>\n",[53,10590,10591],{"class":55,"line":221},[53,10592,10593],{}," \u003Cserver>\n",[53,10595,10596],{"class":55,"line":242},[53,10597,10598],{}," \u003Cid>oss\u003C/id>\n",[53,10600,10601],{"class":55,"line":273},[53,10602,10603],{}," \u003Cusername>${env.NEXUS_USERNAME}\u003C/username>\n",[53,10605,10606],{"class":55,"line":279},[53,10607,10608],{}," \u003Cpassword>${env.NEXUS_PASSWORD}\u003C/password>\n",[53,10610,10611],{"class":55,"line":496},[53,10612,10613],{}," \u003C/server>\n",[53,10615,10616],{"class":55,"line":503},[53,10617,10593],{},[53,10619,10620],{"class":55,"line":509},[53,10621,10622],{}," \u003Cid>github\u003C/id>\n",[53,10624,10625],{"class":55,"line":515},[53,10626,10627],{}," \u003Cusername>${env.GITHUB_USERNAME}\u003C/username>\n",[53,10629,10630],{"class":55,"line":521},[53,10631,10632],{}," \u003Cpassword>${env.GITHUB_TOKEN}\u003C/password>\n",[53,10634,10635],{"class":55,"line":527},[53,10636,10613],{},[53,10638,10639],{"class":55,"line":533},[53,10640,10641],{}," \u003C/servers>\n",[53,10643,10644],{"class":55,"line":539},[53,10645,500],{"emptyLinePlaceholder":499},[53,10647,10648],{"class":55,"line":545},[53,10649,10650],{},"\u003C/settings>\n",[649,10652,10654],{"id":10653},"release-script","Release script",[18,10656,10657,10658,10661],{},"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",[50,10659,10660],{},"TRAVIS_TAG"," environment variable\nwill be used. Travis CI uses this variable to inject the value of the git tag into the build.",[43,10663,10665],{"className":1980,"code":10664,"language":1982,"meta":48,"style":48},"#!/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",[50,10666,10667,10672,10676,10681,10685,10690,10695,10699,10704],{"__ignoreMap":48},[53,10668,10669],{"class":55,"line":56},[53,10670,10671],{},"#!/usr/bin/env bash\n",[53,10673,10674],{"class":55,"line":86},[53,10675,500],{"emptyLinePlaceholder":499},[53,10677,10678],{"class":55,"line":126},[53,10679,10680],{},"set -e\n",[53,10682,10683],{"class":55,"line":163},[53,10684,500],{"emptyLinePlaceholder":499},[53,10686,10687],{"class":55,"line":186},[53,10688,10689],{},"echo \"Ensuring that pom matches $TRAVIS_TAG\"\n",[53,10691,10692],{"class":55,"line":221},[53,10693,10694],{},"./mvnw org.codehaus.mojo:versions-maven-plugin:2.5:set -DnewVersion=$TRAVIS_TAG\n",[53,10696,10697],{"class":55,"line":242},[53,10698,500],{"emptyLinePlaceholder":499},[53,10700,10701],{"class":55,"line":273},[53,10702,10703],{},"echo \"Uploading to oss repo and GitHub\"\n",[53,10705,10706],{"class":55,"line":279},[53,10707,10708],{},"./mvnw deploy --settings .travis/settings.xml -DskipTests=true --batch-mode --update-snapshots -Prelease\n",[18,10710,10711,10712,10715,10716,10719,10720,10723,10724,10727],{},"The script first sets the ",[50,10713,10714],{},"\u003Cversion>","in the pom exactly to our git tag’s value. So your tag always matches the version\nyou want to release, e.g. ",[50,10717,10718],{},"1.0","or ",[50,10721,10722],{},"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",[50,10725,10726],{},"release","profile. This tells Maven to not only create and upload a Maven release but also to\ncreate a new GitHub Release.",[18,10729,10730,10731,10734,10735,10737,10738,4562],{},"Name this script ",[50,10732,10733],{},"release.sh",", put it inside the ",[50,10736,10539],{},"directory and make it executable (",[50,10739,10740],{},"chmod +x",[649,10742,10744],{"id":10743},"build-configuration","Build configuration",[18,10746,10747,10748,10750],{},"Travis CI uses a file named ",[50,10749,10525],{},"at the root of a GitHub repository. The snipped contains the necessary steps.",[18,10752,10753,10754,10757,10758,10761],{},"In a normal build we just want to execute a simple ",[50,10755,10756],{},"clean verify",". The ",[50,10759,10760],{},"verify","goal will execute unit and integration\ntests. To make subsequent builds faster, we want to cache the m2 repositories during builds.",[18,10763,10764,10765,10767,10768,10771,10772,986],{},"The most important part is the deploy section. Here we configure Travis CI to run the ",[50,10766,10733],{},"script if and only if\na tag has been pushed (",[50,10769,10770],{},"tags: true",") on the repo ",[50,10773,10774],{},"example/project",[43,10776,10778],{"className":1980,"code":10777,"language":1982,"meta":48,"style":48},"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",[50,10779,10780,10785,10790,10795,10800,10805,10810,10815,10820,10825,10830,10835,10840,10845,10850,10855],{"__ignoreMap":48},[53,10781,10782],{"class":55,"line":56},[53,10783,10784],{},"sudo: false\n",[53,10786,10787],{"class":55,"line":86},[53,10788,10789],{},"language: java\n",[53,10791,10792],{"class":55,"line":126},[53,10793,10794],{},"jdk:\n",[53,10796,10797],{"class":55,"line":163},[53,10798,10799],{}," - oraclejdk8\n",[53,10801,10802],{"class":55,"line":186},[53,10803,10804],{},"script: ./mvnw clean verify\n",[53,10806,10807],{"class":55,"line":221},[53,10808,10809],{},"cache:\n",[53,10811,10812],{"class":55,"line":242},[53,10813,10814],{}," directories:\n",[53,10816,10817],{"class":55,"line":273},[53,10818,10819],{}," - $HOME/.m2\n",[53,10821,10822],{"class":55,"line":279},[53,10823,10824],{},"deploy:\n",[53,10826,10827],{"class":55,"line":496},[53,10828,10829],{}," provider: script\n",[53,10831,10832],{"class":55,"line":503},[53,10833,10834],{}," script: .travis/release.sh\n",[53,10836,10837],{"class":55,"line":509},[53,10838,10839],{}," skip_cleanup: true\n",[53,10841,10842],{"class":55,"line":515},[53,10843,10844],{}," on:\n",[53,10846,10847],{"class":55,"line":521},[53,10848,10849],{}," repo: example/project\n",[53,10851,10852],{"class":55,"line":527},[53,10853,10854],{}," tags: true\n",[53,10856,10857],{"class":55,"line":533},[53,10858,10859],{}," jdk: oraclejdk8\n",[649,10861,10863],{"id":10862},"configuring-travis-ci-itself","Configuring Travis CI itself",[18,10865,10866],{},"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:",[18,10868,10869],{},[1773,10870],{"alt":10871,"src":10872},"travis environment","https://media.synyx.de/uploads/2018/01/travis-environment-768x282.png",[18,10874,10875,10876,10879,10880,10883,10884,10889,10890,10879,10893,10896,10897,10900,10901,10906],{},"Add ",[50,10877,10878],{},"NEXUS_USER","and ",[50,10881,10882],{},"NEXUS_PASSWORD","for your\nnewly ",[585,10885,10888],{"href":10886,"rel":10887},"https://synyx.de/blog/2018-01-24-travisci-github-releases/?page=3#preparing-maven-releases",[589],"created user",". You\nalso need to configure ",[50,10891,10892],{},"GITHUB_USERNAME",[50,10894,10895],{},"GITHUB_TOKEN",". But even though the field is called password, what your\nreally want to configure here is your ",[27,10898,10899],{},"GitHub API token",". Otherwise, the GitHub release plugin will not be able to\nupload artifacts. You can obtain your personal access token ",[585,10902,10905],{"href":10903,"rel":10904},"https://github.com/settings/tokens",[589],"here",". The token needs\naccess to the repo scope.",[18,10908,10909,10910,10913],{},"And that’s it. Now you can simply create a new tag in your repository (",[50,10911,10912],{},"git tag -a 1.1 -m \"Release 1.1\"","), push it to\nGitHub and Travis CI will trigger the release process. Happy releasing!",[2352,10915,10916],{"id":10265},"Pitfalls",[18,10918,10919],{},"Some advice so that you do not encounter the same problems we did:",[577,10921,10922,10931,10943,10946],{},[580,10923,10924,10925,10927,10928],{},"Do not forget to make ",[50,10926,10733],{},"executable otherwise the Travis CI build will fail with a rather unhelpful message:\n",[50,10929,10930],{},"Script failed with status 127.",[580,10932,10933,10934,10937,10938,10940,10941,986],{},"The ",[50,10935,10936],{},"serverId","in the GitHub release plugin’s configuration refers to a ",[50,10939,10543],{},"entry in Maven’s ",[50,10942,10521],{},[580,10944,10945],{},"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.",[580,10947,10948,10949,10952],{},"Do not change the value of ",[50,10950,10951],{},"\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.",[607,10954,989],{},{"title":48,"searchDepth":86,"depth":86,"links":10956},[10957,10958,10959,10965],{"id":10166,"depth":86,"text":10167},{"id":10449,"depth":86,"text":10450},{"id":10514,"depth":86,"text":10515,"children":10960},[10961,10962,10963,10964],{"id":10529,"depth":126,"text":10530},{"id":10653,"depth":126,"text":10654},{"id":10743,"depth":126,"text":10744},{"id":10862,"depth":126,"text":10863},{"id":10265,"depth":86,"text":10916},[613,614],"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":10153,"description":10163},"blog/using-travis-ci-to-deploy-to-maven-repositories-and-github-releases",[10975,10976,290,10977,10978,10979],"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":10983,"title":10984,"author":10985,"body":10986,"category":11401,"date":11402,"description":11403,"extension":617,"link":11404,"meta":11405,"navigation":499,"path":11406,"seo":11407,"slug":10990,"stem":11408,"tags":11409,"teaser":11414,"__hash__":11415},"blog/blog/the-struggle-with-hazelcast-queue-persistence.md","The struggle with Hazelcast queue persistence",[9507],{"type":11,"value":10987,"toc":11388},[10988,10991,10994,10998,11001,11009,11012,11029,11044,11050,11054,11062,11065,11068,11072,11075,11078,11155,11159,11162,11165,11168,11171,11180,11184,11187,11198,11203,11206,11209,11213,11216,11302,11306,11309,11312,11315,11318,11322,11325,11328,11343,11346,11350,11353,11362,11366,11369,11372,11386],[14,10989,10984],{"id":10990},"the-struggle-with-hazelcast-queue-persistence",[18,10992,10993],{},"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.",[2352,10995,10997],{"id":10996},"the-problem-to-solve","The problem to solve",[18,10999,11000],{},"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:",[3525,11002,11003,11006],{},[580,11004,11005],{},"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.",[580,11007,11008],{},"Queueing and retrying failed calls to external systems for higher resilience",[18,11010,11011],{},"We gathered the following core requirements for the queueing mechanism:",[577,11013,11014,11017,11020,11023,11026],{},[580,11015,11016],{},"Embedded into the application. Using a potentially failing external system would defeat reason 2",[580,11018,11019],{},"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.",[580,11021,11022],{},"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",[580,11024,11025],{},"Performance. Due to the amount of processed data the solution has to be fast.",[580,11027,11028],{},"Low complexity and easy maintainability. “Keep it simple” is a key ambition for everything that we use or build.",[18,11030,11031,11032,11037,11038,11043],{},"After a short evaluation phase these requirements led us to the conclusion that ",[585,11033,11036],{"href":11034,"rel":11035},"https://hazelcast.org/",[589],"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 ",[585,11039,11042],{"href":11040,"rel":11041},"https://github.com/hazelcast/hazelcast",[589],"open source",", yay!",[18,11045,11046],{},[1773,11047],{"alt":11048,"src":11049},"Logo hazelcast","https://media.synyx.de/uploads/2019/04/hazelcast_logo_small-768x185.png",[2352,11051,11053],{"id":11052},"the-easy-part-divide-and-queue","The easy part – divide and queue",[18,11055,11056,11057,11061],{},"The first implementation of the Hazelcast queue in our application was a piece of cake. Following\nthe ",[585,11058,10259],{"href":11059,"rel":11060},"http://docs.hazelcast.org/docs/3.8.2/manual/html-single/index.html",[589]," 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.",[18,11063,11064],{},"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.",[18,11066,11067],{},"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.",[2352,11069,11071],{"id":11070},"the-hard-part-persist-the-shit-out-of-it","The hard part – persist the shit out of it",[18,11073,11074],{},"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!",[18,11076,11077],{},"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.",[43,11079,11081],{"className":288,"code":11080,"language":290,"meta":48,"style":48},"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",[50,11082,11083,11088,11092,11097,11101,11106,11110,11115,11119,11124,11128,11133,11137,11142,11146,11151],{"__ignoreMap":48},[53,11084,11085],{"class":55,"line":56},[53,11086,11087],{},"public interface QueueStore\u003CT> {\n",[53,11089,11090],{"class":55,"line":86},[53,11091,500],{"emptyLinePlaceholder":499},[53,11093,11094],{"class":55,"line":126},[53,11095,11096],{}," void store(Long key, T value);\n",[53,11098,11099],{"class":55,"line":163},[53,11100,500],{"emptyLinePlaceholder":499},[53,11102,11103],{"class":55,"line":186},[53,11104,11105],{}," void storeAll(Map\u003CLong, T> map);\n",[53,11107,11108],{"class":55,"line":221},[53,11109,500],{"emptyLinePlaceholder":499},[53,11111,11112],{"class":55,"line":242},[53,11113,11114],{}," void delete(Long key);\n",[53,11116,11117],{"class":55,"line":273},[53,11118,500],{"emptyLinePlaceholder":499},[53,11120,11121],{"class":55,"line":279},[53,11122,11123],{}," void deleteAll(Collection\u003CLong> keys);\n",[53,11125,11126],{"class":55,"line":496},[53,11127,500],{"emptyLinePlaceholder":499},[53,11129,11130],{"class":55,"line":503},[53,11131,11132],{}," T load(Long key);\n",[53,11134,11135],{"class":55,"line":509},[53,11136,500],{"emptyLinePlaceholder":499},[53,11138,11139],{"class":55,"line":515},[53,11140,11141],{}," Map\u003CLong, T> loadAll(Collection\u003CLong> keys);\n",[53,11143,11144],{"class":55,"line":521},[53,11145,500],{"emptyLinePlaceholder":499},[53,11147,11148],{"class":55,"line":527},[53,11149,11150],{}," Set\u003CLong> loadAllKeys();\n",[53,11152,11153],{"class":55,"line":533},[53,11154,282],{},[649,11156,11158],{"id":11157},"difficulty-1-how-to-persist","Difficulty #1: How to persist?",[18,11160,11161],{},"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.",[18,11163,11164],{},"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.",[18,11166,11167],{},"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.",[18,11169,11170],{},"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.",[18,11172,11173,11174,11179],{},"In the end the train stopped at ",[585,11175,11178],{"href":11176,"rel":11177},"https://github.com/OpenHFT/Chronicle-Map",[589],"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!",[649,11181,11183],{"id":11182},"obstacle-2-i-am-the-persistence-master","Obstacle #2: I am the persistence Master!",[18,11185,11186],{},"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?",[18,11188,11189,11190,11193,11194,11197],{},"After some more research we discovered a sentence in the Hazelcast documentation of the ",[27,11191,11192],{},"map"," persistence that was\nmissing in the documentation of the ",[27,11195,11196],{},"queue"," persistence. It says:",[5221,11199,11200],{},[18,11201,11202],{},"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.",[18,11204,11205],{},"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!",[18,11207,11208],{},"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.",[649,11210,11212],{"id":11211},"stumbling-block-3-configuring-the-queuestore","Stumbling block #3: Configuring the QueueStore",[18,11214,11215],{},"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.",[43,11217,11219],{"className":288,"code":11218,"language":290,"meta":48,"style":48},"// 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",[50,11220,11221,11226,11231,11236,11241,11246,11251,11256,11261,11266,11270,11275,11279,11283,11288,11293,11298],{"__ignoreMap":48},[53,11222,11223],{"class":55,"line":56},[53,11224,11225],{},"// no Strings - does not work\n",[53,11227,11228],{"class":55,"line":86},[53,11229,11230],{},"QueueStoreConfig queueStoreConfig = new QueueStoreConfig();\n",[53,11232,11233],{"class":55,"line":126},[53,11234,11235],{},"queueStoreConfig.setEnabled(true);\n",[53,11237,11238],{"class":55,"line":163},[53,11239,11240],{},"Properties properties = new Properties();\n",[53,11242,11243],{"class":55,"line":186},[53,11244,11245],{},"properties.put(\"binary\", true);\n",[53,11247,11248],{"class":55,"line":221},[53,11249,11250],{},"properties.put(\"memory-limit\", 0);\n",[53,11252,11253],{"class":55,"line":242},[53,11254,11255],{},"properties.put(\"bulk-load\", 4L);\n",[53,11257,11258],{"class":55,"line":273},[53,11259,11260],{},"queueStoreConfig.setProperties(properties);\n",[53,11262,11263],{"class":55,"line":279},[53,11264,11265],{},"queueStoreConfig.setStoreImplementation(new ChronicleMapQueueStore());\n",[53,11267,11268],{"class":55,"line":496},[53,11269,500],{"emptyLinePlaceholder":499},[53,11271,11272],{"class":55,"line":503},[53,11273,11274],{},"// Strings - does work\n",[53,11276,11277],{"class":55,"line":509},[53,11278,11230],{},[53,11280,11281],{"class":55,"line":515},[53,11282,11235],{},[53,11284,11285],{"class":55,"line":521},[53,11286,11287],{},"queueStoreConfig.setProperty(\"binary\", \"false\");\n",[53,11289,11290],{"class":55,"line":527},[53,11291,11292],{},"queueStoreConfig.setProperty(\"memory-limit\", \"0\");\n",[53,11294,11295],{"class":55,"line":533},[53,11296,11297],{},"queueStoreConfig.setProperty(\"bulk-load\", \"4\");\n",[53,11299,11300],{"class":55,"line":539},[53,11301,11265],{},[649,11303,11305],{"id":11304},"anxiety-4-no-transactions-obviously","Anxiety #4: No transactions – obviously",[18,11307,11308],{},"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.",[18,11310,11311],{},"To feel safe about our solution we needed a transactional, central data store. Captain Obvious knocked on the door and\nsaid “Helloooo? Database?”.",[18,11313,11314],{},"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.",[18,11316,11317],{},"So finally we decided to change the QueueStore implementation to persist to a key-value table into our database.",[649,11319,11321],{"id":11320},"wtf-5-its-a-real-bug","WTF #5: It’s a real bug!",[18,11323,11324],{},"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?",[18,11326,11327],{},"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.",[18,11329,11330,11331,11336,11337,11342],{},"I recently opened an ",[585,11332,11335],{"href":11333,"rel":11334},"https://github.com/hazelcast/hazelcast/issues/10621",[589],"issue"," for the bug including a\nsmall ",[585,11338,11341],{"href":11339,"rel":11340},"https://github.com/indyarni/hazelcastbugdemo",[589],"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!",[18,11344,11345],{},"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.",[649,11347,11349],{"id":11348},"_6-going-prod-finally-successful","(╯°□°)╯︵ ┻━┻ #6: Going Prod – finally successful?",[18,11351,11352],{},"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.",[18,11354,11355,11356,11361],{},"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 ",[585,11357,11360],{"href":11358,"rel":11359},"https://github.com/hazelcast/hazelcast/issues/5444",[589],"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 🙂",[2352,11363,11365],{"id":11364},"final-words","Final words",[18,11367,11368],{},"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….",[18,11370,11371],{},"After this Odyssey we can draw some conclusions:",[577,11373,11374,11377,11380,11383],{},[580,11375,11376],{},"Hazelcast is a great tool! It’s fun to work with, the core features work reaaaally well and I would use it again.",[580,11378,11379],{},"However the non-core functionalities (like queue persistence) require some effort to get them working in a complex\nenvironment.",[580,11381,11382],{},"When using a new tool you should read the documentation carefully and try to understand how it really works!",[580,11384,11385],{},"If you really want to understand how Hazelcast works it’s not enough to read the documentation carefully 😉",[607,11387,989],{},{"title":48,"searchDepth":86,"depth":86,"links":11389},[11390,11391,11392,11400],{"id":10996,"depth":86,"text":10997},{"id":11052,"depth":86,"text":11053},{"id":11070,"depth":86,"text":11071,"children":11393},[11394,11395,11396,11397,11398,11399],{"id":11157,"depth":126,"text":11158},{"id":11182,"depth":126,"text":11183},{"id":11211,"depth":126,"text":11212},{"id":11304,"depth":126,"text":11305},{"id":11320,"depth":126,"text":11321},{"id":11348,"depth":126,"text":11349},{"id":11364,"depth":86,"text":11365},[613,614],"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":10984,"description":10993},"blog/the-struggle-with-hazelcast-queue-persistence",[11410,11411,11412,11413],"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":11417,"title":11418,"author":11419,"body":11421,"category":11599,"date":11600,"description":11601,"extension":617,"link":11602,"meta":11603,"navigation":499,"path":11604,"seo":11605,"slug":11425,"stem":11607,"tags":11608,"teaser":11612,"__hash__":11613},"blog/blog/validating-internal-structure-dependencies-using-intellij-idea.md","Validating internal structure / dependencies using IntelliJ IDEA",[11420],"kannegiesser",{"type":11,"value":11422,"toc":11597},[11423,11426,11452,11478,11481,11495,11506,11512,11530,11536,11539,11546,11552,11555,11566,11572,11575,11581,11589],[14,11424,11418],{"id":11425},"validating-internal-structure-dependencies-using-intellij-idea",[18,11427,11428,11429,3038,11434,11439,11440,11445,11446,11451],{},"There are several different tools to maintain the internal structure of a java application available. The tools range\nfrom simple open source software like",[585,11430,11433],{"href":11431,"rel":11432},"https://github.com/clarkware/jdepend",[589],"jdepend",[585,11435,11438],{"href":11436,"rel":11437},"https://web.archive.org/web/20200924121825/http://blog.schauderhaft.de/degraph/",[589],"degraph"," to full fledged\narchitecture tooling like ",[585,11441,11444],{"href":11442,"rel":11443},"http://structure101.com/",[589],"Structure101","\nor ",[585,11447,11450],{"href":11448,"rel":11449},"https://www.hello2morrow.com/products/sonargraph/architect9",[589],"Sonargraph Architect",". All these provide methods to\ndefine the internal structure of an application and validate it somehow.",[18,11453,11454,11455,11460,11461,11466,11467,11472,11473,986],{},"Since we are using ",[585,11456,11459],{"href":11457,"rel":11458},"https://www.jetbrains.com/idea/",[589],"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 ",[585,11462,11465],{"href":11463,"rel":11464},"https://www.jetbrains.com/help/idea/2017.1/scopes.html",[589],"define scopes"," and match your code to them. You can then\nrule how they may or may not access each other using\nthe ",[585,11468,11471],{"href":11469,"rel":11470},"https://www.jetbrains.com/help/idea/2017.1/dependency-viewer.html",[589],"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 ",[585,11474,11477],{"href":11475,"rel":11476},"https://www.jetbrains.com/idea/#chooseYourEdition",[589],"IntelliJ IDEA Community Edition",[18,11479,11480],{},"Imagine we have a simple layered application with Java packages representing these layers:",[577,11482,11483,11486,11489,11492],{},[580,11484,11485],{},"api: API-Layer features controllers that are responsible to render a RESTful API to our application",[580,11487,11488],{},"business: our higher level operations/business logic happens here",[580,11490,11491],{},"persistence: this layer is responsible for storing data and providing access to it",[580,11493,11494],{},"domain: In addition we have a domain “layer” where all the layers share their common domain",[18,11496,11497,11498,11501,11502,11505],{},"We can now define scopes for each of these layers. We can do so using ",[573,11499,11500],{},"File -> Settings"," and then select\n",[573,11503,11504],{},"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.",[18,11507,11508],{},[1773,11509],{"alt":11510,"src":11511},"\"Creating Scopes using Settings\"","https://media.synyx.de/uploads//2017/05/1-creating-sopes.png",[18,11513,11514,11515,11518,11519,11522,11523,11526,11527],{},"We then define archuitectural constraints on these scopes using the ",[573,11516,11517],{},"Analyze Dependencies View"," (accessible using the\n",[573,11520,11521],{},"Analyze"," Menu). We do so by clicking the ",[573,11524,11525],{},"Edit Rules Icon"," in the ",[573,11528,11529],{},"Dependency-View.",[18,11531,11532],{},[1773,11533],{"alt":11534,"src":11535},"\"Defining rules\"","https://media.synyx.de/uploads//2017/05/2-defining-rules.png",[18,11537,11538],{},"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.",[18,11540,11541,11542,11545],{},"When we return to the ",[573,11543,11544],{},"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.",[18,11547,11548],{},[1773,11549],{"alt":11550,"src":11551},"\"Violations view from dependencies\"","https://media.synyx.de/uploads//2017/05/3-violations-in-dependency-view.png",[18,11553,11554],{},"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.",[18,11556,11557,11558,11561,11562,11565],{},"As an alternative we can also access the information by running the analysis ",[573,11559,11560],{},"Illegal package dependencies"," (e.g. using\nthe ",[573,11563,11564],{},"Analyze – > Run inspection by name",") dialog. From there we can also edit our rules and navigate to the violating\ncode.",[18,11567,11568],{},[1773,11569],{"alt":11570,"src":11571},"\"Violations-View from inspections\"","https://media.synyx.de/uploads//2017/05/4-violations-in-inspections-view.png",[18,11573,11574],{},"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.",[18,11576,11577],{},[1773,11578],{"alt":11579,"src":11580},"\"Scope definitions in .idea/scopes\"","https://media.synyx.de/uploads//2017/05/5-export.png",[18,11582,11583,11584,986],{},"To test and experiment with scopes yourself you can build on\nmy",[585,11585,11588],{"href":11586,"rel":11587},"https://github.com/marckanneg/idea-scopes-demo",[589],"my demo-project",[18,11590,11591,11592,11596],{},"You can clone it\nfrom ",[585,11593,11594],{"href":11594,"rel":11595},"https://github.com/marckanneg/idea-scopes-demo.git",[589]," and open\nit using your IntelliJ IDEA(File ->Open).",{"title":48,"searchDepth":86,"depth":86,"links":11598},[],[613],"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":11418,"description":11606},"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",[11609,11610,6503,11611,3494],"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…","nRJYUHMxI944vdsZA69SvSdJ70YYEvHqera1wPQeKgI",{"id":11615,"title":11616,"author":11617,"body":11621,"category":11754,"date":11756,"description":11757,"extension":617,"link":11758,"meta":11759,"navigation":499,"path":11760,"seo":11761,"slug":11762,"stem":11763,"tags":11764,"teaser":11769,"__hash__":11770},"blog/blog/kids-lernen-programmieren-mit-jeder-menge-spass.md","Kids lernen Programmieren – mit jeder Menge Spaß",[11618,11619,11620],"antony","karrasz","schneider",{"type":11,"value":11622,"toc":11752},[11623,11626,11629,11635,11638,11641,11646,11649,11654,11657,11662,11677,11680,11691,11694,11705,11713,11736,11744],[14,11624,11616],{"id":11625},"kids-lernen-programmieren-mit-jeder-menge-spaß",[18,11627,11628],{},"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.",[18,11630,11631],{},[1773,11632],{"alt":11633,"src":11634},"\"devoxx4Kids\"","https://media.synyx.de/uploads//2017/05/IMG_7435.jpg",[18,11636,11637],{},"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.",[18,11639,11640],{},"Wir wiederholen im Frühjahr die Workshops vom Herbst, also stand erneut Jumping Sumo, Cardboard und Lego Mindstorms auf\ndem Programm.",[18,11642,11643],{},[27,11644,11645],{},"Cardboard",[18,11647,11648],{},"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.",[18,11650,11651],{},[27,11652,11653],{},"Lego Mindstorms",[18,11655,11656],{},"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.",[18,11658,11659],{},[27,11660,11661],{},"Jumping Sumo",[18,11663,11664,11665,11670,11671,11676],{},"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 ",[585,11666,11669],{"href":11667,"rel":11668},"https://github.com/Devoxx4KidsDE/drone-controller",[589],"Drone-Controller",", mit Hilfe\nvon ",[585,11672,11675],{"href":11673,"rel":11674},"https://github.com/Devoxx4KidsDE/workshop-jumping-sumo-4-scratch",[589],"Scratch umgesetz",". Der Workshop stand unter dem\nMotto „Mars Mission“. Die Aufgabe war, nach einer",[18,11678,11679],{},"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.",[18,11681,11682,3566,11685,3566,11688],{},[1773,11683],{"alt":48,"src":11684},"https://media.synyx.de/uploads//2017/05/IMG_7534.jpg",[1773,11686],{"alt":48,"src":11687},"https://media.synyx.de/uploads//2017/05/IMG_7645.jpg",[1773,11689],{"alt":48,"src":11690},"https://media.synyx.de/uploads//2017/05/IMG_7525.jpg",[18,11692,11693],{},"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.",[18,11695,11696,3566,11699,3566,11702],{},[1773,11697],{"alt":48,"src":11698},"https://media.synyx.de/uploads//2017/05/IMG_7707.jpg",[1773,11700],{"alt":48,"src":11701},"https://media.synyx.de/uploads//2017/05/IMG_7720.jpg",[1773,11703],{"alt":48,"src":11704},"https://media.synyx.de/uploads//2017/05/IMG_7734.jpg",[18,11706,11707,11708,11712],{},"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 (",[585,11709,11711],{"href":11710},"mailto:info@devoxx4kids.de","info@devoxx4kids.de",") melden. Aber natürlich auch, falls\njemand Unterstützung bei einer Devoxx4Kids in einer anderen Stadt braucht.",[18,11714,11715,11716,99,11721,99,11726,11730,11731,986],{},"Zum Schluss möchten wir uns noch bei den tollen Sponsoren\nbedanken ",[585,11717,11720],{"href":11718,"rel":11719},"https://www.dm.de/arbeiten-und-lernen/arbeiten-bei-uns/filiadata-c534052.html",[589],"FILIADATA GmbH",[585,11722,11725],{"href":11723,"rel":11724},"http://www.emendare.de/",[589],"Emendare",[585,11727,5743],{"href":11728,"rel":11729},"http://www.synyx.de",[589],"\nund der ",[585,11732,11735],{"href":11733,"rel":11734},"http://karlshochschule.de/de/",[589],"Karlshochschule",[18,11737,11738,11739,986],{},"Unser neues Video zur Devoxx4Kids findet ihr auf ",[585,11740,11743],{"href":11741,"rel":11742},"https://youtu.be/JG5yCZr6tus",[589],"YouTube",[18,11745,11746,11747,986],{},"Weitere Fotos gibt es hier in unserer ",[585,11748,11751],{"href":11749,"rel":11750},"http://www.devoxx4kids.de/karlsruhe/galerie/",[589],"Galerie",{"title":48,"searchDepth":86,"depth":86,"links":11753},[],[11755],"devoxx4kids","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":11616,"description":11628},"kids-lernen-programmieren-mit-jeder-menge-spass","blog/kids-lernen-programmieren-mit-jeder-menge-spass",[11765,11755,11766,11767,11768],"cardboard","jumping-sumo","lego-mindstorms","workshop","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…","cBR58cr9dX_G5wzrShIhy_y9p_C-aWuLmIQIytDLvR4",{"id":11772,"title":11773,"author":11774,"body":11775,"category":12063,"date":12064,"description":12065,"extension":617,"link":8523,"meta":12066,"navigation":499,"path":12067,"seo":12068,"slug":11779,"stem":12070,"tags":12071,"teaser":12075,"__hash__":12076},"blog/blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts.md","Urlaubsverwaltung – Die Geschichte eines Open Source Projekts",[8328],{"type":11,"value":11776,"toc":12051},[11777,11780,11787,11801,11804,11807,11811,11814,11821,11834,11838,11848,11858,11862,11869,11873,11890,11903,11916,11920,11938,11946,11950,11961,11964,11968,11975,11979,11986,11990,11999,12003,12009,12018,12025,12029,12032],[14,11778,11773],{"id":11779},"urlaubsverwaltung-die-geschichte-eines-open-source-projekts",[18,11781,11782,11783],{},"Nach fast 6 Jahren hat das Open Source Projekt,\ndie ",[585,11784,11786],{"href":8418,"rel":11785},[589],"synyx Urlaubsverwaltung",[577,11788,11789,11792,11795,11798],{},[580,11790,11791],{},"70 offene Issues",[580,11793,11794],{},"194 gelöste Issues",[580,11796,11797],{},"2207 Commits",[580,11799,11800],{},"99 Releases",[18,11802,11803],{},"Stand heute, 26. April 2017",[18,11805,11806],{},"…und eine kleine Geschichte, die durchaus erzählenswert ist und auch ein bisschen meine eigene Geschichte spiegelt.",[2352,11808,11810],{"id":11809},"die-geburtsstunde-der-urlaubsverwaltung","Die Geburtsstunde der Urlaubsverwaltung",[18,11812,11813],{},"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.",[18,11815,11816,11817,11820],{},"Im Oktober 2011 wurde es dann an der Zeit für ein ",[573,11818,11819],{},"“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.",[18,11822,11823,11824,11827,11828,11833],{},"Am ",[27,11825,11826],{},"20. Oktober 2011"," erfolgte\nder ",[585,11829,11832],{"href":11830,"rel":11831},"https://github.com/synyx/urlaubsverwaltung/commit/d194f12e3d6344495cc73c240f58a6d18bca8b83",[589],"erste Commit"," – dies\nwar die Geburtsstunde der Urlaubsverwaltung.",[2352,11835,11837],{"id":11836},"das-erste-krabbeln","Das erste Krabbeln",[18,11839,11823,11840,11843,11844,11847],{},[27,11841,11842],{},"5. März 2012"," ging die erste Version der Urlaubsverwaltung ",[27,11845,11846],{},"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.",[18,11849,11850,11851,11854,11855,8526],{},"Durch den relativ schnellen Live-Gang mit den grundlegenden Features konnten Bugs früh behoben (",[573,11852,11853],{},"“Oh, an den Fall hab\nich ja beim Testen und Durchklicken gar nicht gedacht”",") und die Benutzbarkeit der Features zügig optimiert werden (\n",[573,11856,11857],{},"“Stimmt, das ist eigentlich ganz schön umständlich und unübersichtlich”",[2352,11859,11861],{"id":11860},"ein-projekt-lernt-laufen","Ein Projekt lernt laufen",[18,11863,11864,11865,11868],{},"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 ",[27,11866,11867],{},"Juni 2012"," trauten wir uns dann endgültig, den *\n*Papierkrieg aufzugeben** und die Urlaubsbeantragung nur noch über die Software durchzuführen.",[2352,11870,11872],{"id":11871},"ein-projekt-zieht-in-die-welt-hinaus","Ein Projekt zieht in die Welt hinaus",[18,11874,11875,11876,11881,11882,11885,11886,11889],{},"Aufgrund eines ",[585,11877,11880],{"href":11878,"rel":11879},"https://synyx.de/2012/11/urlaubsverwaltung-was-hat-sich-getan/",[589],"Blog-Posts"," kamen im ",[27,11883,11884],{},"Juni 2013","\nrelativ zeitgleich die ",[27,11887,11888],{},"ersten Anfragen"," zur Urlaubsverwaltung. Es gab also noch mehr Unternehmen, die dasselbe\nProblem zu lösen hatten.",[18,11891,11892,11893,11896,11897,3566,11900,986],{},"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 ",[27,11894,11895],{},"18. Juli 2013","\nauf ",[585,11898,9390],{"href":8418,"rel":11899},[589],[27,11901,11902],{},"veröffentlicht",[18,11904,11905,11906,3839,11911,8526],{},"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. ",[585,11907,11910],{"href":11908,"rel":11909},"https://synyx.de/2012/06/scheduling-and-asynchronous-execution-with-spring/",[589],"Spring Scheduled",[585,11912,11915],{"href":11913,"rel":11914},"https://synyx.de/2012/05/how-to-monitor-and-manage-your-java-application-with-jmx/",[589],"JMX",[2352,11917,11919],{"id":11918},"umstyling-von-außen-und-von-innen","Umstyling – von außen und von innen",[18,11921,4541,11922,11925,11926,11931,11932,11937],{},[27,11923,11924],{},"Oktober 2014"," stand für synyx der ",[585,11927,11930],{"href":11928,"rel":11929},"https://synyx.de/2014/09/wir-ziehen-um/",[589],"Umzug"," ins neue Büro an. Die Woche,\ndie ich aufgrunddessen im Home Office verbrachte, nutzte ich für\nzahlreiche ",[585,11933,11936],{"href":11934,"rel":11935},"https://synyx.de/2014/10/urlaubsverwaltung-goes-mobile/",[589],"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.",[18,11939,11940,11941,11945],{},"Da wir immer wieder E-Mails mit Fragen bzgl. Installation und Konfiguration erhielten, entschlossen wir uns im *\n*Oktober 2015**, die Urlaubsverwaltung auf ",[585,11942,8936],{"href":11943,"rel":11944},"https://projects.spring.io/spring-boot/",[589]," umzustellen. Dies\nbrachte einige Vereinfachungen in der Konfiguration und erleichterte interessierten Unternehmen die Nutzung der\nUrlaubsverwaltung.",[2352,11947,11949],{"id":11948},"ein-projekt-wird-erwachsen","Ein Projekt wird erwachsen",[18,11951,11952,11953,11956,11957,11960],{},"Ab ",[27,11954,11955],{},"2015"," bekamen wir ",[27,11958,11959],{},"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.",[18,11962,11963],{},"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.",[2352,11965,11967],{"id":11966},"weniger-ist-manchmal-mehr","Weniger ist manchmal mehr",[18,11969,11970,11971,11974],{},"Wir kamen zu dem Punkt, an dem wir uns die Frage stellen mussten: ",[27,11972,11973],{},"Wie soll es mit der Urlaubsverwaltung weitergehen?","\nIn einer Task Force von freiwilligen Interessierten besprachen wir die Möglichkeiten zur weiteren Strategie.",[1217,11976,11978],{"id":11977},"sollen-wir-weiterhin-als-dienstleister-agieren-und-individuelle-wünsche-umsetzen","Sollen wir weiterhin als Dienstleister agieren und individuelle Wünsche umsetzen?",[18,11980,11981,11982,11985],{},"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 ",[573,11983,11984],{},"“Nebenbei-Entwicklung”"," der Urlaubsverwaltung, wie sie bisher zeitgleich zu unserem\neigentlichen Kundengeschäft betrieben wurde, wäre dann realistisch betrachtet nicht mehr sinnvoll machbar.",[1217,11987,11989],{"id":11988},"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?",[18,11991,11992,11993,11998],{},"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 ",[585,11994,11997],{"href":11995,"rel":11996},"https://synyx.de/open-source/",[589],"Open Source"," Projekt betreiben wollen.\n**",[2352,12000,12002],{"id":12001},"ist-das-der-tod-eines-projekts","Ist das der Tod eines Projekts?",[18,12004,12005,12006,986],{},"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: ",[573,12007,12008],{},"back to the roots, weniger ist manchmal mehr",[18,12010,12011,12012,12017],{},"Neue Anfragen leiten wir nun an einen von\nuns ",[585,12013,12016],{"href":12014,"rel":12015},"http://www.andre-janus.de/",[589],"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",[18,12019,12020,12021,12024],{},"Denn das ist ja das Coole an unserer Open Source Urlaubsverwaltung: man kann sie nicht nur ",[573,12022,12023],{},"as it is"," kostenfrei nutzen,\nsondern sie auch für eigene Zwecke modifizieren und diese angepasste Version nutzen.",[649,12026,12028],{"id":12027},"und-meine-geschichte","Und meine Geschichte?",[18,12030,12031],{},"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 🙂",[18,12033,12034,12035,12040,12041,12044,12045,12050],{},"Seitdem ist ",[585,12036,12039],{"href":12037,"rel":12038},"https://github.com/honnel",[589],"Daniel Hammann"," der Hauptansprechpartner für die Urlaubsverwaltung als Open\nSource Projekt von synyx. Auf ",[585,12042,9390],{"href":8418,"rel":12043},[589],"\nbin ",[585,12046,12049],{"href":12047,"rel":12048},"https://github.com/fraulyoner",[589],"ich"," momentan ein eher stiller Beobachter.",{"title":48,"searchDepth":86,"depth":86,"links":12052},[12053,12054,12055,12056,12057,12058,12059,12060],{"id":11809,"depth":86,"text":11810},{"id":11836,"depth":86,"text":11837},{"id":11860,"depth":86,"text":11861},{"id":11871,"depth":86,"text":11872},{"id":11918,"depth":86,"text":11919},{"id":11948,"depth":86,"text":11949},{"id":11966,"depth":86,"text":11967},{"id":12001,"depth":86,"text":12002,"children":12061},[12062],{"id":12027,"depth":126,"text":12028},[614],"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":11773,"description":12069},"Nach fast 6 Jahren hat das Open Source Projekt,\ndie synyx Urlaubsverwaltung","blog/urlaubsverwaltung-die-geschichte-eines-open-source-projekts",[12072,5743,12073,12074],"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":12078,"title":12079,"author":12080,"body":12082,"category":12139,"date":12140,"description":12141,"extension":617,"link":12142,"meta":12143,"navigation":499,"path":12144,"seo":12145,"slug":12086,"stem":12146,"tags":12147,"teaser":12150,"__hash__":12151},"blog/blog/synyx-went-wild.md","synyx went wild!",[12081],"ulrich",{"type":11,"value":12083,"toc":12137},[12084,12087,12090,12093,12098,12101,12106,12109,12112,12117,12120,12125,12128],[14,12085,12079],{"id":12086},"synyx-went-wild",[18,12088,12089],{},"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.",[18,12091,12092],{},"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.",[18,12094,12095],{},[1773,12096],{"alt":48,"src":12097},"https://media.synyx.de/uploads//2017/04/frueh1.jpg",[18,12099,12100],{},"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.",[18,12102,12103],{},[1773,12104],{"alt":48,"src":12105},"https://media.synyx.de/uploads//2017/04/timetable2.jpg",[18,12107,12108],{},"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.",[18,12110,12111],{},"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.",[18,12113,12114],{},[1773,12115],{"alt":48,"src":12116},"https://media.synyx.de/uploads//2017/04/draussen3.jpg",[18,12118,12119],{},"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.",[18,12121,12122],{},[1773,12123],{"alt":48,"src":12124},"https://media.synyx.de/uploads//2017/04/fly4.jpg",[18,12126,12127],{},"So… let‘s go wild again synyx! 😉",[18,12129,12130,12131,12136],{},"Noch mehr synyxCamp gibt es in diesem ",[585,12132,12135],{"href":12133,"rel":12134},"https://youtu.be/TaQ-9muog-E",[589],"Video","!",{"title":48,"searchDepth":86,"depth":86,"links":12138},[],[614],"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":12079,"description":12089},"blog/synyx-went-wild",[8480,7043,12148,12149],"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…","RNVXhW0_TjmfdTf7DhiOlskdXcRDd75bcCpQHOCofVo",{"id":12153,"title":12154,"author":12155,"body":12157,"category":12307,"date":12308,"description":12309,"extension":617,"link":12310,"meta":12311,"navigation":499,"path":12312,"seo":12313,"slug":12161,"stem":12315,"tags":12316,"teaser":12321,"__hash__":12322},"blog/blog/devoxx4kids-javaland4kids.md","Devoxx4Kids @ JavaLand4Kids",[12156,11620],"hammann",{"type":11,"value":12158,"toc":12302},[12159,12162,12189,12193,12201,12204,12209,12218,12225,12230,12233,12235,12244,12255,12259,12266,12269,12277,12280],[14,12160,12154],{"id":12161},"devoxx4kids-javaland4kids",[18,12163,12164,12165,12170,12171,12176,12177,12182,12183,12188],{},"Dieses Jahr waren wir, ",[585,12166,12169],{"href":12167,"rel":12168},"http://www.devoxx4kids.de/",[589],"Devoxx4Kids Deutschland",", wieder als Mentoren mit bei der\nOrganisation und Durchführung\nder ",[585,12172,12175],{"href":12173,"rel":12174},"https://web.archive.org/web/20160420190058/http://www.javaland.eu:80/de/javaland4kids/",[589],"JavaLand4Kids"," dabei. Die\nvon der ",[585,12178,12181],{"href":12179,"rel":12180},"https://www.doag.org/de/home/",[589],"DOAG"," organisierte Veranstaltung findet alljährlich im Rahmen\nder ",[585,12184,12187],{"href":12185,"rel":12186},"https://www.javaland.eu/de/home/",[589],"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.",[2352,12190,12192],{"id":12191},"jumping-sumo-4-scratch","Jumping Sumo 4 Scratch",[18,12194,12195,12196,12200],{},"Wir hatten dieses Jahr die Möglichkeit die 30 Kinder mit\nunserem ",[585,12197,12199],{"href":11673,"rel":12198},[589],"Jumping Sumo 4 Scratch Workshop"," spielerisch\nan die Informatik heranzuführen.",[18,12202,12203],{},"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.",[18,12205,12206],{},[1773,12207],{"alt":48,"src":12208},"https://media.synyx.de/uploads//2017/03/IMG_20170327_091100.jpg",[18,12210,12211,12212,12217],{},"Die Steuerung bei diesem Workshop basiert auf der graphischen Programmierung\nvia ",[585,12213,12216],{"href":12214,"rel":12215},"https://scratch.mit.edu/",[589],"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.",[18,12219,12220,12221,12224],{},"Die Kommunikation zwischen dem zur Programmierung verwendeten Computer und dem Jumping Sumo wird über WLAN und\nden ",[585,12222,11669],{"href":11667,"rel":12223},[589]," 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.",[18,12226,12227],{},[1773,12228],{"alt":48,"src":12229},"https://media.synyx.de/uploads//2017/03/2017-03-27-12.21.13-e1490863119907.jpg",[18,12231,12232],{},"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.",[2352,12234,11653],{"id":11767},[18,12236,12237,12238,12243],{},"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 ",[585,12239,12242],{"href":12240,"rel":12241},"https://www.ald.softbankrobotics.com/en/cool-robots/pepper",[589],"Pepper"," zur\nSeite.",[18,12245,12246,3566,12249,3566,12252],{},[1773,12247],{"alt":48,"src":12248},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.33.47-e1490863662532.jpg",[1773,12250],{"alt":48,"src":12251},"https://media.synyx.de/uploads//2017/03/IMG_20170327_140827664.jpg",[1773,12253],{"alt":48,"src":12254},"https://media.synyx.de/uploads//2017/03/IMG_20170327_113718.jpg",[2352,12256,12258],{"id":12257},"chibitronics","Chibitronics",[18,12260,4541,12261,12265],{},[585,12262,12258],{"href":12263,"rel":12264},"https://chibitronics.com/",[589]," 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.",[18,12267,12268],{},"Die Kinder erhielten die Aufgabe Osterkarten zu basteln und die Nase des Hasen zum Leuchten zu bringen.",[18,12270,12271,3566,12274],{},[1773,12272],{"alt":48,"src":12273},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.53.02-e1490863318470.jpg",[1773,12275],{"alt":48,"src":12276},"https://media.synyx.de/uploads//2017/03/2017-03-27-13.48.57-e1490863304407.jpg",[18,12278,12279],{},"Ein großer Dank geht an das Organisationsteam rund um Simone Fischer die dieser Devoxx4Kids auf der Javaland4Kids einen\ntollen Rahmen gegeben haben.",[18,12281,12282,12283,12286,12287,12290,12291,12296,12297,986],{},"Die nächste Devoxx4Kids werden in ",[27,12284,12285],{},"Wiesbaden am 22.4.2017"," und in ",[27,12288,12289],{},"Karlsruhe am 06.05.2017"," stattfinden. Alle\nInformationen dazu gibt es auf ",[585,12292,12295],{"href":12293,"rel":12294},"http://www.Devoxx4Kids.de",[589],"Devoxx4Kids.de"," und\nüber ",[585,12298,12301],{"href":12299,"rel":12300},"https://twitter.com/devoxx4kidsde",[589],"Twitter",{"title":48,"searchDepth":86,"depth":86,"links":12303},[12304,12305,12306],{"id":12191,"depth":86,"text":12192},{"id":11767,"depth":86,"text":11653},{"id":12257,"depth":86,"text":12258},[11755],"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":12154,"description":12314},"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",[12257,11755,4810,12317,11766,4231,12318,12319,12320],"javaland4kids","lego","pepper","scratch","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…","LCuMhPe96plS0FrZjy8wDfCisfe74mKaQzsJzBWwXJw",{"id":12324,"title":12325,"author":12326,"body":12328,"category":12559,"date":12560,"description":12561,"extension":617,"link":12562,"meta":12563,"navigation":499,"path":12564,"seo":12565,"slug":12332,"stem":12566,"tags":12567,"teaser":12575,"__hash__":12576},"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",[12327],"posch",{"type":11,"value":12329,"toc":12557},[12330,12333,12336,12339,12342,12345,12348,12353,12359,12362,12373,12376,12381,12384,12389,12392,12397,12403,12406,12409,12412,12417,12420,12423,12428,12431,12437,12440,12443,12446,12449,12452,12457,12460,12463,12477,12480,12483,12488,12491,12496,12499,12504,12507,12510,12513,12516,12519,12536,12541,12549],[14,12331,12325],{"id":12332},"visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana",[18,12334,12335],{},"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.",[18,12337,12338],{},"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.",[18,12340,12341],{},"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.",[18,12343,12344],{},"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.",[18,12346,12347],{},"The resulting dashboard looks like this:",[18,12349,12350],{},[1773,12351],{"alt":48,"src":12352},"https://media.synyx.de/uploads//2017/03/iot_grafana_dashboard.png",[18,12354,12355],{},[585,12356,12357],{"href":12357,"rel":12358},"https://snapshot.raintank.io/dashboard/snapshot/Ept58LQH5U8sRSW7LP9hl17Cajy0T7i4",[589],[18,12360,12361],{},"It shows the data from a total of four ESP8266-based nodes:",[577,12363,12364,12367,12370],{},[580,12365,12366],{},"One with a single DHT-22 temperature/humidity sensor.",[580,12368,12369],{},"One with a DHT-22 and MH-Z14 CO2 sensor.",[580,12371,12372],{},"Two connected to a single coffee machine each.",[18,12374,12375],{},"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 🙂",[18,12377,12378],{},[27,12379,12380],{},"Infrastructure",[18,12382,12383],{},"Visualised the infrastructure we created looks like this:",[18,12385,12386],{},[1773,12387],{"alt":48,"src":12388},"https://media.synyx.de/uploads//2017/03/iot_infra.png",[18,12390,12391],{},"Central to everything is the Mosquitto MQTT broker. It facilitates communication between the ESP8266 nodes, C&C and\nInfluxDB.",[18,12393,12394],{},[27,12395,12396],{},"The ESP8266 nodes",[18,12398,12399,12400,12402],{},"For the firmware of the ESP8266 (ESP-12E-based NodeMCU) boards we use C++ and the Sming ",[53,12401,7598],{}," 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.",[18,12404,12405],{},"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.",[18,12407,12408],{},"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.",[18,12410,12411],{},"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.",[18,12413,12414],{},[27,12415,12416],{},"Sensors",[18,12418,12419],{},"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.",[18,12421,12422],{},"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).",[18,12424,12425],{},[27,12426,12427],{},"Coffee machine",[18,12429,12430],{},"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.",[18,12432,12433,12434,12436],{},"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 ",[53,12435,7607],{}," 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.",[18,12438,12439],{},"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.",[18,12441,12442],{},"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.",[18,12444,12445],{},"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.",[18,12447,12448],{},"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.",[18,12450,12451],{},"Decoded, the response looks like this:",[18,12453,12454],{},[50,12455,12456],{},"rt:008D0001054E0000000F00390000009300290000162500000000000000000000",[18,12458,12459],{},"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.",[18,12461,12462],{},"So far we confirmed these counters across our coffee machines:",[3525,12464,12465,12468,12471,12474],{},[580,12466,12467],{},"espresso",[580,12469,12470],{},"2x espresso",[580,12472,12473],{},"coffee",[580,12475,12476],{},"2x coffee",[18,12478,12479],{},"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.",[18,12481,12482],{},"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.",[18,12484,12485],{},[27,12486,12487],{},"LEDs",[18,12489,12490],{},"We’re looking at connecting various types of LED lighting to centrally control them (colour, intensity, etc.), including\nWS2812b RGB LEDs and kin.",[18,12492,12493],{},[27,12494,12495],{},"Grafana",[18,12497,12498],{},"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.",[18,12500,12501],{},[27,12502,12503],{},"Next steps",[18,12505,12506],{},"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.",[18,12508,12509],{},"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.",[18,12511,12512],{},"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.",[18,12514,12515],{},"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.",[18,12517,12518],{},"Beyond all of this the main goal of the project remains to improve comfort and fun levels for everyone 🙂",[18,12520,12521,3566,12524,3566,12527,3566,12530,3566,12533],{},[1773,12522],{"alt":48,"src":12523},"https://media.synyx.de/uploads//2017/03/DSC02242_04.jpg",[1773,12525],{"alt":48,"src":12526},"https://media.synyx.de/uploads//2017/03/DSC02245_02.jpg",[1773,12528],{"alt":48,"src":12529},"https://media.synyx.de/uploads//2017/03/DSC02244.jpg",[1773,12531],{"alt":48,"src":12532},"https://media.synyx.de/uploads//2017/03/DSC02248_02.jpg",[1773,12534],{"alt":48,"src":12535},"https://media.synyx.de/uploads//2017/03/DSC02243.jpg",[18,12537,12538],{},[27,12539,12540],{},"References",[18,12542,12543,3566,12545],{},[53,12544,7598],{},[585,12546,12547],{"href":12547,"rel":12548},"https://github.com/SmingHub/Sming",[589],[18,12550,12551,3566,12553],{},[53,12552,7607],{},[585,12554,12555],{"href":12555,"rel":12556},"http://protocoljura.wiki-site.com/index.php/Hauptseite",[589],{"title":48,"searchDepth":86,"depth":86,"links":12558},[],[613],"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":12325,"description":12335},"blog/visualising-sensors-and-coffee-machines-with-esp8266-mqtt-influxdb-and-grafana",[12568,12569,12570,12571,12572,12573,12574],"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…","8lgpb9e8mFlDVD9UIjRM-_4nIl-Ac-bgPTlLtBkpy4A",{"id":12578,"title":12579,"author":12580,"body":12583,"category":12783,"date":12784,"description":12785,"extension":617,"link":12786,"meta":12787,"navigation":499,"path":12788,"seo":12789,"slug":12587,"stem":12791,"tags":12792,"teaser":12796,"__hash__":12797},"blog/blog/axon-3-event-replaying.md","Axon 3: Event Replaying",[12581,12582],"messner","thieme",{"type":11,"value":12584,"toc":12769},[12585,12588,12602,12606,12613,12616,12620,12623,12627,12630,12634,12641,12645,12648,12656,12661,12664,12673,12683,12718,12721,12728,12737,12741,12744,12748,12751,12755,12758,12760,12764,12767],[14,12586,12579],{"id":12587},"axon-3-event-replaying",[18,12589,12590,12595,12596,12601],{},[585,12591,12594],{"href":12592,"rel":12593},"http://www.axonframework.org/",[589],"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 ",[585,12597,12600],{"href":12598,"rel":12599},"https://msdn.microsoft.com/en-us/library/jj591577.aspx",[589],"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.",[2352,12603,12605],{"id":12604},"event-replaying-what-and-why","Event Replaying: what and why?",[18,12607,12608,12609,12612],{},"Besides rebuilding current application state from the stored events we can employ a technique called ",[27,12610,12611],{},"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.",[18,12614,12615],{},"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.",[649,12617,12619],{"id":12618},"adding-new-read-models","Adding new read models",[18,12621,12622],{},"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.",[649,12624,12626],{"id":12625},"removing-inconsistencies-from-existing-read-models","Removing inconsistencies from existing read models",[18,12628,12629],{},"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.",[649,12631,12633],{"id":12632},"debugging-purposes","Debugging purposes",[18,12635,12636,12637,12640],{},"Sometimes you want to know exactly ",[573,12638,12639],{},"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.",[2352,12642,12644],{"id":12643},"how","How?",[18,12646,12647],{},"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.",[18,12649,12650,12651,2304],{},"There are two types of event processors: Subscribing Event Processors and Tracking Event Processors. From\nthe ",[585,12652,12655],{"href":12653,"rel":12654},"https://docs.axonframework.org/v/3.0/part3/event-processing.html",[589],"Axon documentation",[5221,12657,12658],{},[18,12659,12660],{},"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.",[18,12662,12663],{},"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):",[43,12665,12667],{"className":1980,"code":12666,"language":1982,"meta":48,"style":48},"eventHandlingConfiguration.usingTrackingProcessors();\n",[50,12668,12669],{"__ignoreMap":48},[53,12670,12671],{"class":55,"line":56},[53,12672,12666],{},[18,12674,12675,12676,12679,12680,2304],{},"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 ",[50,12677,12678],{},"TokenEntry"," available. You have to tell your JPA provider where it is\nlocated. In Spring Boot this can be done with ",[50,12681,12682],{},"@EntityScan",[43,12684,12686],{"className":1980,"code":12685,"language":1982,"meta":48,"style":48}," @EntityScan(\n basePackages = {\n ...,\n \"org.axonframework.eventhandling.tokenstore.jpa\"\n }\n )\n",[50,12687,12688,12693,12698,12703,12708,12713],{"__ignoreMap":48},[53,12689,12690],{"class":55,"line":56},[53,12691,12692],{}," @EntityScan(\n",[53,12694,12695],{"class":55,"line":86},[53,12696,12697],{}," basePackages = {\n",[53,12699,12700],{"class":55,"line":126},[53,12701,12702],{}," ...,\n",[53,12704,12705],{"class":55,"line":163},[53,12706,12707],{}," \"org.axonframework.eventhandling.tokenstore.jpa\"\n",[53,12709,12710],{"class":55,"line":186},[53,12711,12712],{}," }\n",[53,12714,12715],{"class":55,"line":221},[53,12716,12717],{}," )\n",[18,12719,12720],{},"This is everything needed to configure your application to be able to do event replaying.",[18,12722,12723,12724,12727],{},"In order to trigger an event replay in production you just need to delete the tracking tokens associated with an event\nprocessor from the",[50,12725,12726],{},"token_entry"," table. The corresponding event processor then pulls all events from the event store.",[18,12729,12730,12731,12736],{},"This is a really simple configuration. It’s also possible to configure seperate tracking event processors for different\nreplaying use cases. See\nthe ",[585,12732,12735],{"href":12733,"rel":12734},"http://www.axonframework.org/apidocs/3.0/org/axonframework/config/EventHandlingConfiguration.html",[589],"EventHandlingConfiguration","\nclass for more detail.",[2352,12738,12740],{"id":12739},"common-pitfalls","Common pitfalls",[18,12742,12743],{},"Event replaying is a very useful technique but you have to be aware of some common pitfalls.",[649,12745,12747],{"id":12746},"replaying-events-to-external-services","Replaying events to external services",[18,12749,12750],{},"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.",[649,12752,12754],{"id":12753},"eventual-consistency","Eventual Consistency",[18,12756,12757],{},"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.",[649,12759],{"id":48},[2352,12761,12763],{"id":12762},"conclusion","Conclusion",[18,12765,12766],{},"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.",[607,12768,989],{},{"title":48,"searchDepth":86,"depth":86,"links":12770},[12771,12776,12777,12782],{"id":12604,"depth":86,"text":12605,"children":12772},[12773,12774,12775],{"id":12618,"depth":126,"text":12619},{"id":12625,"depth":126,"text":12626},{"id":12632,"depth":126,"text":12633},{"id":12643,"depth":86,"text":12644},{"id":12739,"depth":86,"text":12740,"children":12778},[12779,12780,12781],{"id":12746,"depth":126,"text":12747},{"id":12753,"depth":126,"text":12754},{"id":48,"depth":126,"text":48},{"id":12762,"depth":86,"text":12763},[613],"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":12579,"description":12790},"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",[12793,12794,12795,290],"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":12799,"title":12800,"author":12801,"body":12802,"category":12845,"date":12846,"description":12847,"extension":617,"link":12848,"meta":12849,"navigation":499,"path":12850,"seo":12851,"slug":12852,"stem":12853,"tags":12854,"teaser":12855,"__hash__":12856},"blog/blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung.md","Kommunikationsgrenzen überschreiten durch Visualisierung",[11620],{"type":11,"value":12803,"toc":12843},[12804,12807,12810,12813,12818,12821,12832,12835,12840],[14,12805,12800],{"id":12806},"kommunikationsgrenzen-überschreiten-durch-visualisierung",[18,12808,12809],{},"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.",[18,12811,12812],{},"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.",[18,12814,12815],{},[1773,12816],{"alt":48,"src":12817},"https://media.synyx.de/uploads//2017/01/IMG_6110.jpg",[18,12819,12820],{},"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.",[18,12822,12823,3566,12826,3566,12829],{},[1773,12824],{"alt":48,"src":12825},"https://media.synyx.de/uploads//2017/01/IMG_6103.jpg",[1773,12827],{"alt":48,"src":12828},"https://media.synyx.de/uploads//2017/01/IMG_6093.jpg",[1773,12830],{"alt":48,"src":12831},"https://media.synyx.de/uploads//2017/01/IMG_6100.jpg",[18,12833,12834],{},"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.",[18,12836,12837],{},[1773,12838],{"alt":48,"src":12839},"https://media.synyx.de/uploads//2017/01/IMG_6073.jpg",[18,12841,12842],{},"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":48,"searchDepth":86,"depth":86,"links":12844},[],[613],"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":12800,"description":12809},"kommunikationsgrenzen-ueberschreiten-durch-visualisierung","blog/kommunikationsgrenzen-ueberschreiten-durch-visualisierung",[6608,12318,4232],"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…","gFh4am_7Ll9-fh-N5qyc2iAk6dzBY5KUFQctF0PEw08",{"id":12858,"title":12859,"author":12860,"body":12862,"category":13093,"date":13094,"description":13095,"extension":617,"link":13096,"meta":13097,"navigation":499,"path":13098,"seo":13099,"slug":12866,"stem":13101,"tags":13102,"teaser":13108,"__hash__":13109},"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",[12861],"knell",{"type":11,"value":12863,"toc":13088},[12864,12867,12876,12879,12883,12886,12889,12996,12999,13003,13006,13012,13016,13019,13080,13083,13086],[14,12865,12859],{"id":12866},"android-installing-multiple-variations-of-the-same-app-on-one-device",[18,12868,12869,12870,12875],{},"This post is bit of a follow up to my last post on how\nto ",[585,12871,12874],{"href":12872,"rel":12873},"https://synyx.de/2016/08/android-building-apks-for-different-environments-using-build-types-and-product-flavors/",[589],"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.",[18,12877,12878],{},"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?",[2352,12880,12882],{"id":12881},"modifiying-the-applicationid","Modifiying the applicationId",[18,12884,12885],{},"To install the app multiple times, you have to provide a unique applicationId to every instance you want to install.",[18,12887,12888],{},"You can do that with the ‘applicationIdSuffix’ property in the buildTypes:",[43,12890,12894],{"className":12891,"code":12892,"language":12893,"meta":48,"style":48},"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",[50,12895,12896,12901,12906,12911,12916,12920,12925,12930,12934,12938,12943,12948,12952,12956,12961,12966,12970,12974,12979,12984,12988,12992],{"__ignoreMap":48},[53,12897,12898],{"class":55,"line":56},[53,12899,12900],{}," buildTypes {\n",[53,12902,12903],{"class":55,"line":86},[53,12904,12905],{}," debug{\n",[53,12907,12908],{"class":55,"line":126},[53,12909,12910],{}," applicationIdSuffix \".debug\";\n",[53,12912,12913],{"class":55,"line":163},[53,12914,12915],{}," ...\n",[53,12917,12918],{"class":55,"line":186},[53,12919,12712],{},[53,12921,12922],{"class":55,"line":221},[53,12923,12924],{}," local{\n",[53,12926,12927],{"class":55,"line":242},[53,12928,12929],{}," applicationIdSuffix \".local\";\n",[53,12931,12932],{"class":55,"line":273},[53,12933,12915],{},[53,12935,12936],{"class":55,"line":279},[53,12937,12712],{},[53,12939,12940],{"class":55,"line":496},[53,12941,12942],{}," tst{ // build types may not start with 'test'\n",[53,12944,12945],{"class":55,"line":503},[53,12946,12947],{}," applicationIdSuffix \".test\";\n",[53,12949,12950],{"class":55,"line":509},[53,12951,12915],{},[53,12953,12954],{"class":55,"line":515},[53,12955,12712],{},[53,12957,12958],{"class":55,"line":521},[53,12959,12960],{}," stage{\n",[53,12962,12963],{"class":55,"line":527},[53,12964,12965],{}," applicationIdSuffix \".stage\";\n",[53,12967,12968],{"class":55,"line":533},[53,12969,12915],{},[53,12971,12972],{"class":55,"line":539},[53,12973,12712],{},[53,12975,12976],{"class":55,"line":545},[53,12977,12978],{}," release {\n",[53,12980,12981],{"class":55,"line":2070},[53,12982,12983],{}," // for releases we want to keep the original applicationId\n",[53,12985,12986],{"class":55,"line":2075},[53,12987,12915],{},[53,12989,12990],{"class":55,"line":2081},[53,12991,12712],{},[53,12993,12994],{"class":55,"line":2087},[53,12995,860],{},[18,12997,12998],{},"Apply the changes, and the different buildTypes APKs may now co-exist on a device.",[2352,13000,13002],{"id":13001},"which-is-which","Which is which?",[18,13004,13005],{},"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?",[18,13007,13008],{},[1773,13009],{"alt":13010,"src":13011},"demo1","https://media.synyx.de/uploads//2016/08/demo1-300x133.png",[2352,13013,13015],{"id":13014},"setting-the-activity-titles","Setting the Activity titles",[18,13017,13018],{},"To ensure we can also distinguish the different environments inside the app, we can append the environment from the\nBuildConfig to our Activity titles:",[43,13020,13022],{"className":288,"code":13021,"language":290,"meta":48,"style":48},"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",[50,13023,13024,13029,13034,13039,13044,13049,13053,13058,13063,13068,13072,13076],{"__ignoreMap":48},[53,13025,13026],{"class":55,"line":56},[53,13027,13028],{},"public class DemoActivity extends AppCompatActivity {\n",[53,13030,13031],{"class":55,"line":86},[53,13032,13033],{}," @Override\n",[53,13035,13036],{"class":55,"line":126},[53,13037,13038],{}," protected void onCreate(Bundle savedInstanceState) {\n",[53,13040,13041],{"class":55,"line":163},[53,13042,13043],{}," super.onCreate(savedInstanceState);\n",[53,13045,13046],{"class":55,"line":186},[53,13047,13048],{}," updateTitle();\n",[53,13050,13051],{"class":55,"line":221},[53,13052,860],{},[53,13054,13055],{"class":55,"line":242},[53,13056,13057],{}," private void updateTitle() {\n",[53,13059,13060],{"class":55,"line":273},[53,13061,13062],{}," if(!\"PROD\".equals(BuildConfig.ENVIRONMENT)){\n",[53,13064,13065],{"class":55,"line":279},[53,13066,13067],{}," setTitle(BuildConfig.ENVIRONMENT + \" \" + getTitle());\n",[53,13069,13070],{"class":55,"line":496},[53,13071,12712],{},[53,13073,13074],{"class":55,"line":503},[53,13075,860],{},[53,13077,13078],{"class":55,"line":509},[53,13079,282],{},[18,13081,13082],{},"Maybe you’ll want to write the code for updating the title a bit cleaner, but that’s it for the example 😉",[18,13084,13085],{},"Please let me hear your opinion about this approach in the comments!",[607,13087,989],{},{"title":48,"searchDepth":86,"depth":86,"links":13089},[13090,13091,13092],{"id":12881,"depth":86,"text":12882},{"id":13001,"depth":86,"text":13002},{"id":13014,"depth":86,"text":13015},[614],"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":12859,"description":13100},"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",[4526,13103,13104,13105,13106,13107],"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":13111,"title":13112,"author":13113,"body":13115,"category":13180,"date":13181,"description":13182,"extension":617,"link":13183,"meta":13184,"navigation":499,"path":13185,"seo":13186,"slug":13119,"stem":13187,"tags":13188,"teaser":13196,"__hash__":13197},"blog/blog/transformation-oder-neuaufbau.md","Transformation oder Neuaufbau?",[13114],"rueckert",{"type":11,"value":13116,"toc":13178},[13117,13120,13123,13126,13133,13136,13139,13146,13149,13152,13155,13162,13165,13172,13175],[14,13118,13112],{"id":13119},"transformation-oder-neuaufbau",[18,13121,13122],{},"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.",[18,13124,13125],{},"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.",[5221,13127,13128],{},[18,13129,13130],{},[573,13131,13132],{},"„Wir müssen uns komplett neu erfinden, zu einem agilen, flexiblen und innovativen Unternehmen transformieren!“.",[18,13134,13135],{},"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.",[18,13137,13138],{},"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.",[5221,13140,13141],{},[18,13142,13143],{},[573,13144,13145],{},"Der Abbau der organisatorischen und prozessualen Schuld ist nicht so einfach durch Zeit und Geld zu erschlagen.",[18,13147,13148],{},"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.",[18,13150,13151],{},"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.",[18,13153,13154],{},"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?",[5221,13156,13157],{},[18,13158,13159],{},[573,13160,13161],{},"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.",[18,13163,13164],{},"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.",[5221,13166,13167],{},[18,13168,13169],{},[573,13170,13171],{},"Das bestehende Unternehmen dient dem Neuen als Investor, und sorgt für die notwendige Liquidität.",[18,13173,13174],{},"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.",[18,13176,13177],{},"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":48,"searchDepth":86,"depth":86,"links":13179},[],[614],"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":13112,"description":13122},"blog/transformation-oder-neuaufbau",[13189,13190,5378,13191,13192,13193,13194,13195],"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":13199,"title":13200,"author":13201,"body":13203,"category":13289,"date":13290,"description":13291,"extension":617,"link":13292,"meta":13293,"navigation":499,"path":13294,"seo":13295,"slug":13207,"stem":13296,"tags":13297,"teaser":13301,"__hash__":13302},"blog/blog/agiles-arbeiten-als-dienstleister.md","Agiles Arbeiten als Dienstleister",[13202],"eifler",{"type":11,"value":13204,"toc":13287},[13205,13208,13211,13214,13219,13222,13227,13230,13235,13238,13243,13246,13249,13252,13255,13266,13269,13272,13275,13278,13281,13284],[14,13206,13200],{"id":13207},"agiles-arbeiten-als-dienstleister",[18,13209,13210],{},"“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.",[18,13212,13213],{},"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:",[3525,13215,13216],{},[580,13217,13218],{},"Kein Verständnis für Intrinsische Motivation",[18,13220,13221],{},"Beispiel: Kunde hat keine Produktvision und beharrt auf Auftragserfüllung.",[3525,13223,13224],{"start":86},[580,13225,13226],{},"Interessenkonflikte beim Dienstleister",[18,13228,13229],{},"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.",[3525,13231,13232],{"start":126},[580,13233,13234],{},"Entscheidungskompetenz, Kontrolle & versteckte Hierachie",[18,13236,13237],{},"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.",[3525,13239,13240],{"start":163},[580,13241,13242],{},"Crossfunktionalität – Zugriff/Verfügbarkeit von Fachexperten",[18,13244,13245],{},"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.",[18,13247,13248],{},"5.Wertekonflikt zwischen Dienstleister und Kunde",[18,13250,13251],{},"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.",[18,13253,13254],{},"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:",[3525,13256,13257,13260,13263],{},[580,13258,13259],{},"Woher kommt die Motivation im Scrum Team beim Dienstleister?",[580,13261,13262],{},"Wieviel Entscheidungsfreiheit ist notwendig um eine gesunde Balance zwischen Selbstorganisation und Erfüllung des\nKundenwunsches zu gewährleisten.",[580,13264,13265],{},"Können Werte aus zwei verschiedenen Welten vereint werden und wenn ja, was braucht es dafür?",[18,13267,13268],{},"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.",[18,13270,13271],{},"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.",[18,13273,13274],{},"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.",[18,13276,13277],{},"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.",[18,13279,13280],{},"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.",[18,13282,13283],{},"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.",[18,13285,13286],{},"Dann werden wir zwar immernoch viele Herausforderungen haben, aber dann ist aus meiner Sicht agiles Arbeiten auch im\nDienstleistungskontext möglich.",{"title":48,"searchDepth":86,"depth":86,"links":13288},[],[614],"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":13200,"description":13210},"blog/agiles-arbeiten-als-dienstleister",[4221,13298,13299,4232,13300],"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…","l1wvT4in5Te0QxLIzl6ygt2p8i5_SHBWD8-dUKrm0M8",{"id":13304,"title":13305,"author":13306,"body":13307,"category":14771,"date":14772,"description":14773,"extension":617,"link":14774,"meta":14775,"navigation":499,"path":14776,"seo":14777,"slug":13311,"stem":14779,"tags":14780,"teaser":14783,"__hash__":14784},"blog/blog/javascript-code-refactoring-automatisieren.md","JavaScript Code Refactoring automatisieren",[5191],{"type":11,"value":13308,"toc":14762},[13309,13312,13320,13340,13346,13350,13359,13366,13369,13385,13389,13392,13397,13405,13410,13413,13418,13421,13424,13432,13435,13439,13442,13459,13462,13466,13469,13479,13572,13575,13602,13605,13660,13662,13665,13696,13699,13702,13800,13840,13843,13857,13860,13885,13891,13937,13949,14049,14068,14080,14087,14139,14142,14156,14159,14173,14176,14179,14182,14202,14212,14345,14353,14358,14485,14488,14510,14516,14617,14620,14623,14635,14638,14652,14655,14659,14668,14671,14688,14732,14734,14737,14751,14756,14759],[14,13310,13305],{"id":13311},"javascript-code-refactoring-automatisieren",[18,13313,13314,13315,13319],{},"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 ",[585,13316,13317],{"href":13317,"rel":13318},"http://jasmine.github.io/2.0/upgrading.html",[589]," wurde super\nbeschrieben was für Änderungen man genau machen muss beim Umstieg von Jasmine 1.x auf 2.x.",[18,13321,13322,13323,1628,13326,13329,13330,13333,13334,13339],{},"In diesem Artikel möchte ich zeigen, wie ich die Transformation der ",[50,13324,13325],{},"runs",[50,13327,13328],{},"waitsFor"," Blöcke zum neuen ",[50,13331,13332],{},"done","\nCallback Muster mittels ",[585,13335,13338],{"href":13336,"rel":13337},"https://github.com/facebook/jscodeshift",[589],"jscodeshift"," automatisiert habe.",[18,13341,13342],{},[1773,13343],{"alt":13344,"src":13345},"jasmine_async_vergleich","https://media.synyx.de/uploads//2016/08/jasmine_async_vergleich-1024x469.png",[2352,13347,13349],{"id":13348},"jscodeshift-recast-esprima-codemods","jscodeshift / recast / esprima / codemods",[18,13351,13352,13353,13358],{},"Jscodeshift ist ein Werkzeug dass von Facebook gebaut wurde und ",[585,13354,13357],{"href":13355,"rel":13356},"https://github.com/benjamn/recast",[589],"recast"," erweitert.\nDieses wiederum arbeitet mit dem Esprima Parser. Dieser baut einen abstrakten Syntaxbaum (engl. abstract source tree,\nAST) auf, der traversiert werden kann.",[18,13360,13361,13362,13365],{},"Mit jscodeshift ist es z. B. möglich, alle anonyme Funktionen herauszuholen und mit einem Namen ",[573,13363,13364],{},"ichBinNichtMehrAnonym","\nzu ersetzen. Ein weiteres, nettes Feature wie ich finde, ist die Beibehaltung der originalen Code Formatierung (so weit\nmöglich).",[18,13367,13368],{},"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 🙂",[577,13370,13371,13378],{},[580,13372,13373],{},[585,13374,13377],{"href":13375,"rel":13376},"https://vramana.github.io/blog/2015/12/21/codemod-tutorial/",[589],"How to write a codemod",[580,13379,13380],{},[585,13381,13384],{"href":13382,"rel":13383},"https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb",[589],"Effective JavaScript Codemods",[2352,13386,13388],{"id":13387},"automatisieren-von-code-refactorings","Automatisieren von Code Refactorings",[18,13390,13391],{},"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.",[18,13393,13394],{},[27,13395,13396],{},"Spaß",[18,13398,13399,13400,13404],{},"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 ",[13401,13402,13403],"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!",[18,13406,13407],{},[27,13408,13409],{},"Zuverlässigkeit",[18,13411,13412],{},"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.",[18,13414,13415],{},[27,13416,13417],{},"Effektivität",[18,13419,13420],{},"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.",[18,13422,13423],{},"Eine Regex zum",[577,13425,13426,13429],{},[580,13427,13428],{},"löschen von Abschnitten wenn Bedingung A zutrifft",[580,13430,13431],{},"verschieben von Code Blöcken",[18,13433,13434],{},"kann entweder einmal geschrieben und nie wieder verstanden werden, oder ist gar unmöglich zu schreiben. Hier kommt\njscodeshift mit codemods zur Rettung.",[2352,13436,13438],{"id":13437},"codemods-zum-upgrade-von-jasmine-1x-auf-2x","Codemods zum Upgrade von Jasmine 1.x auf 2.x",[18,13440,13441],{},"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:",[577,13443,13444,13447,13450,13453,13456],{},[580,13445,13446],{},"spies",[580,13448,13449],{},"asynchrone Tests",[580,13451,13452],{},"expectations",[580,13454,13455],{},"custom matchers (falls vorhanden)",[580,13457,13458],{},"clock",[18,13460,13461],{},"Wir wenden uns folgend den asynchronen Tests zu.",[649,13463,13465],{"id":13464},"asynchrone-tests-transformieren","Asynchrone Tests transformieren",[18,13467,13468],{},"Das Projekt das es zu refactored galt hatte überwiegend sehr einfach geschriebene asynchrone Tests. Perfekt für den\nEinstieg in jscodeshift.",[18,13470,13471,13472,13475,13476,13478],{},"Eine Variable die initial ",[50,13473,13474],{},"false"," ist und nach $Aktion auf ",[50,13477,408],{}," gesetzt wird. Die Assertions werden dann erst\nausgeführt, wenn die Variable gesetzt wurde.",[43,13480,13484],{"className":13481,"code":13482,"language":13483,"meta":48,"style":48},"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",[50,13485,13486,13503,13511,13525,13530,13541,13546,13557,13564,13568],{"__ignoreMap":48},[53,13487,13488,13491,13493,13496,13498,13500],{"class":55,"line":56},[53,13489,13490],{"class":59},"it",[53,13492,1067],{"class":82},[53,13494,13495],{"class":63},"\"tests something async\"",[53,13497,99],{"class":82},[53,13499,5789],{"class":389},[53,13501,13502],{"class":82}," () {\n",[53,13504,13505,13508],{"class":55,"line":86},[53,13506,13507],{"class":389}," var",[53,13509,13510],{"class":82}," done;\n",[53,13512,13513,13516,13518,13520,13523],{"class":55,"line":126},[53,13514,13515],{"class":59}," doSomethingAsync",[53,13517,1067],{"class":82},[53,13519,5789],{"class":389},[53,13521,13522],{"class":59}," callback",[53,13524,7017],{"class":82},[53,13526,13527],{"class":55,"line":163},[53,13528,13529],{"class":2530}," // assertions\n",[53,13531,13532,13535,13537,13539],{"class":55,"line":186},[53,13533,13534],{"class":82}," done ",[53,13536,390],{"class":389},[53,13538,5994],{"class":89},[53,13540,1727],{"class":82},[53,13542,13543],{"class":55,"line":221},[53,13544,13545],{"class":82}," });\n",[53,13547,13548,13551,13553,13555],{"class":55,"line":242},[53,13549,13550],{"class":59}," waitsFor",[53,13552,1067],{"class":82},[53,13554,5789],{"class":389},[53,13556,13502],{"class":82},[53,13558,13559,13562],{"class":55,"line":273},[53,13560,13561],{"class":389}," return",[53,13563,13510],{"class":82},[53,13565,13566],{"class":55,"line":279},[53,13567,13545],{"class":82},[53,13569,13570],{"class":55,"line":496},[53,13571,7148],{"class":82},[18,13573,13574],{},"Für jasmine 2.x müssen wir also",[577,13576,13577,13586,13596],{},[580,13578,13579,13580,13582,13583,13585],{},"einmal der Funktion die dem ",[50,13581,13490],{}," übergeben wird einen Parameter ",[50,13584,13332],{}," hinzufügen",[580,13587,13588,13591,13592,13595],{},[50,13589,13590],{},"done = true;"," mit ",[50,13593,13594],{},"done();"," ersetzen",[580,13597,13598,13599,13601],{},"und den ",[50,13600,13328],{}," Block löschen",[18,13603,13604],{},"Nach dem Refactoring soll das Ganze also wie folgt aussehen:",[43,13606,13608],{"className":13481,"code":13607,"language":13483,"meta":48,"style":48},"it(\"tests something async\", function (done) {\n doSomethingAsync(function callback() {\n // assertions\n done();\n });\n});\n",[50,13609,13610,13629,13641,13645,13652,13656],{"__ignoreMap":48},[53,13611,13612,13614,13616,13618,13620,13622,13624,13626],{"class":55,"line":56},[53,13613,13490],{"class":59},[53,13615,1067],{"class":82},[53,13617,13495],{"class":63},[53,13619,99],{"class":82},[53,13621,5789],{"class":389},[53,13623,7040],{"class":82},[53,13625,13332],{"class":5805},[53,13627,13628],{"class":82},") {\n",[53,13630,13631,13633,13635,13637,13639],{"class":55,"line":86},[53,13632,13515],{"class":59},[53,13634,1067],{"class":82},[53,13636,5789],{"class":389},[53,13638,13522],{"class":59},[53,13640,7017],{"class":82},[53,13642,13643],{"class":55,"line":126},[53,13644,13529],{"class":2530},[53,13646,13647,13650],{"class":55,"line":163},[53,13648,13649],{"class":59}," done",[53,13651,7061],{"class":82},[53,13653,13654],{"class":55,"line":186},[53,13655,13545],{"class":82},[53,13657,13658],{"class":55,"line":221},[53,13659,7148],{"class":82},[1217,13661,13338],{"id":13338},[18,13663,13664],{},"Bevor wir loslegen können, müssen noch wenige Dinge erledigt werden.",[43,13666,13670],{"className":13667,"code":13668,"language":13669,"meta":48,"style":48},"language-plaintext shiki shiki-themes github-light github-dark","\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","plaintext",[50,13671,13672,13676,13681,13686,13691],{"__ignoreMap":48},[53,13673,13674],{"class":55,"line":56},[53,13675,500],{"emptyLinePlaceholder":499},[53,13677,13678],{"class":55,"line":86},[53,13679,13680],{},"$> npm install -g jscodeshift\n",[53,13682,13683],{"class":55,"line":126},[53,13684,13685],{},"$> mkdir jasmineCodemods && cd jasmineCodemods\n",[53,13687,13688],{"class":55,"line":163},[53,13689,13690],{},"$> git init && git commit -m \"initial commit\" --allow-empty\n",[53,13692,13693],{"class":55,"line":186},[53,13694,13695],{},"$> touch jasmine-async.js\n",[18,13697,13698],{},"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!",[18,13700,13701],{},"Dann legen wir uns eine Datei für der/die/das erste codemod an und schreiben folgenden Inhalt:",[43,13703,13705],{"className":13481,"code":13704,"language":13483,"meta":48,"style":48},"// 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",[50,13706,13707,13712,13742,13755,13773,13787,13795],{"__ignoreMap":48},[53,13708,13709],{"class":55,"line":56},[53,13710,13711],{"class":2530},"// jasmine-async.js\n",[53,13713,13714,13717,13719,13722,13724,13727,13730,13732,13735,13737,13740],{"class":55,"line":86},[53,13715,13716],{"class":89},"module",[53,13718,986],{"class":82},[53,13720,13721],{"class":89},"exports",[53,13723,1245],{"class":389},[53,13725,13726],{"class":389}," function",[53,13728,13729],{"class":59}," transformer",[53,13731,1067],{"class":82},[53,13733,13734],{"class":5805},"file",[53,13736,99],{"class":82},[53,13738,13739],{"class":5805},"api",[53,13741,13628],{"class":82},[53,13743,13744,13747,13750,13752],{"class":55,"line":126},[53,13745,13746],{"class":389}," const",[53,13748,13749],{"class":89}," j",[53,13751,1245],{"class":389},[53,13753,13754],{"class":82}," api.jscodeshift;\n",[53,13756,13757,13759,13762,13765,13768,13770],{"class":55,"line":163},[53,13758,13746],{"class":389},[53,13760,13761],{"class":82}," { ",[53,13763,13764],{"class":89},"statement",[53,13766,13767],{"class":82}," } ",[53,13769,390],{"class":389},[53,13771,13772],{"class":82}," j.template;\n",[53,13774,13775,13777,13780,13782,13784],{"class":55,"line":186},[53,13776,13746],{"class":389},[53,13778,13779],{"class":89}," root",[53,13781,1245],{"class":389},[53,13783,13749],{"class":59},[53,13785,13786],{"class":82},"(file.source);\n",[53,13788,13789,13792],{"class":55,"line":221},[53,13790,13791],{"class":389}," return",[53,13793,13794],{"class":82}," root;\n",[53,13796,13797],{"class":55,"line":242},[53,13798,13799],{"class":82},"};\n",[18,13801,13802,13803,13806,13807,99,13810,99,13813,99,13816,13819,13820,13823,13824,3566,13829,11896,13834,13839],{},"Auf ",[50,13804,13805],{},"root"," können wir jetzt jscodeshift Methoden aufrufen wie ",[50,13808,13809],{},"find",[50,13811,13812],{},"filter",[50,13814,13815],{},"forEach",[50,13817,13818],{},"replaceWith"," und zuletzt\n",[50,13821,13822],{},"toSource",". Die Methoden machen genau das was der Name sagt, selbsterklärend. Genaueres muss man sich leider selbst\nim ",[585,13825,13828],{"href":13826,"rel":13827},"https://github.com/facebook/jscodeshift/blob/fe67b121d4c2519c5227a00be3f590e7f7c46d2b/src/Collection.js",[589],"Source",[585,13830,13833],{"href":13831,"rel":13832},"https://github.com/facebook/jscodeshift/tree/fe67b121d4c2519c5227a00be3f590e7f7c46d2b/src/collections",[589],"Code",[585,13835,13838],{"href":13836,"rel":13837},"https://github.com/facebook/jscodeshift/tree/fe67b121d4c2519c5227a00be3f590e7f7c46d2b",[589],"Github"," zusammenkratzen.",[18,13841,13842],{},"Ausführen können wir das Skript später mit",[43,13844,13846],{"className":13667,"code":13845,"language":13669,"meta":48,"style":48},"\n$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei\n\n",[50,13847,13848,13852],{"__ignoreMap":48},[53,13849,13850],{"class":55,"line":56},[53,13851,500],{"emptyLinePlaceholder":499},[53,13853,13854],{"class":55,"line":86},[53,13855,13856],{},"$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei\n",[18,13858,13859],{},"Doch zuerst müssen Transformationen gecoded werden 😮",[18,13861,13862,13863,13865,13866,13868,13869,13872,13873,13875,13876,13878,13879,13884],{},"Wir wollen fürs erste alle ",[50,13864,13490],{}," Knoten finden und der übergebenen Funktion einen ",[50,13867,13332],{}," Parameter spendieren. Zum Suchen\nvon Ausdrücken verwenden wir die jscodeshift Methode ",[50,13870,13871],{},"root.find",". Diese traversiert den AST und gibt uns eine Collection\nvon passenden Knoten zurück. Als Argument müssen wird dem ",[50,13874,13809],{}," Aufruf eine AST Beschreibung des ",[50,13877,13490],{}," Knotens mitgeben.\nBeim Finden der Beschreibung hilft uns der geniale ",[585,13880,13883],{"href":13881,"rel":13882},"https://astexplorer.net",[589],"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!",[18,13886,13887],{},[1773,13888],{"alt":13889,"src":13890},"astexplorer","https://media.synyx.de/uploads//2016/08/astexplorer-1024x631.png",[43,13892,13894],{"className":13481,"code":13893,"language":13483,"meta":48,"style":48},"// jasmine-async.js\nreturn root.find(j.CallExpression, {\n callee: {\n name: \"it\",\n },\n});\n",[50,13895,13896,13900,13913,13918,13928,13933],{"__ignoreMap":48},[53,13897,13898],{"class":55,"line":56},[53,13899,13711],{"class":2530},[53,13901,13902,13905,13908,13910],{"class":55,"line":86},[53,13903,13904],{"class":389},"return",[53,13906,13907],{"class":82}," root.",[53,13909,13809],{"class":59},[53,13911,13912],{"class":82},"(j.CallExpression, {\n",[53,13914,13915],{"class":55,"line":126},[53,13916,13917],{"class":82}," callee: {\n",[53,13919,13920,13923,13926],{"class":55,"line":163},[53,13921,13922],{"class":82}," name: ",[53,13924,13925],{"class":63},"\"it\"",[53,13927,7143],{"class":82},[53,13929,13930],{"class":55,"line":186},[53,13931,13932],{"class":82}," },\n",[53,13934,13935],{"class":55,"line":221},[53,13936,7148],{"class":82},[18,13938,13939,13940,13942,13943,13945,13946,13948],{},"Dann wollen wir für alle Knoten die gefunden werden etwas tun. Nämlich den ",[50,13941,13332],{}," Parameter hinzufügen zur eigentlichen\nTestfunktion. Mit ",[50,13944,13815],{}," können wir über die von ",[50,13947,13809],{}," zurückgegebene Collection iterieren und dies tun.",[43,13950,13952],{"className":13481,"code":13951,"language":13483,"meta":48,"style":48},"\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",[50,13953,13954,13958,13962,13969,13983,13998,14003,14021,14026,14044],{"__ignoreMap":48},[53,13955,13956],{"class":55,"line":56},[53,13957,500],{"emptyLinePlaceholder":499},[53,13959,13960],{"class":55,"line":86},[53,13961,13711],{"class":2530},[53,13963,13964,13966],{"class":55,"line":126},[53,13965,13904],{"class":389},[53,13967,13968],{"class":82}," root\n",[53,13970,13971,13974,13976,13978,13981],{"class":55,"line":163},[53,13972,13973],{"class":82}," .",[53,13975,13809],{"class":59},[53,13977,1067],{"class":82},[53,13979,13980],{"class":389},"...",[53,13982,685],{"class":82},[53,13984,13985,13987,13989,13991,13993,13996],{"class":55,"line":186},[53,13986,13973],{"class":82},[53,13988,13815],{"class":59},[53,13990,1067],{"class":82},[53,13992,18],{"class":5805},[53,13994,13995],{"class":389}," =>",[53,13997,5795],{"class":82},[53,13999,14000],{"class":55,"line":221},[53,14001,14002],{"class":2530}," // p.node.arguments[0] would be the spec description\n",[53,14004,14005,14008,14011,14013,14016,14018],{"class":55,"line":242},[53,14006,14007],{"class":389}," const",[53,14009,14010],{"class":89}," specCallee",[53,14012,1245],{"class":389},[53,14014,14015],{"class":82}," p.node.arguments[",[53,14017,7598],{"class":89},[53,14019,14020],{"class":82},"];\n",[53,14022,14023],{"class":55,"line":273},[53,14024,14025],{"class":2530}," // add 'done' parameter\n",[53,14027,14028,14031,14034,14036,14039,14042],{"class":55,"line":279},[53,14029,14030],{"class":82}," specCallee.params.",[53,14032,14033],{"class":59},"push",[53,14035,1067],{"class":82},[53,14037,14038],{"class":59},"statment",[53,14040,14041],{"class":63},"`done`",[53,14043,1079],{"class":82},[53,14045,14046],{"class":55,"line":496},[53,14047,14048],{"class":82}," })\n",[18,14050,14051,14052,14054,14055,14057,14058,14061,14062,14064,14065,14067],{},"Die Variable ",[50,14053,18],{}," ist der Pfad des gefundenen Knotens. Man könnte die Variable auch ",[50,14056,2606],{}," benennen, würde sich aber\nbeißen mit dem node Modul ",[50,14059,14060],{},"const path = require('path');",". Das importieren dieses Moduls ist keine Seltenheit in\ncodemods denke ich. Und als Konvention nehmen wir einfach ",[50,14063,18],{}," statt ",[50,14066,2606],{},", immer!",[18,14069,14070,14071,14073,14074,14076,14077,14079],{},"Der ASTExplorer zeigt wie wir an die Funktion kommen der wir den ",[50,14072,13332],{}," Parameter hinzufügen möchten. Wir holen uns das\nzweite Element der CallExpression Argumente und fügen dessen Parameter Liste einfach das ",[50,14075,13332],{}," 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 ",[50,14078,13764],{}," 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?",[18,14081,14082,14083,14086],{},"Zum Abschluss müssen wir die Änderungen mit ",[50,14084,14085],{},"toSource()"," an jscodeshift zurückgeben um die Datei neu zu schreiben.",[43,14088,14090],{"className":13481,"code":14089,"language":13483,"meta":48,"style":48},"\n// jasmine-async.js\nreturn root\n .find(...)\n .forEach(...)\n .toSource()\n\n",[50,14091,14092,14096,14100,14106,14118,14130],{"__ignoreMap":48},[53,14093,14094],{"class":55,"line":56},[53,14095,500],{"emptyLinePlaceholder":499},[53,14097,14098],{"class":55,"line":86},[53,14099,13711],{"class":2530},[53,14101,14102,14104],{"class":55,"line":126},[53,14103,13904],{"class":389},[53,14105,13968],{"class":82},[53,14107,14108,14110,14112,14114,14116],{"class":55,"line":163},[53,14109,13973],{"class":82},[53,14111,13809],{"class":59},[53,14113,1067],{"class":82},[53,14115,13980],{"class":389},[53,14117,685],{"class":82},[53,14119,14120,14122,14124,14126,14128],{"class":55,"line":186},[53,14121,13973],{"class":82},[53,14123,13815],{"class":59},[53,14125,1067],{"class":82},[53,14127,13980],{"class":389},[53,14129,685],{"class":82},[53,14131,14132,14134,14136],{"class":55,"line":221},[53,14133,13973],{"class":82},[53,14135,13822],{"class":59},[53,14137,14138],{"class":82},"()\n",[18,14140,14141],{},"Zum schnellen Testen kann die Transformation auf der Konsole mit",[43,14143,14145],{"className":13667,"code":14144,"language":13669,"meta":48,"style":48},"\n$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei.js\n\n",[50,14146,14147,14151],{"__ignoreMap":48},[53,14148,14149],{"class":55,"line":56},[53,14150,500],{"emptyLinePlaceholder":499},[53,14152,14153],{"class":55,"line":86},[53,14154,14155],{},"$> jscodeshift -t ./jasmine-async.js pfad/zur/source/datei.js\n",[18,14157,14158],{},"ausgeführt werden. Nach dem ersten Staunen aber mit",[43,14160,14162],{"className":13667,"code":14161,"language":13669,"meta":48,"style":48},"\n$> git checkout HEAD -- pfad/zur/source/datei.js\n\n",[50,14163,14164,14168],{"__ignoreMap":48},[53,14165,14166],{"class":55,"line":56},[53,14167,500],{"emptyLinePlaceholder":499},[53,14169,14170],{"class":55,"line":86},[53,14171,14172],{},"$> git checkout HEAD -- pfad/zur/source/datei.js\n",[18,14174,14175],{},"zurück gesetzt werden.",[18,14177,14178],{},"Git o/",[18,14180,14181],{},"Der erste Punkt ist erledigt.",[577,14183,14184,14192,14198],{},[580,14185,14186],{},[13401,14187,13579,14188,13582,14190,13585],{},[50,14189,13490],{},[50,14191,13332],{},[580,14193,14194,13591,14196,13595],{},[50,14195,13590],{},[50,14197,13594],{},[580,14199,13598,14200,13601],{},[50,14201,13328],{},[18,14203,14204,14205,14207,14208,14211],{},"Ersetzen wir als nächstes ",[50,14206,13590],{}," mit dem ",[50,14209,14210],{},"done()"," Aufruf. Dazu klicken wir im ASTExplorer auf den entsprechenden\nAusdruck und schauen rechts im AST nach der Pfad Beschreibung die wir brauchen.",[43,14213,14215],{"className":13481,"code":14214,"language":13483,"meta":48,"style":48},"\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",[50,14216,14217,14221,14225,14231,14243,14257,14262,14267,14280,14285,14290,14295,14303,14307,14312,14333,14337],{"__ignoreMap":48},[53,14218,14219],{"class":55,"line":56},[53,14220,500],{"emptyLinePlaceholder":499},[53,14222,14223],{"class":55,"line":86},[53,14224,13711],{"class":2530},[53,14226,14227,14229],{"class":55,"line":126},[53,14228,13904],{"class":389},[53,14230,13968],{"class":82},[53,14232,14233,14235,14237,14239,14241],{"class":55,"line":163},[53,14234,13973],{"class":82},[53,14236,13809],{"class":59},[53,14238,1067],{"class":82},[53,14240,13980],{"class":389},[53,14242,685],{"class":82},[53,14244,14245,14247,14249,14251,14253,14255],{"class":55,"line":186},[53,14246,13973],{"class":82},[53,14248,13815],{"class":59},[53,14250,1067],{"class":82},[53,14252,18],{"class":5805},[53,14254,13995],{"class":389},[53,14256,5795],{"class":82},[53,14258,14259],{"class":55,"line":221},[53,14260,14261],{"class":2530}," // ...\n",[53,14263,14264],{"class":55,"line":242},[53,14265,14266],{"class":2530}," // replace 'done = true' with done() invocation\n",[53,14268,14269,14272,14275,14277],{"class":55,"line":273},[53,14270,14271],{"class":59}," j",[53,14273,14274],{"class":82},"(p).",[53,14276,13809],{"class":59},[53,14278,14279],{"class":82},"(j.ExpressionStatement, {\n",[53,14281,14282],{"class":55,"line":279},[53,14283,14284],{"class":82}," expression: {\n",[53,14286,14287],{"class":55,"line":496},[53,14288,14289],{"class":82}," type: j.AssignmentExpression.name,\n",[53,14291,14292],{"class":55,"line":503},[53,14293,14294],{"class":82}," left: {\n",[53,14296,14297,14300],{"class":55,"line":509},[53,14298,14299],{"class":82}," name: ",[53,14301,14302],{"class":63},"'done'\n",[53,14304,14305],{"class":55,"line":515},[53,14306,12712],{"class":82},[53,14308,14309],{"class":55,"line":521},[53,14310,14311],{"class":82}," }\n",[53,14313,14314,14317,14319,14321,14323,14325,14328,14331],{"class":55,"line":527},[53,14315,14316],{"class":82}," }).",[53,14318,13818],{"class":59},[53,14320,1067],{"class":82},[53,14322,18],{"class":5805},[53,14324,13995],{"class":389},[53,14326,14327],{"class":59}," statement",[53,14329,14330],{"class":63},"`done();`",[53,14332,1079],{"class":82},[53,14334,14335],{"class":55,"line":533},[53,14336,14048],{"class":82},[53,14338,14339,14341,14343],{"class":55,"line":539},[53,14340,13973],{"class":82},[53,14342,13822],{"class":59},[53,14344,14138],{"class":82},[18,14346,14347,14348,14350,14351,986],{},"Da wir wissen, dass ",[50,14349,13590],{}," nur einmal vorkommt, können wir der Collection direkt sagen bitte ersetzen mit dem\nStatement ",[50,14352,13594],{},[18,14354,5442,14355,14357],{},[50,14356,13332],{}," 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:",[43,14359,14361],{"className":13481,"code":14360,"language":13483,"meta":48,"style":48},"\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",[50,14362,14363,14367,14371,14377,14389,14403,14407,14412,14423,14428,14433,14438,14443,14450,14455,14459,14464,14473,14477],{"__ignoreMap":48},[53,14364,14365],{"class":55,"line":56},[53,14366,500],{"emptyLinePlaceholder":499},[53,14368,14369],{"class":55,"line":86},[53,14370,13711],{"class":2530},[53,14372,14373,14375],{"class":55,"line":126},[53,14374,13904],{"class":389},[53,14376,13968],{"class":82},[53,14378,14379,14381,14383,14385,14387],{"class":55,"line":163},[53,14380,13973],{"class":82},[53,14382,13809],{"class":59},[53,14384,1067],{"class":82},[53,14386,13980],{"class":389},[53,14388,685],{"class":82},[53,14390,14391,14393,14395,14397,14399,14401],{"class":55,"line":186},[53,14392,13973],{"class":82},[53,14394,13815],{"class":59},[53,14396,1067],{"class":82},[53,14398,18],{"class":5805},[53,14400,13995],{"class":389},[53,14402,5795],{"class":82},[53,14404,14405],{"class":55,"line":221},[53,14406,14261],{"class":2530},[53,14408,14409],{"class":55,"line":242},[53,14410,14411],{"class":2530}," // get rid of 'var done = false'\n",[53,14413,14414,14416,14418,14420],{"class":55,"line":273},[53,14415,14271],{"class":59},[53,14417,14274],{"class":82},[53,14419,13809],{"class":59},[53,14421,14422],{"class":82},"(j.VariableDeclaration, {\n",[53,14424,14425],{"class":55,"line":279},[53,14426,14427],{"class":82}," declarations: [\n",[53,14429,14430],{"class":55,"line":496},[53,14431,14432],{"class":82}," {\n",[53,14434,14435],{"class":55,"line":503},[53,14436,14437],{"class":82}," type: j.VariableDeclarator.name,\n",[53,14439,14440],{"class":55,"line":509},[53,14441,14442],{"class":82}," id: {\n",[53,14444,14445,14448],{"class":55,"line":515},[53,14446,14447],{"class":82}," name: ",[53,14449,14302],{"class":63},[53,14451,14452],{"class":55,"line":521},[53,14453,14454],{"class":82}," }\n",[53,14456,14457],{"class":55,"line":527},[53,14458,12712],{"class":82},[53,14460,14461],{"class":55,"line":533},[53,14462,14463],{"class":82}," ]\n",[53,14465,14466,14468,14471],{"class":55,"line":539},[53,14467,14316],{"class":82},[53,14469,14470],{"class":59},"remove",[53,14472,14138],{"class":82},[53,14474,14475],{"class":55,"line":545},[53,14476,14048],{"class":82},[53,14478,14479,14481,14483],{"class":55,"line":2070},[53,14480,13973],{"class":82},[53,14482,13822],{"class":59},[53,14484,14138],{"class":82},[18,14486,14487],{},"Zweiter Punkt auch erledigt.",[577,14489,14490,14498,14506],{},[580,14491,14492],{},[13401,14493,13579,14494,13582,14496,13585],{},[50,14495,13490],{},[50,14497,13332],{},[580,14499,14500],{},[13401,14501,14502,13591,14504,13595],{},[50,14503,13590],{},[50,14505,13594],{},[580,14507,13598,14508,13601],{},[50,14509,13328],{},[18,14511,14512,14513,14515],{},"Fehlt nur noch das Entfernen des ",[50,14514,13328],{}," Blocks. Richtig geraten! Der ASTExplorer zeigt uns was wir suchen müssen.",[43,14517,14519],{"className":13481,"code":14518,"language":13483,"meta":48,"style":48},"\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",[50,14520,14521,14525,14529,14535,14547,14561,14565,14570,14580,14585,14593,14597,14605,14609],{"__ignoreMap":48},[53,14522,14523],{"class":55,"line":56},[53,14524,500],{"emptyLinePlaceholder":499},[53,14526,14527],{"class":55,"line":86},[53,14528,13711],{"class":2530},[53,14530,14531,14533],{"class":55,"line":126},[53,14532,13904],{"class":389},[53,14534,13968],{"class":82},[53,14536,14537,14539,14541,14543,14545],{"class":55,"line":163},[53,14538,13973],{"class":82},[53,14540,13809],{"class":59},[53,14542,1067],{"class":82},[53,14544,13980],{"class":389},[53,14546,685],{"class":82},[53,14548,14549,14551,14553,14555,14557,14559],{"class":55,"line":186},[53,14550,13973],{"class":82},[53,14552,13815],{"class":59},[53,14554,1067],{"class":82},[53,14556,18],{"class":5805},[53,14558,13995],{"class":389},[53,14560,5795],{"class":82},[53,14562,14563],{"class":55,"line":221},[53,14564,14261],{"class":2530},[53,14566,14567],{"class":55,"line":242},[53,14568,14569],{"class":2530}," // get rid of obsolete waitsFor block\n",[53,14571,14572,14574,14576,14578],{"class":55,"line":273},[53,14573,14271],{"class":59},[53,14575,14274],{"class":82},[53,14577,13809],{"class":59},[53,14579,13912],{"class":82},[53,14581,14582],{"class":55,"line":279},[53,14583,14584],{"class":82}," callee: {\n",[53,14586,14587,14590],{"class":55,"line":496},[53,14588,14589],{"class":82}," name: ",[53,14591,14592],{"class":63},"'waitsFor'\n",[53,14594,14595],{"class":55,"line":503},[53,14596,14311],{"class":82},[53,14598,14599,14601,14603],{"class":55,"line":509},[53,14600,14316],{"class":82},[53,14602,14470],{"class":59},[53,14604,14138],{"class":82},[53,14606,14607],{"class":55,"line":515},[53,14608,14048],{"class":82},[53,14610,14611,14613,14615],{"class":55,"line":521},[53,14612,13973],{"class":82},[53,14614,13822],{"class":59},[53,14616,14138],{"class":82},[18,14618,14619],{},"Fertig!",[18,14621,14622],{},"Schnell noch testen obs auch wirklich tut:",[43,14624,14625],{"className":13667,"code":14144,"language":13669,"meta":48,"style":48},[50,14626,14627,14631],{"__ignoreMap":48},[53,14628,14629],{"class":55,"line":56},[53,14630,500],{"emptyLinePlaceholder":499},[53,14632,14633],{"class":55,"line":86},[53,14634,14155],{},[18,14636,14637],{},"Und ab damit ins Repo",[43,14639,14641],{"className":13667,"code":14640,"language":13669,"meta":48,"style":48},"\n$> git commit -am \"jasmine async test upgrade from 1.x to 2.x; automated 🎉\"\n\n",[50,14642,14643,14647],{"__ignoreMap":48},[53,14644,14645],{"class":55,"line":56},[53,14646,500],{"emptyLinePlaceholder":499},[53,14648,14649],{"class":55,"line":86},[53,14650,14651],{},"$> git commit -am \"jasmine async test upgrade from 1.x to 2.x; automated 🎉\"\n",[18,14653,14654],{},"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.",[2352,14656,14658],{"id":14657},"ausblick","Ausblick",[18,14660,14661,14662,14664,14665,14667],{},"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",[50,14663,13328],{}," 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 ",[50,14666,13328],{}," Blöcke hatten weil mehrere Klicks simuliert\nwurden.",[18,14669,14670],{},"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.",[18,14672,14673,14674,14679,14680,14683,14684,14687],{},"Werden codemods komplexer kann man über Unit Tests nachdenken. Hier gibt es Hilfe von jscodeshift. Als Beispiel\nhilft ",[585,14675,14678],{"href":14676,"rel":14677},"https://github.com/cpojer/js-codemod/blob/82af3089f22fa0687159f64177b73908b82d074f/transforms/__tests__/arrow-function-test.js",[589],"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 ‘",[27,14681,14682],{},"testfixtures","’ abgelegt. Der Test liegt im Verzeichnis\n‘",[27,14685,14686],{},"tests","’ und definiert mittels den TestUtils einfach nur den Test. Codemods testgetrieben entwickeln steht nichts\nmehr im Weg.",[43,14689,14691],{"className":13481,"code":14690,"language":13483,"meta":48,"style":48},"// jasmine-async.spec.js\nconst defineTest = require(\"jscodeshift/dist/testUtils\").defineTest;\ndefineTest(__dirname, \"jasmine-async\");\n",[50,14692,14693,14698,14719],{"__ignoreMap":48},[53,14694,14695],{"class":55,"line":56},[53,14696,14697],{"class":2530},"// jasmine-async.spec.js\n",[53,14699,14700,14703,14706,14708,14711,14713,14716],{"class":55,"line":86},[53,14701,14702],{"class":389},"const",[53,14704,14705],{"class":89}," defineTest",[53,14707,1245],{"class":389},[53,14709,14710],{"class":59}," require",[53,14712,1067],{"class":82},[53,14714,14715],{"class":63},"\"jscodeshift/dist/testUtils\"",[53,14717,14718],{"class":82},").defineTest;\n",[53,14720,14721,14724,14727,14730],{"class":55,"line":126},[53,14722,14723],{"class":59},"defineTest",[53,14725,14726],{"class":82},"(__dirname, ",[53,14728,14729],{"class":63},"\"jasmine-async\"",[53,14731,1079],{"class":82},[2352,14733,969],{"id":968},[18,14735,14736],{},"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.",[18,14738,14739,14740,9267,14745,14750],{},"Oft verwendet habe ich bisher die ",[585,14741,14744],{"href":14742,"rel":14743},"https://github.com/cpojer/js-codemod",[589],"js-codemods",[585,14746,14749],{"href":14747,"rel":14748},"https://twitter.com/cpojer",[589],"@cpojer"," zum transformieren zu neuen es2015 Sprachfeatures.",[18,14752,14753],{},[27,14754,14755],{},"Meine Empfehlung:",[18,14757,14758],{},"Einmal reinknien und machen! Vielleicht sogar für kleinere Projekte.",[607,14760,14761],{},"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":48,"searchDepth":86,"depth":86,"links":14763},[14764,14765,14766,14769,14770],{"id":13348,"depth":86,"text":13349},{"id":13387,"depth":86,"text":13388},{"id":13437,"depth":86,"text":13438,"children":14767},[14768],{"id":13464,"depth":126,"text":13465},{"id":14657,"depth":86,"text":14658},{"id":968,"depth":86,"text":969},[613],"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":13305,"description":14778},"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",[14781,6991,14782],"automatisierung","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…","npdOLNgV0j3l4jQhircA5afYDyTYYRrnbICTKF9u9Mw",{"id":14786,"title":14787,"author":14788,"body":14789,"category":15520,"date":15521,"description":15522,"extension":617,"link":15523,"meta":15524,"navigation":499,"path":15525,"seo":15526,"slug":14793,"stem":15527,"tags":15528,"teaser":15533,"__hash__":15534},"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",[12861],{"type":11,"value":14790,"toc":15510},[14791,14794,14797,14800,14803,14817,14820,14855,14858,14862,14865,14868,14871,14874,14891,14895,14898,14901,14904,14907,14910,14913,15046,15049,15058,15062,15065,15068,15071,15075,15078,15114,15117,15120,15123,15203,15206,15209,15217,15220,15228,15232,15235,15259,15262,15304,15307,15310,15316,15350,15355,15397,15402,15460,15463,15469,15477,15479,15490,15494,15497,15505,15508],[14,14792,14787],{"id":14793},"android-building-apks-for-different-environments-using-build-types-and-product-flavors",[18,14795,14796],{},"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.",[2352,14798,14799],{"id":13104},"Build types:",[18,14801,14802],{},"Each android project comes with some default build types:",[577,14804,14805,14808,14811,14814],{},[580,14806,14807],{},"debug -> for development and debugging on devices",[580,14809,14810],{},"test -> for running unit tests",[580,14812,14813],{},"androidTest -> for running device tests",[580,14815,14816],{},"release -> for the actual release version of the app",[18,14818,14819],{},"A freshly created build.gradle, has the following build types defined:",[43,14821,14823],{"className":12891,"code":14822,"language":12893,"meta":48,"style":48},"\n buildTypes {\n release {\n minifyEnabled false\n proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n }\n }\n\n",[50,14824,14825,14829,14833,14837,14842,14847,14851],{"__ignoreMap":48},[53,14826,14827],{"class":55,"line":56},[53,14828,500],{"emptyLinePlaceholder":499},[53,14830,14831],{"class":55,"line":86},[53,14832,12900],{},[53,14834,14835],{"class":55,"line":126},[53,14836,12978],{},[53,14838,14839],{"class":55,"line":163},[53,14840,14841],{}," minifyEnabled false\n",[53,14843,14844],{"class":55,"line":186},[53,14845,14846],{}," proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n",[53,14848,14849],{"class":55,"line":221},[53,14850,12712],{},[53,14852,14853],{"class":55,"line":242},[53,14854,860],{},[18,14856,14857],{},"The default build types don’t need to be declared, but you can declare them in order to extend them with custom config.",[2352,14859,14861],{"id":14860},"different-environments","Different environments",[18,14863,14864],{},"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.",[18,14866,14867],{},"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.",[18,14869,14870],{},"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.",[18,14872,14873],{},"The above requirements result in the following build types:",[577,14875,14876,14879,14882,14885,14888],{},[580,14877,14878],{},"test system",[580,14880,14881],{},"stage system",[580,14883,14884],{},"production sytem -> we can just use the default release build type",[580,14886,14887],{},"local service",[580,14889,14890],{},"no service at all -> mocked services",[2352,14892,14894],{"id":14893},"different-urls-based-on-the-build-type","Different URLs based on the build type",[18,14896,14897],{},"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.",[18,14899,14900],{},"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.",[18,14902,14903],{},"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.",[18,14905,14906],{},"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.",[18,14908,14909],{},"The builds for test, stage and production should be all built with the release certificate, since we want to upload them\nto the playstore.",[18,14911,14912],{},"With these additions applied, our builtTypes look like this:",[43,14914,14916],{"className":12891,"code":14915,"language":12893,"meta":48,"style":48},"\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",[50,14917,14918,14922,14926,14930,14935,14940,14945,14950,14954,14958,14962,14967,14972,14977,14981,14985,14989,14993,14998,15003,15007,15011,15016,15020,15024,15028,15033,15038,15042],{"__ignoreMap":48},[53,14919,14920],{"class":55,"line":56},[53,14921,500],{"emptyLinePlaceholder":499},[53,14923,14924],{"class":55,"line":86},[53,14925,12900],{},[53,14927,14928],{"class":55,"line":126},[53,14929,12924],{},[53,14931,14932],{"class":55,"line":163},[53,14933,14934],{}," debuggable true\n",[53,14936,14937],{"class":55,"line":186},[53,14938,14939],{}," signingConfig signingConfigs.debug\n",[53,14941,14942],{"class":55,"line":221},[53,14943,14944],{}," buildConfigField 'String', 'ENVIRONMENT', '\"LOCAL\"';\n",[53,14946,14947],{"class":55,"line":242},[53,14948,14949],{}," buildConfigField 'String', 'API_URL', '\"http://'+ InetAddress.getLocalHost().getCanonicalHostName() + ':8080/\"';\n",[53,14951,14952],{"class":55,"line":273},[53,14953,12712],{},[53,14955,14956],{"class":55,"line":279},[53,14957,12942],{},[53,14959,14960],{"class":55,"line":496},[53,14961,14934],{},[53,14963,14964],{"class":55,"line":503},[53,14965,14966],{}," signingConfig signingConfigs.release\n",[53,14968,14969],{"class":55,"line":509},[53,14970,14971],{}," buildConfigField 'String', 'ENVIRONMENT', '\"TEST\"';\n",[53,14973,14974],{"class":55,"line":515},[53,14975,14976],{}," buildConfigField 'String', 'API_URL', '\"https://my-service-test.mydomain/\"';\n",[53,14978,14979],{"class":55,"line":521},[53,14980,12712],{},[53,14982,14983],{"class":55,"line":527},[53,14984,12960],{},[53,14986,14987],{"class":55,"line":533},[53,14988,14934],{},[53,14990,14991],{"class":55,"line":539},[53,14992,14966],{},[53,14994,14995],{"class":55,"line":545},[53,14996,14997],{}," buildConfigField 'String', 'ENVIRONMENT', '\"STAGE\"';\n",[53,14999,15000],{"class":55,"line":2070},[53,15001,15002],{}," buildConfigField 'String', 'API_URL', '\"https://my-service-stage.mydomain/\"';\n",[53,15004,15005],{"class":55,"line":2075},[53,15006,12712],{},[53,15008,15009],{"class":55,"line":2081},[53,15010,12978],{},[53,15012,15013],{"class":55,"line":2087},[53,15014,15015],{}," debuggable false\n",[53,15017,15018],{"class":55,"line":2092},[53,15019,14841],{},[53,15021,15022],{"class":55,"line":2097},[53,15023,14966],{},[53,15025,15026],{"class":55,"line":2103},[53,15027,14846],{},[53,15029,15030],{"class":55,"line":2109},[53,15031,15032],{}," buildConfigField 'String', 'ENVIRONMENT', '\"PROD\"';\n",[53,15034,15035],{"class":55,"line":2115},[53,15036,15037],{}," buildConfigField 'String', 'API_URL', '\"https://my-service.mydomain/\"';\n",[53,15039,15040],{"class":55,"line":2120},[53,15041,12712],{},[53,15043,15044],{"class":55,"line":2946},[53,15045,860],{},[18,15047,15048],{},"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.",[43,15050,15052],{"className":13667,"code":15051,"language":13669,"meta":48,"style":48},"BuildConfig.API_URL\n",[50,15053,15054],{"__ignoreMap":48},[53,15055,15056],{"class":55,"line":56},[53,15057,15051],{},[2352,15059,15061],{"id":15060},"mocked-services","Mocked Services",[18,15063,15064],{},"For the mocked services, I’ll provide you with two different approaches, which both have their upsides and downsides.",[18,15066,15067],{},"The first approach is to use more build types.",[18,15069,15070],{},"The second approach is using two product flavors.",[649,15072,15074],{"id":15073},"mocked-services-via-build-type","Mocked Services via build type",[18,15076,15077],{},"We add the config we need to the ‘debug’ buildType:",[43,15079,15081],{"className":12891,"code":15080,"language":12893,"meta":48,"style":48}," buildTypes {\n debug{\n buildConfigField 'String', 'ENVIRONMENT', '\"DEV\"';\n buildConfigField 'String', 'API_URL', '\"https://not.really.needed/\"';\n }\n ...\n }\n\n",[50,15082,15083,15087,15091,15096,15101,15105,15110],{"__ignoreMap":48},[53,15084,15085],{"class":55,"line":56},[53,15086,12900],{},[53,15088,15089],{"class":55,"line":86},[53,15090,12905],{},[53,15092,15093],{"class":55,"line":126},[53,15094,15095],{}," buildConfigField 'String', 'ENVIRONMENT', '\"DEV\"';\n",[53,15097,15098],{"class":55,"line":163},[53,15099,15100],{}," buildConfigField 'String', 'API_URL', '\"https://not.really.needed/\"';\n",[53,15102,15103],{"class":55,"line":186},[53,15104,12712],{},[53,15106,15107],{"class":55,"line":221},[53,15108,15109],{}," ...\n",[53,15111,15112],{"class":55,"line":242},[53,15113,860],{},[18,15115,15116],{},"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.",[18,15118,15119],{},"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.",[18,15121,15122],{},"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.",[43,15124,15126],{"className":288,"code":15125,"language":290,"meta":48,"style":48},"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",[50,15127,15128,15133,15138,15143,15148,15153,15157,15162,15166,15171,15176,15181,15186,15191,15195,15199],{"__ignoreMap":48},[53,15129,15130],{"class":55,"line":56},[53,15131,15132],{},"public class DemoApplication extends Application {\n",[53,15134,15135],{"class":55,"line":86},[53,15136,15137],{}," private DemoService demoService;\n",[53,15139,15140],{"class":55,"line":126},[53,15141,15142],{}," public DemoService getDemoService() {\n",[53,15144,15145],{"class":55,"line":163},[53,15146,15147],{}," if (demoService == null) {\n",[53,15149,15150],{"class":55,"line":186},[53,15151,15152],{}," demoService = createDemoService();\n",[53,15154,15155],{"class":55,"line":221},[53,15156,12712],{},[53,15158,15159],{"class":55,"line":242},[53,15160,15161],{}," return demoService;\n",[53,15163,15164],{"class":55,"line":273},[53,15165,860],{},[53,15167,15168],{"class":55,"line":279},[53,15169,15170],{}," private DemoService createDemoService() {\n",[53,15172,15173],{"class":55,"line":496},[53,15174,15175],{}," if (\"DEV\".equals(BuildConfig.ENVIRONMENT)) {\n",[53,15177,15178],{"class":55,"line":503},[53,15179,15180],{}," return new DemoServiceMockImpl();\n",[53,15182,15183],{"class":55,"line":509},[53,15184,15185],{}," } else {\n",[53,15187,15188],{"class":55,"line":515},[53,15189,15190],{}," return new DemoServiceWebApiImpl();\n",[53,15192,15193],{"class":55,"line":521},[53,15194,12712],{},[53,15196,15197],{"class":55,"line":527},[53,15198,860],{},[53,15200,15201],{"class":55,"line":533},[53,15202,282],{},[18,15204,15205],{},"With this approach, we have to make sure that the DemoApplication is the only place, where an instance of this service\nis created.",[18,15207,15208],{},"Upsides:",[577,15210,15211,15214],{},[580,15212,15213],{},"Easy configuration",[580,15215,15216],{},"Fast build speed (+1 build type = +1 APK to build)",[18,15218,15219],{},"Downsides:",[577,15221,15222,15225],{},[580,15223,15224],{},"We need to check the environment in the code",[580,15226,15227],{},"We ship the mock code in the production APK",[649,15229,15231],{"id":15230},"mocked-services-via-product-flavors","Mocked Services via product flavors",[18,15233,15234],{},"In this approach, instead of adding a new buildType, we add a build flavor:",[43,15236,15238],{"className":12891,"code":15237,"language":12893,"meta":48,"style":48}," productFlavors {\n webApi {}\n mock {}\n }\n\n",[50,15239,15240,15245,15250,15255],{"__ignoreMap":48},[53,15241,15242],{"class":55,"line":56},[53,15243,15244],{}," productFlavors {\n",[53,15246,15247],{"class":55,"line":86},[53,15248,15249],{}," webApi {}\n",[53,15251,15252],{"class":55,"line":126},[53,15253,15254],{}," mock {}\n",[53,15256,15257],{"class":55,"line":163},[53,15258,860],{},[18,15260,15261],{},"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.",[43,15263,15265],{"className":288,"code":15264,"language":290,"meta":48,"style":48},"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",[50,15266,15267,15271,15275,15279,15283,15288,15292,15296,15300],{"__ignoreMap":48},[53,15268,15269],{"class":55,"line":56},[53,15270,15132],{},[53,15272,15273],{"class":55,"line":86},[53,15274,15137],{},[53,15276,15277],{"class":55,"line":126},[53,15278,15142],{},[53,15280,15281],{"class":55,"line":163},[53,15282,15147],{},[53,15284,15285],{"class":55,"line":186},[53,15286,15287],{}," demoService = new DemoServiceImpl();\n",[53,15289,15290],{"class":55,"line":221},[53,15291,12712],{},[53,15293,15294],{"class":55,"line":242},[53,15295,15161],{},[53,15297,15298],{"class":55,"line":273},[53,15299,860],{},[53,15301,15302],{"class":55,"line":279},[53,15303,282],{},[18,15305,15306],{},"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.",[18,15308,15309],{},"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.",[18,15311,15312],{},[1773,15313],{"alt":15314,"src":15315},"productFlavors","https://media.synyx.de/uploads//2016/08/productFlavors-300x265.png",[43,15317,15319],{"className":288,"code":15318,"language":290,"meta":48,"style":48},"public interface DemoService {\n /**\n * @return list of stuff, may be empty\n */\n List\u003CString> getStuff();\n}\n\n",[50,15320,15321,15326,15331,15336,15341,15346],{"__ignoreMap":48},[53,15322,15323],{"class":55,"line":56},[53,15324,15325],{},"public interface DemoService {\n",[53,15327,15328],{"class":55,"line":86},[53,15329,15330],{}," /**\n",[53,15332,15333],{"class":55,"line":126},[53,15334,15335],{}," * @return list of stuff, may be empty\n",[53,15337,15338],{"class":55,"line":163},[53,15339,15340],{}," */\n",[53,15342,15343],{"class":55,"line":186},[53,15344,15345],{}," List\u003CString> getStuff();\n",[53,15347,15348],{"class":55,"line":221},[53,15349,282],{},[18,15351,15352],{},[27,15353,15354],{},"webApi:",[43,15356,15358],{"className":288,"code":15357,"language":290,"meta":48,"style":48},"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",[50,15359,15360,15365,15369,15374,15379,15384,15389,15393],{"__ignoreMap":48},[53,15361,15362],{"class":55,"line":56},[53,15363,15364],{},"public class DemoServiceImpl implements DemoService {\n",[53,15366,15367],{"class":55,"line":86},[53,15368,13033],{},[53,15370,15371],{"class":55,"line":126},[53,15372,15373],{}," public List\u003CString> getStuff() {\n",[53,15375,15376],{"class":55,"line":163},[53,15377,15378],{}," List\u003CString> stuff = new ArrayList\u003C>();\n",[53,15380,15381],{"class":55,"line":186},[53,15382,15383],{}," // do web request and put the results into 'stuff'\n",[53,15385,15386],{"class":55,"line":221},[53,15387,15388],{}," return stuff;\n",[53,15390,15391],{"class":55,"line":242},[53,15392,860],{},[53,15394,15395],{"class":55,"line":273},[53,15396,282],{},[18,15398,15399],{},[27,15400,15401],{},"mock:",[43,15403,15405],{"className":288,"code":15404,"language":290,"meta":48,"style":48},"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",[50,15406,15407,15411,15416,15421,15426,15431,15436,15440,15444,15448,15452,15456],{"__ignoreMap":48},[53,15408,15409],{"class":55,"line":56},[53,15410,15364],{},[53,15412,15413],{"class":55,"line":86},[53,15414,15415],{}," private static List\u003CString> stuff = new ArrayList\u003C>();\n",[53,15417,15418],{"class":55,"line":126},[53,15419,15420],{}," public DemoServiceImpl() {\n",[53,15422,15423],{"class":55,"line":163},[53,15424,15425],{}," stuff.add(\"foo\");\n",[53,15427,15428],{"class":55,"line":186},[53,15429,15430],{}," stuff.add(\"bar\");\n",[53,15432,15433],{"class":55,"line":221},[53,15434,15435],{}," stuff.add(\"baz\");\n",[53,15437,15438],{"class":55,"line":242},[53,15439,860],{},[53,15441,15442],{"class":55,"line":273},[53,15443,13033],{},[53,15445,15446],{"class":55,"line":279},[53,15447,15373],{},[53,15449,15450],{"class":55,"line":496},[53,15451,15388],{},[53,15453,15454],{"class":55,"line":503},[53,15455,860],{},[53,15457,15458],{"class":55,"line":509},[53,15459,282],{},[18,15461,15462],{},"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.",[18,15464,15465],{},[1773,15466],{"alt":15467,"src":15468},"buildTypes","https://media.synyx.de/uploads//2016/08/buildTypes-1-300x266.png",[577,15470,15471,15474],{},[580,15472,15473],{},"No code checks needed (for this usecase of product flavors)",[580,15475,15476],{},"Clean separation of release code and mock code",[18,15478,15219],{},[577,15480,15481,15484,15487],{},[580,15482,15483],{},"Configuration and development are a bit trickier",[580,15485,15486],{},"Slower build speed (For each product flavor, an APK for every buildType is built)",[580,15488,15489],{},"Compilation errors in currently unused flavors may go unnoticed, as the classes in unused flavors aren’t compiled",[649,15491,15493],{"id":15492},"thats-it","That’s it",[18,15495,15496],{},"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.",[18,15498,15499,15500,986],{},"More details on the build types and product flavors can be found in\nthe ",[585,15501,15504],{"href":15502,"rel":15503},"https://developer.android.com/studio/build/build-variants.html",[589],"official documentation",[18,15506,15507],{},"As always, feel free to share your questions, thoughts and better approaches in the comments 🙂",[607,15509,989],{},{"title":48,"searchDepth":86,"depth":86,"links":15511},[15512,15513,15514,15515],{"id":13104,"depth":86,"text":14799},{"id":14860,"depth":86,"text":14861},{"id":14893,"depth":86,"text":14894},{"id":15060,"depth":86,"text":15061,"children":15516},[15517,15518,15519],{"id":15073,"depth":126,"text":15074},{"id":15230,"depth":126,"text":15231},{"id":15492,"depth":126,"text":15493},[614],"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":14787,"description":14796},"blog/android-building-apks-for-different-environments-using-build-types-and-product-flavors",[4526,13104,15529,13105,5902,15530,15531,15532],"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":15536,"title":15537,"author":15538,"body":15539,"category":15659,"date":15660,"description":48,"extension":617,"link":15661,"meta":15662,"navigation":499,"path":15663,"seo":15664,"slug":15543,"stem":15665,"tags":15666,"teaser":15670,"__hash__":15671},"blog/blog/feedback-nach-dem-bahn-modell.md","Feedback nach dem BAHN-Modell",[8053],{"type":11,"value":15540,"toc":15657},[15541,15544,15550,15553,15556,15559,15562,15573,15576,15579,15593,15596,15599,15602,15605,15608,15611,15614,15617,15624,15627,15630,15633,15636,15639,15642,15645,15648,15651],[14,15542,15537],{"id":15543},"feedback-nach-dem-bahn-modell",[18,15545,15546],{},[1773,15547],{"alt":15548,"src":15549},"\"IMG_4095\"","https://media.synyx.de/uploads//2016/08/IMG_4095-1.jpg",[18,15551,15552],{},"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.",[18,15554,15555],{},"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?",[18,15557,15558],{},"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.",[18,15560,15561],{},"Beim konstruktiven Feedback gibt es einige Fallstricke zu beachten:",[3525,15563,15564,15567,15570],{},[580,15565,15566],{},"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.",[580,15568,15569],{},"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.",[580,15571,15572],{},"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.",[18,15574,15575],{},"Das BAHN-Modell:",[18,15577,15578],{},"Das BAHN-Modell ist ein Feedback-Modell unter vielen. Was es jedoch attraktiv und interessant macht, ist der einfache\nund klar strukturierte Aufbau:",[3525,15580,15581,15584,15587,15590],{},[580,15582,15583],{},"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.",[580,15585,15586],{},"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.",[580,15588,15589],{},"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",[580,15591,15592],{},"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.",[18,15594,15595],{},"Um das BAHN-Modell greifbarer zu machen, folgt ein Beispiel aus dem Arbeitsalltag:",[18,15597,15598],{},"(Der Einstieg in das Feedback-Modell)",[18,15600,15601],{},"Feedbackgeber: „Darf ich dir Feedback geben?“",[18,15603,15604],{},"Feedbackempfänger: „Ja.“",[18,15606,15607],{},"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)“",[18,15609,15610],{},"(Der Ausstieg aus dem Feedback-Modell)",[18,15612,15613],{},"Feedbackempfänger: „Danke für das Feedback.“",[18,15615,15616],{},"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.",[18,15618,15619,15620,15623],{},"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: „",[53,15621,15622],{},"…",", 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.",[18,15625,15626],{},"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.",[18,15628,15629],{},"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.",[18,15631,15632],{},"Mit der Betonung, dass dem Feedbackgeber die „Zusammenarbeit am Herzen liegt“, eröffnet er die Nachfrage mit der Bitte\num ein weiteres Gespräch.",[18,15634,15635],{},"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.",[18,15637,15638],{},"Warum ist im BAHN-Modell keine Aufforderung oder Wunsch zur Verhaltensänderung enthalten?",[18,15640,15641],{},"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.",[18,15643,15644],{},"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.",[18,15646,15647],{},"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.",[18,15649,15650],{},"Ich wünsche viel Spaß beim Feedback geben!",[18,15652,15653],{},[1773,15654],{"alt":15655,"src":15656},"\"IMG_4094\"","https://media.synyx.de/uploads//2016/08/IMG_4094-1.jpg",{"title":48,"searchDepth":86,"depth":86,"links":15658},[],[614],"2016-08-16T14:40:26","https://synyx.de/blog/feedback-nach-dem-bahn-modell/",{},"/blog/feedback-nach-dem-bahn-modell",{"title":15537,"description":48},"blog/feedback-nach-dem-bahn-modell",[15667,15668,4232,5743,15669],"agil","feedback","werte","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…","MKef7Q2qwsDtFACkiazT83kdEg4Y1PEmVU5T-KENlkg",{"id":15673,"title":15674,"author":15675,"body":15676,"category":16549,"date":16550,"description":15685,"extension":617,"link":16551,"meta":16552,"navigation":499,"path":16553,"seo":16554,"slug":15680,"stem":16555,"tags":16556,"teaser":16560,"__hash__":16561},"blog/blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben.md","Verstehen und verstanden werden – Das Gordon-Modell im Berufsleben",[8328],{"type":11,"value":15677,"toc":16526},[15678,15681,15686,15689,15694,15697,15702,15716,15725,15728,15732,15735,15738,15743,15746,15751,15756,15759,15764,15769,15772,15777,15783,15786,15790,15800,15805,15808,15812,15827,15834,15839,15842,15845,15850,15860,15863,15868,15871,15881,15888,15894,15897,15925,15928,15932,15937,15940,15943,15946,15949,15952,15956,15959,15964,15975,15979,15996,16001,16004,16008,16130,16134,16143,16148,16151,16154,16169,16184,16189,16200,16206,16209,16238,16241,16246,16256,16261,16264,16269,16272,16277,16280,16285,16288,16292,16301,16306,16309,16312,16315,16382,16385,16389,16394,16400,16412,16419,16425,16430,16434,16437,16441,16448,16455,16462,16469],[14,15679,15674],{"id":15680},"verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben",[18,15682,15683],{},[27,15684,15685],{},"Wir alle haben Beziehungen.",[18,15687,15688],{},"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.",[5221,15690,15691],{},[18,15692,15693],{},"Egal, ob ich Menschen nahe komme oder Abstand halte, ich habe eine Beziehung zu ihnen.",[18,15695,15696],{},"Kommunikation erlaubt uns, die Form unserer Beziehungen zu verändern. Durch gute Kommunikation verbessern wir\nBeziehungen, durch schlechte Kommunikation verschlechtern wir sie.",[18,15698,15699],{},[27,15700,15701],{},"Doch wodurch zeichnet sich gute und wodurch schlechte Kommunikation aus?",[18,15703,15704,15709,15710,15715],{},[585,15705,15708],{"href":15706,"rel":15707},"https://de.wikipedia.org/wiki/Thomas_Gordon_(Psychologe)",[589],"Thomas Gordon",", ein amerikanischer Psychologe, entwickelte\ndas sogenannte ",[585,15711,15714],{"href":15712,"rel":15713},"https://de.wikipedia.org/wiki/Gordon-Modell",[589],"Gordon-Modell",", ein Kommunikationsmodell zur Lösung von\nKonflikten.",[18,15717,15718,15719,15724],{},"In seinem\nBuch ",[585,15720,15723],{"href":15721,"rel":15722},"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",[589],"Familienkonferenz",",\ndas im Jahre 1970 erschien, beschreibt Gordon erstmals dieses Kommunikationsmodell zur Lösung von Konflikten zwischen\nEltern und Kind.",[18,15726,15727],{},"Das Gordon-Modell lässt sich jedoch auf jegliche Formen von Beziehungen anwenden, sowohl im privaten, als auch auch im\nberuflichen Bereich.",[2352,15729,15731],{"id":15730},"verhaltensfenster","Verhaltensfenster",[18,15733,15734],{},"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.",[18,15736,15737],{},"Das sogenannte Verhaltensfenster veranschaulicht dies in drei unterschiedlichen Zuständen:",[18,15739,15740],{},[27,15741,15742],{},"Jemand anderes besitzt ein Problem",[18,15744,15745],{},"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.",[18,15747,15748],{},[573,15749,15750],{},"Beispiel: Arbeitskollege erzählt, dass er sich vom Chef ungerecht behandelt fühlt.",[18,15752,15753],{},[27,15754,15755],{},"Problemfreie Zone",[18,15757,15758],{},"Keiner hat ein Problem.",[18,15760,15761],{},[573,15762,15763],{},"Beispiel: Arbeitskollege erzählt von seinem Sommerurlaub.",[18,15765,15766],{},[27,15767,15768],{},"Ich besitze ein Problem",[18,15770,15771],{},"Ich habe ein Problem, das mir schadet oder weh tut – und das liegt möglicherweise an der Verhaltensweise einer anderen\nPerson.",[18,15773,15774],{},[573,15775,15776],{},"Beispiel: Arbeitskollege surft öfters privat im Internet statt seine Arbeit zu erledigen, die dann an mir hängen\nbleibt.",[18,15778,15779],{},[1773,15780],{"alt":15781,"src":15782},"\"Verhaltensfenster\"","https://media.synyx.de/uploads//2016/07/verhaltensfenster4-1.png",[18,15784,15785],{},"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.",[2352,15787,15789],{"id":15788},"aktives-zuhören","Aktives Zuhören",[5221,15791,15792,15795],{},[18,15793,15794],{},"In der zwischenmenschlichen Kommunikation geht es darum, zu verstehen und verstanden zu werden.",[18,15796,15797,15798],{},"– ",[573,15799,15708],{},[18,15801,15802],{},[27,15803,15804],{},"Wer ein Problem hat, empfindet negative Gefühle.",[18,15806,15807],{},"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.",[649,15809,15811],{"id":15810},"beispiel","Beispiel",[5221,15813,15814,15817,15822],{},[18,15815,15816],{},"Gespräch zwischen zwei Kollegen beim Mittagessen:",[18,15818,15819],{},[573,15820,15821],{},"„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.“",[18,15823,15824],{},[573,15825,15826],{},"„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?“",[18,15828,15829,15830,15833],{},"Einerseits erfährt derjenige mit dem Problem Zustimmung ",[573,15831,15832],{},"„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.",[18,15835,15836],{},[27,15837,15838],{},"Hilfe zur Selbsthilfe",[18,15840,15841],{},"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.",[18,15843,15844],{},"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.",[18,15846,15847],{},[27,15848,15849],{},"Verschlüsselte Botschaften",[18,15851,15852,15853,15856,15857,986],{},"Teilweise können Probleme ganz klar geäußert werden, durch Äußerungen wie ",[573,15854,15855],{},"„Das Geräusch hat mich erschreckt“"," oder\n",[573,15858,15859],{},"„Ich bin müde und deshalb nicht so gut drauf“",[18,15861,15862],{},"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.",[18,15864,15865],{},[27,15866,15867],{},"Botschaften entschlüsseln",[18,15869,15870],{},"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.",[18,15872,15873,15874,3839,15877,15880],{},"Wenn der Sinn richtig wiedergegeben wurde, wird der Sprecher zustimmen – und sei es nur durch ein kurzes ",[573,15875,15876],{},"„richtig“",[573,15878,15879],{},"„ja“"," – und fortfahren.",[18,15882,15883,15884,15887],{},"Wenn der Sinn nicht korrekt entschlüsselt wurde, wird der Sprecher korrigieren ",[573,15885,15886],{},"„Nein, ich meine eher …“"," und erst\ndanach fortfahren.",[18,15889,15890],{},[1773,15891],{"alt":15892,"src":15893},"\"Botschaften entschlüsseln\"","https://media.synyx.de/uploads//2016/07/entschluesseln.jpg",[649,15895,15811],{"id":15896},"beispiel-1",[5221,15898,15899,15901,15905,15910,15915,15920],{},[18,15900,15816],{},[18,15902,15903],{},[573,15904,15821],{},[18,15906,15907],{},[573,15908,15909],{},"„Meinst du, XY hat zu schnell geredet, sodass es dir schwer fiel, gedanklich zu folgen?“",[18,15911,15912],{},[573,15913,15914],{},"„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.“",[18,15916,15917],{},[573,15918,15919],{},"„Du hast Angst das neue Wissen nicht so schnell aufsaugen zu können wie deine Kollegen.“",[18,15921,15922],{},[573,15923,15924],{},"„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.“",[18,15926,15927],{},"In diesem Gespräch erfährt derjenige mit dem Problem Verständnis vom Zuhörer und findet gleichzeitig selbst zur Lösung\nseines Problems.",[649,15929,15931],{"id":15930},"best-practices","Best Practices",[18,15933,15934],{},[27,15935,15936],{},"Ich kann nur richtig zuhören…",[18,15938,15939],{},"… wenn ich aufmerksam bin. Ich kann nicht aufmerksam sein, wenn ich gerade eigentlich keine Zeit oder Lust zum Zuhören\nhabe.",[18,15941,15942],{},"… wenn ich den anderen annehmen kann wie er ist, ohne seine Aussagen direkt zu bewerten oder zu beurteilen.",[18,15944,15945],{},"… wenn ich still bin. Wenn ich gleichzeitig rede oder überlege, was ich als nächstes sage, kann ich nicht wirklich\nkonzentriert zuhören.",[18,15947,15948],{},"… wenn ich ernsthaft versuche mich in das Gesagte einzufühlen und die entschlüsselte Botschaft in meinen Worten\nzurückzumelden.",[18,15950,15951],{},"… wenn ich vermeide, den Retter zu spielen, indem ich dem Problembesitzer Lösungsvorschläge vorsetze.",[2352,15953,15955],{"id":15954},"türöffner-vs-kommunikationssperren","Türöffner vs. Kommunikationssperren",[18,15957,15958],{},"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.",[18,15960,15961],{},[27,15962,15963],{},"Gespräche einleiten und fortführen",[18,15965,15966,15967,15970,15971,15974],{},"Türöffner sind offene Fragen, die nicht mit einem einfachen ",[573,15968,15969],{},"„Ja“"," oder ",[573,15972,15973],{},"„Nein“"," beantwortet werden können und dadurch\nein Gespräch initiieren.",[649,15976,15978],{"id":15977},"beispiele","Beispiele",[5221,15980,15981,15986,15991],{},[18,15982,15983],{},[573,15984,15985],{},"„Mich würde deine Meinung dazu interessieren.“",[18,15987,15988],{},[573,15989,15990],{},"„Stimmt etwas nicht?“",[18,15992,15993],{},[573,15994,15995],{},"„Was denkst du darüber?“",[18,15997,15998],{},[27,15999,16000],{},"Gespräche im Keim ersticken",[18,16002,16003],{},"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.",[649,16005,16007],{"id":16006},"die-12-kommunikationssperren-nach-gordon","Die 12 Kommunikationssperren nach Gordon",[5221,16009,16010,16015,16020,16025,16030,16035,16040,16045,16050,16055,16060,16065,16070,16075,16080,16085,16090,16095,16100,16105,16110,16115,16120,16125],{},[18,16011,16012],{},[27,16013,16014],{},"Befehlen, Anordnen, Bestimmen",[18,16016,16017],{},[573,16018,16019],{},"„Das muss heute noch getan werden, da gibt es nichts zu diskutieren.“",[18,16021,16022],{},[27,16023,16024],{},"Drohen, Warnen",[18,16026,16027],{},[573,16028,16029],{},"„Wenn das bis nächste Woche nicht erledigt ist, dann haben wir ein Problem miteinander.“",[18,16031,16032],{},[27,16033,16034],{},"Moralisieren, Predigen",[18,16036,16037],{},[573,16038,16039],{},"„Stell dich nicht so an, da muss jeder mal durch.“",[18,16041,16042],{},[27,16043,16044],{},"Ratschläge erteilen, Lösungen vorgeben",[18,16046,16047],{},[573,16048,16049],{},"„Also an deiner Stelle würde ich ja …“",[18,16051,16052],{},[27,16053,16054],{},"Vorträge halten, belehren, Fakten liefern",[18,16056,16057],{},[573,16058,16059],{},"„Also eigentlich sind die Fakten ja folgendermaßen …“",[18,16061,16062],{},[27,16063,16064],{},"Urteile fällen, Vorwürfe machen, kritisieren",[18,16066,16067],{},[573,16068,16069],{},"„Das ist doch Schwachsinn, was du da sagst!“",[18,16071,16072],{},[27,16073,16074],{},"Loben, schmeicheln",[18,16076,16077],{},[573,16078,16079],{},"„Das kriegst du schon hin!“",[18,16081,16082],{},[27,16083,16084],{},"Beschimpfen, lächerlich machen",[18,16086,16087],{},[573,16088,16089],{},"„Tu doch nicht so, als ob du jetzt das erste Mal davon hörst!“",[18,16091,16092],{},[27,16093,16094],{},"Interpretieren, diagnostizieren, analysieren",[18,16096,16097],{},[573,16098,16099],{},"„Das sagst du doch jetzt nur, weil …“",[18,16101,16102],{},[27,16103,16104],{},"Trösten, Sympathie bekunden",[18,16106,16107],{},[573,16108,16109],{},"„Nimm dir das nicht so zu Herzen.“",[18,16111,16112],{},[27,16113,16114],{},"Forschen, fragen, verhören",[18,16116,16117],{},[573,16118,16119],{},"„Was hast du getan, um eine Lösung zu finden?“",[18,16121,16122],{},[27,16123,16124],{},"Zurückziehen, sarkastisch reagieren, ausweichen",[18,16126,16127],{},[573,16128,16129],{},"„Da musst du einfach drüber stehen. Komm, lass uns zusammen einen Kaffee holen gehen, dann kommst du auf andere\nGedanken.“",[2352,16131,16133],{"id":16132},"ich-botschaften","Ich-Botschaften",[5221,16135,16136,16139],{},[18,16137,16138],{},"Wenn ich möchte, dass andere Menschen mich verstehen, muss ich über mich selbst sprechen.",[18,16140,15797,16141],{},[573,16142,15708],{},[18,16144,16145],{},[27,16146,16147],{},"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.",[18,16149,16150],{},"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.",[649,16152,15978],{"id":16153},"beispiele-1",[5221,16155,16156,16161,16166],{},[18,16157,16158],{},[27,16159,16160],{},"Du-Botschaft",[18,16162,16163],{},[573,16164,16165],{},"„Du bist unhöflich“",[18,16167,16168],{},"Hier muss die konfrontierte Person mehr oder weniger raten, was genau ich als unhöflich in ihrem Verhalten empfinde.",[5221,16170,16171,16176,16181],{},[18,16172,16173],{},[27,16174,16175],{},"Ich-Botschaft",[18,16177,16178],{},[573,16179,16180],{},"„Wenn du mich unterbrichst und nicht ausreden lässt, habe ich das Gefühl, dass meine Teilnahme in diesem Meeting\nnicht erwünscht ist.“",[18,16182,16183],{},"Hier wird konkret beschrieben, was am Verhalten der konfrontierten Person als unhöflich empfunden wird.",[18,16185,16186],{},[27,16187,16188],{},"Eine Ich-Botschaft setzt sich aus folgenden drei Komponenten zusammen:",[3525,16190,16191,16194,16197],{},[580,16192,16193],{},"Eine neutrale, vorwurfsfreie Beschreibung, die keine Verallgemeinerungen oder Interpretationen enthält",[580,16195,16196],{},"Greifbare, konkrete Folgen des Verhaltens der konfrontierten Person für mich",[580,16198,16199],{},"Meine persönlichen Empfindungen angesichts der Situation",[18,16201,16202],{},[1773,16203],{"alt":16204,"src":16205},"\"Ich-Botschaft\"","https://media.synyx.de/uploads//2016/07/ich-botschaft.jpg",[649,16207,15811],{"id":16208},"beispiel-2",[5221,16210,16211,16219,16225,16230],{},[18,16212,16213,3566,16216],{},[573,16214,16215],{},"„Wenn du nicht pünktlich zum vereinbarten Termin erscheinst,",[53,16217,16218],{},"neutrale Beschreibung",[18,16220,16221,16224],{},[573,16222,16223],{},"müssen wir auf dich warten und können nicht pünktlich beginnen, weshalb der Termin dann länger dauert."," [greifbare",[5221,16226,16227],{},[18,16228,16229],{},"Wirkung]",[18,16231,16232,3566,16235],{},[573,16233,16234],{},"Da ich häufig Folgetermine habe, muss ich direkt zum nächsten Termin hetzen. Dadurch fühle ich mich oft in Stress\nversetzt.“",[53,16236,16237],{},"persönliche Empfindung",[649,16239,15931],{"id":16240},"best-practices-1",[18,16242,16243],{},[27,16244,16245],{},"Vermeiden von Verallgemeinerungen und Interpretationen",[18,16247,16248,16249,15970,16252,16255],{},"Beim Beschreiben von Verhalten sollte auf Verallgemeinerungen wie ",[573,16250,16251],{},"„immer“",[573,16253,16254],{},"„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.",[18,16257,16258],{},[27,16259,16260],{},"Zeitnah und situationsbezogen klären",[18,16262,16263],{},"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.",[18,16265,16266],{},[27,16267,16268],{},"Angemessene Ich-Botschaften",[18,16270,16271],{},"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.",[18,16273,16274],{},[27,16275,16276],{},"Ärger ist kein primäres Gefühl",[18,16278,16279],{},"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.",[18,16281,16282],{},[27,16283,16284],{},"Lösung offen halten",[18,16286,16287],{},"Eine Ich-Botschaft sollte keine Lösung enthalten, sondern dem Gegenüber die Chance geben, sein Verhalten selbst zu\nändern.",[2352,16289,16291],{"id":16290},"umschalten","Umschalten",[5221,16293,16294,16297],{},[18,16295,16296],{},"Wenn ich Hilfe haben will, muss ich auch bereit sein, Hilfe zu leisten.",[18,16298,15797,16299],{},[573,16300,15708],{},[18,16302,16303],{},[27,16304,16305],{},"Menschliche Beziehungen und menschliches Verhalten sind komplex.",[18,16307,16308],{},"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.",[18,16310,16311],{},"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.",[649,16313,15811],{"id":16314},"beispiel-3",[5221,16316,16317,16320,16327,16333,16336,16342,16347,16352,16357,16362,16367,16372,16377],{},[18,16318,16319],{},"Ich warte seit drei Tagen auf eine E-Mail von meinem Arbeitskollegen mit Daten, die ich zur Erstellung einer\nwichtigen Präsentation brauche.",[18,16321,16322,16323,16326],{},"Ich konfrontiere ihn: ",[573,16324,16325],{},"„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)",[18,16328,16329,16330],{},"Er entgegnet: ",[573,16331,16332],{},"„Ich dachte, es eilt nicht so sehr, aber wenn du dich so aufregst, kümmere ich sofort darum und lasse\ndir die Daten zukommen.“",[18,16334,16335],{},"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.",[18,16337,16338,16341],{},[573,16339,16340],{},"„Du denkst, ich bin ungeduldig und nimmst mir das übel.“"," (Zuhören)",[18,16343,16344],{},[573,16345,16346],{},"„Naja, eigentlich hatte ich vor, mich heute darum zu kümmern, aber jetzt fühle ich mich gehetzt und dadurch in Stress\nversetzt.“",[18,16348,16349,16341],{},[573,16350,16351],{},"„Du hast das Gefühl, ich dränge dich zu etwas, das du sowieso tun wolltest.“",[18,16353,16354],{},[573,16355,16356],{},"„Richtig.“",[18,16358,16359,16326],{},[573,16360,16361],{},"„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.“",[18,16363,16364],{},[573,16365,16366],{},"„Ich hatte einfach viele Termine und bin schlicht und ergreifend noch nicht dazu gekommen.“",[18,16368,16369,16341],{},[573,16370,16371],{},"„Das verstehe ich. Wenn man selbst viel zu tun hat, kann das leicht passieren.“",[18,16373,16374],{},[573,16375,16376],{},"„Ich kümmere mich noch heute darum. Spätestens heute Abend hast du meine E-Mail mit den aufbereiteten Daten.“",[18,16378,16379],{},[573,16380,16381],{},"„Danke dir!“",[18,16383,16384],{},"Durch aktives Zuhören konnte die emotionale Temperatur des Gesprächspartners etwas heruntergekühlt und das Gespräch mit\neinem positiven Ausgang verlassen werden.",[2352,16386,16388],{"id":16387},"zusammenfassung","Zusammenfassung",[18,16390,16391],{},[27,16392,16393],{},"Wenn ich meine Beziehungen verbessern möchte, muss ich lernen…",[18,16395,16396,16397,8526],{},"… zu identifizieren, wer in einer Situation das Problem besitzt (",[585,16398,15731],{"href":16399},"#verhaltensfenster",[18,16401,16402,16403,16407,16408,8526],{},"… wie ich Gespräche einleiten und fortführen kann (",[585,16404,16406],{"href":16405},"#tueroffner","Türöffner",") und was ich vermeiden sollte, um\nGesprächsversuche nicht im Keim zu ersticken (",[585,16409,16411],{"href":16410},"#kommunikationssperren","Kommunikationssperren",[18,16413,16414,16415,8526],{},"… wie ich richtig zuhören kann (",[585,16416,16418],{"href":16417},"#aktives-zuhoeren","aktives Zuhören",[18,16420,16421,16422,8526],{},"… wie ich mich mitteilen kann, sodass andere mich verstehen (",[585,16423,16133],{"href":16424},"#ich-botschaften",[18,16426,16427],{},[1773,16428],{"alt":15781,"src":16429},"https://media.synyx.de/uploads//2016/07/verhaltensfenster5.png",[2352,16431,16433],{"id":16432},"anmerkung","Anmerkung",[18,16435,16436],{},"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.",[2352,16438,16440],{"id":16439},"literatur","Literatur",[18,16442,16443],{},[585,16444,16447],{"href":16445,"rel":16446},"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",[589],"Familienkonferenz: Die Lösung von Konflikten zwischen Eltern und Kind",[18,16449,16450],{},[585,16451,16454],{"href":16452,"rel":16453},"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",[589],"Die Neue Familienkonferenz: Kinder erziehen ohne zu strafen",[18,16456,16457],{},[585,16458,16461],{"href":16459,"rel":16460},"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",[589],"Gute Beziehungen: Wie sie entstehen und stärker werden",[18,16463,16464],{},[585,16465,16468],{"href":16466,"rel":16467},"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",[589],"Managerkonferenz: Effektives Führungstraining",[18,16470,16471,16472,16477,16482,16487,16492,16497,16502,16507,16512,16517,16522],{},"Tags:\n",[585,16473,16476],{"href":16474,"rel":16475},"http://localhost:8080/blog/tags/aktives-zuhoeren/",[589],"aktives zuhören",[585,16478,16481],{"href":16479,"rel":16480},"http://localhost:8080/blog/tags/familienkonferenz/",[589],"familienkonferenz",[585,16483,16486],{"href":16484,"rel":16485},"http://localhost:8080/blog/tags/gewaltfreie-kommunikation/",[589],"gewaltfreie kommunikation",[585,16488,16491],{"href":16489,"rel":16490},"http://localhost:8080/blog/tags/gordon/",[589],"gordon",[585,16493,16496],{"href":16494,"rel":16495},"http://localhost:8080/blog/tags/gordon-modell/",[589],"gordon-modell",[585,16498,16501],{"href":16499,"rel":16500},"http://localhost:8080/blog/tags/ich-botschaft/",[589],"ich-botschaft",[585,16503,16506],{"href":16504,"rel":16505},"http://localhost:8080/blog/tags/kommunikation/",[589],"Kommunikation",[585,16508,16511],{"href":16509,"rel":16510},"http://localhost:8080/blog/tags/kommunikationssperren/",[589],"kommunikationssperren",[585,16513,16516],{"href":16514,"rel":16515},"http://localhost:8080/blog/tags/managerkonferenz/",[589],"managerkonferenz",[585,16518,16521],{"href":16519,"rel":16520},"http://localhost:8080/blog/tags/tueroeffner/",[589],"türöffner",[585,16523,15730],{"href":16524,"rel":16525},"http://localhost:8080/blog/tags/verhaltensfenster/",[589],{"title":48,"searchDepth":86,"depth":86,"links":16527},[16528,16529,16534,16538,16543,16546,16547,16548],{"id":15730,"depth":86,"text":15731},{"id":15788,"depth":86,"text":15789,"children":16530},[16531,16532,16533],{"id":15810,"depth":126,"text":15811},{"id":15896,"depth":126,"text":15811},{"id":15930,"depth":126,"text":15931},{"id":15954,"depth":86,"text":15955,"children":16535},[16536,16537],{"id":15977,"depth":126,"text":15978},{"id":16006,"depth":126,"text":16007},{"id":16132,"depth":86,"text":16133,"children":16539},[16540,16541,16542],{"id":16153,"depth":126,"text":15978},{"id":16208,"depth":126,"text":15811},{"id":16240,"depth":126,"text":15931},{"id":16290,"depth":86,"text":16291,"children":16544},[16545],{"id":16314,"depth":126,"text":15811},{"id":16387,"depth":86,"text":16388},{"id":16432,"depth":86,"text":16433},{"id":16439,"depth":86,"text":16440},[614],"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":15674,"description":15685},"blog/verstehen-und-verstanden-werden-das-gordon-modell-im-berufsleben",[16557,16481,16558,16491,16496,16501,6608,16511,16516,16559,15730],"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…","RHa85ZTNg03OaLn9W6ZjdZpUq0vZSP0DnAGuvOgPMsc",{"id":16563,"title":16564,"author":16565,"body":16567,"category":16633,"date":16634,"description":16635,"extension":617,"link":16636,"meta":16637,"navigation":499,"path":16638,"seo":16639,"slug":16641,"stem":16642,"tags":16643,"teaser":16646,"__hash__":16647},"blog/blog/our-days-parallel-2016.md","Our days @ para//el 2016",[16566],"clausen",{"type":11,"value":16568,"toc":16631},[16569,16572,16587,16596,16605,16608,16613,16622,16625,16628],[14,16570,16564],{"id":16571},"our-days-parael-2016",[18,16573,16574,16575,16580,16581,16586],{},"Last week Stefan and me took part as guests at the ",[585,16576,16579],{"href":16577,"rel":16578},"http://parallelcon.de/",[589],"para//el conference"," in Heidelberg. The\nactual ",[585,16582,16585],{"href":16583,"rel":16584},"http://parallelcon.de/programm.php",[589],"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! 🙂",[18,16588,16589,16590,16595],{},"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 ",[585,16591,16594],{"href":16592,"rel":16593},"https://en.wikipedia.org/wiki/Supercomputer",[589],"HPC",", but\nrather something that can be discussed in a broader sense.",[18,16597,16598,16599,16604],{},"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 ",[585,16600,16603],{"href":16601,"rel":16602},"https://en.wikipedia.org/wiki/Numerical_analysis",[589],"numerical precision errors",", it\nmight be more visible to those environments and reveals the great spectrum of possible talks everyone could attend.",[18,16606,16607],{},"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.",[18,16609,16610],{},[573,16611,16612],{},"In a concurrent world, how much precision would you be willing to relinquish, for a correct view of the shared world?",[18,16614,16615,16616,16621],{},"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 ",[585,16617,16620],{"href":16618,"rel":16619},"http://jcip.net/",[589],"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.",[18,16623,16624],{},"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.",[18,16626,16627],{},"Well, might be relevant for 2017, might not be relevant, we will see.",[18,16629,16630],{},"Thanks to everyone who made the conference to what we’ve seen in 2016!",{"title":48,"searchDepth":86,"depth":86,"links":16632},[],[613],"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":16564,"description":16640},"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",[16644,3491,290,16645],"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":16649,"title":16650,"author":16651,"body":16652,"category":17058,"date":17059,"description":17060,"extension":617,"link":17061,"meta":17062,"navigation":499,"path":17063,"seo":17064,"slug":17066,"stem":17067,"tags":17068,"teaser":17072,"__hash__":17073},"blog/blog/springboot-reactjs-progressive-enhancement-based-on-list-sorting.md","springboot & reactjs #2 | progressive enhancement based on list sorting",[5191],{"type":11,"value":16653,"toc":17050},[16654,16657,16666,16669,16672,16676,16721,16723,16726,16729,16734,16741,16744,16747,16754,16776,16787,16790,16797,16804,16807,16814,16833,16839,16842,16845,16848,16862,16865,16888,16902,16905,16915,16921,16928,16931,16934,16937,16951,16970,16973,16982,16998,17002,17026,17032,17036,17043,17048],[14,16655,16650],{"id":16656},"springboot-reactjs-2-progressive-enhancement-based-on-list-sorting",[18,16658,16659,16660,16665],{},"This is the second article of a springboot & reactjs article series about server side rendering and progressive\nenhancement. In the ",[585,16661,16664],{"href":16662,"rel":16663},"https://synyx.de/2016/03/universal-webapp-development-with-spring-boot-react/",[589],"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…",[18,16667,16668],{},"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/",[16670,16671],"hr",{},[2352,16673,16675],{"id":16674},"springboot-reactjs-article-series","springboot & reactjs article series",[3525,16677,16678,16686,16715,16718],{},[580,16679,16680,16685],{},[585,16681,16684],{"href":16682,"rel":16683},"https://synyx.de/2016/03/springboot-reactjs-server-side-rendering",[589],"server side rendering"," ✅",[580,16687,16688,16689],{},"progressive enhancement based on list sorting 🆕\n",[577,16690,16691,16697,16703,16709],{},[580,16692,16693],{},[585,16694,16696],{"href":16695},"#html-form-and-server-side-rendering","HTML form and server side rendering",[580,16698,16699],{},[585,16700,16702],{"href":16701},"#enhance-the-client","Enhance the client",[580,16704,16705],{},[585,16706,16708],{"href":16707},"#make-the-back-button-work-again","Make the back button work again",[580,16710,16711],{},[585,16712,16714],{"href":16713},"#what-do-we-have-learned-so-far","What do we have learned so far",[580,16716,16717],{},"improving developer experience",[580,16719,16720],{},"lessons learned",[16670,16722],{},[18,16724,16725],{},"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",[18,16727,16728],{},"But let’s take one step after another…",[18,16730,16731],{},[27,16732,16733],{},"tl;dr",[18,16735,16736],{},[585,16737,16740],{"href":16738,"rel":16739},"https://github.com/synyx/springboot-reactjs-demo",[589],"project source code is available on github",[2352,16742,16696],{"id":16743},"html-form-and-server-side-rendering",[18,16745,16746],{},"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.",[18,16748,16749,16750,16753],{},"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 ",[50,16751,16752],{},"name=\"sort\""," and a label to\nincrease the clickable area.",[18,16755,16756,16757,16760,16761,16764,16765,16768,16769,16771,16772,16775],{},"In the previous blog the ",[27,16758,16759],{},"ProductList"," was the only component to render and therefore the entry in ",[27,16762,16763],{},"main.js",". But\nthe new ",[27,16766,16767],{},"ProductFilter"," is not part of the list. The ",[27,16770,16759],{}," reacts to the filter parameter set by the user.\nSo we have to create an ",[27,16773,16774],{},"App"," container that combines our awesome ProductList and ProductFilter components.",[18,16777,16778,16779,16782,16783,16786],{},"Since we have a container now to combine the ProductList and the ProductFilter components we also have to adjust the\n",[50,16780,16781],{},"global.renderServer"," function. First we add a second parameter ",[50,16784,16785],{},"sortBy"," to be able to render the selected radio button.\nThen we must render the new App container instead of the plain ProductList.",[18,16788,16789],{},"That’s it for the frontend part!",[18,16791,16792,16793,16796],{},"Next we need to extend the backend controller to process the ",[50,16794,16795],{},"sort"," request parameter defined in the ProductFilter form\nand use it to sort the product list.",[18,16798,16799,16800,16803],{},"Additionally the ",[50,16801,16802],{},"React#renderProducts"," method must be extended, too, of course.",[18,16805,16806],{},"And that’s it with the backend part as well!",[18,16808,16809,16810,16813],{},"Now build the frontend, start the spring boot app, open your browser on ",[50,16811,16812],{},"http://localhost:8080"," and start sorting the\nawesome product list 🙂",[43,16815,16817],{"className":13667,"code":16816,"language":13669,"meta":48,"style":48},"\n$ npm run build\n$ ./gradlew bootRun\n\n",[50,16818,16819,16823,16828],{"__ignoreMap":48},[53,16820,16821],{"class":55,"line":56},[53,16822,500],{"emptyLinePlaceholder":499},[53,16824,16825],{"class":55,"line":86},[53,16826,16827],{},"$ npm run build\n",[53,16829,16830],{"class":55,"line":126},[53,16831,16832],{},"$ ./gradlew bootRun\n",[18,16834,16835],{},[1773,16836],{"alt":16837,"src":16838},"awesome_productlist_sorting","https://media.synyx.de/uploads//2016/04/awesome_productlist_sorting.gif",[2352,16840,16702],{"id":16841},"enhance-the-client",[18,16843,16844],{},"So far our awesome product list is fully functional. Let’s recap what we can do now.",[18,16846,16847],{},"We are able to:",[577,16849,16850,16853,16856,16859],{},[580,16851,16852],{},"see the awesome product info",[580,16854,16855],{},"sort the awesome products by name or price",[580,16857,16858],{},"use the browser’s back and forward button (static site!)",[580,16860,16861],{},"bookmark every single view",[18,16863,16864],{},"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.",[18,16866,16867,16868,16872,16873,16877,16878,7143,16881,99,16884,16887],{},"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](",[585,16869,16870],{"href":16870,"rel":16871},"http://git@gitlab-test.synyx.coffee",[589],":\nseber/SynyxBibliothek.git ",[585,16874,16875],{"href":16875,"rel":16876},"https://facebook.github.io/react/docs/component-specs.html#lifecycle-methods",[589],") like ",[50,16879,16880],{},"onClick",[50,16882,16883],{},"onChange",[50,16885,16886],{},"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.",[18,16889,16890,16891,16893,16894,16896,16897,12136],{},"You may ask why we are subscribing our submit handler via ",[50,16892,16886],{}," on the form and not the ",[50,16895,16880],{}," 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 ",[585,16898,16901],{"href":16899,"rel":16900},"https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements",[589],"HTMLFormControlsCollection",[18,16903,16904],{},"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? 😉",[18,16906,16907,16908,16910,16911,16914],{},"So additionally to the ",[50,16909,16781],{}," function used by Nashorn we need a second function ",[50,16912,16913],{},"window.renderClient","\nthat we have to call on the client side (browser) as we will see later in this tutorial.",[18,16916,16917,16918,16920],{},"Next we have to add the initial rendering to the index.html template. Of course, we must call ",[50,16919,16913],{}," 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).",[18,16922,16923,16924,16927],{},"Back to the Java backend we have to inject the initial product list and the sortBy value into the server side model of\nthe ",[50,16925,16926],{},"ProductController.java"," class. Additionally we add a second endpoint to provide the sorted product list as json.",[18,16929,16930],{},"That’s it!",[18,16932,16933],{},"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.",[2352,16935,16708],{"id":16936},"make-the-back-button-work-again",[18,16938,16939,16940,16945,16946,986],{},"Okay, at first we should face the browser url. With HTML5 we’ve gained\nthe ",[585,16941,16944],{"href":16942,"rel":16943},"https://developer.mozilla.org/en-US/docs/Web/API/History_API",[589],"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 ",[585,16947,16950],{"href":16948,"rel":16949},"https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries",[589],"window.history.pushState",[18,16952,16953,16954,16957,16958,16961,16962,16965,16966,16969],{},"Next we want to listen to the browsers back and forward buttons. This can be implemented with subscribing to the\n",[50,16955,16956],{},"popstate"," event. The subscription is done within ",[50,16959,16960],{},"componentDidMount"," since we only want to subscribe in the browser\nenvironment. ",[50,16963,16964],{},"Constructor"," and it’s counterpart ",[50,16967,16968],{},"componentWillMount"," are both called on server side creating the static\nhtml markup.",[18,16971,16972],{},"Finally we made it 🙂",[18,16974,16975,16976,16981],{},"Go on, build the frontend, start the spring boot app, open ",[573,16977,16978],{},[585,16979,16812],{"href":16812,"rel":16980},[589]," and admire our awesome progressively\nenhanced product list. Functional without JavaScript and even better with enabled JavaScript.",[43,16983,16984],{"className":13667,"code":16816,"language":13669,"meta":48,"style":48},[50,16985,16986,16990,16994],{"__ignoreMap":48},[53,16987,16988],{"class":55,"line":56},[53,16989,500],{"emptyLinePlaceholder":499},[53,16991,16992],{"class":55,"line":86},[53,16993,16827],{},[53,16995,16996],{"class":55,"line":126},[53,16997,16832],{},[2352,16999,17001],{"id":17000},"what-do-we-have-learned-so-far","What do we have learned so far?",[577,17003,17004,17014,17023],{},[580,17005,17006,17007,17010,17011],{},"use plain HTML ",[50,17008,17009],{},"\u003Cform>"," element and enhance it with JavaScript and ",[50,17012,17013],{},"event.preventDefault",[580,17015,17016,17017,17019,17020,17022],{},"use ",[50,17018,16944],{}," and ",[50,17021,16956],{}," event to handle the browser back/forward button on the client",[580,17024,17025],{},"manually rebuilding and reloading the ReactJS app still sucks (autoreload would be cool, right)",[18,17027,17028],{},[1773,17029],{"alt":17030,"src":17031},"progressive_js","https://media.synyx.de/uploads//2016/04/progressive_js.gif",[2352,17033,17035],{"id":17034},"the-next-steps-will-be","The next steps will be",[577,17037,17038,17041],{},[580,17039,17040],{},"using webpack to enhance the developer experience",[580,17042,16720],{},[18,17044,17045],{},[27,17046,17047],{},"Stay tuned and keep learning!",[607,17049,989],{},{"title":48,"searchDepth":86,"depth":86,"links":17051},[17052,17053,17054,17055,17056,17057],{"id":16674,"depth":86,"text":16675},{"id":16743,"depth":86,"text":16696},{"id":16841,"depth":86,"text":16702},{"id":16936,"depth":86,"text":16708},{"id":17000,"depth":86,"text":17001},{"id":17034,"depth":86,"text":17035},[613],"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":16650,"description":17065},"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",[290,6991,17069,17070,1010,17071],"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…","-bm-m-HOVsX8sO3gHZgQPhuKHIL62ikfjs8JOfJb9w8",{"id":17075,"title":17076,"author":17077,"body":17079,"category":17247,"date":17248,"description":17088,"extension":617,"link":17249,"meta":17250,"navigation":499,"path":17251,"seo":17252,"slug":17253,"stem":17254,"tags":17255,"teaser":17261,"__hash__":17262},"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",[17078],"tamara",{"type":11,"value":17080,"toc":17245},[17081,17084,17089,17092,17097,17100,17103,17106,17111,17114,17120,17125,17131,17138,17144,17150,17156,17162,17168,17177,17182,17185,17188,17191,17196,17199,17202,17207,17210,17215,17218,17223,17226,17231,17236,17239,17242],[14,17082,17076],{"id":17083},"anwendungsprojekt-der-hochschule-karlsruhe-bei-synyx-prototyp-für-ein-resource-planning-board",[18,17085,17086],{},[27,17087,17088],{},"Einleitung",[18,17090,17091],{},"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.",[18,17093,17094],{},[27,17095,17096],{},"Über das Anwendungsprojekt",[18,17098,17099],{},"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.",[18,17101,17102],{},"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.",[18,17104,17105],{},"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.",[18,17107,17108],{},[27,17109,17110],{},"Das Projekt: Resource Planning Board",[18,17112,17113],{},"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.",[18,17115,17116],{},[1773,17117],{"alt":17118,"src":17119},"\"UI Resource Planning Board\"","https://media.synyx.de/uploads//2016/03/screenshot.png",[18,17121,17122],{},[27,17123,17124],{},"Technisch haben wir folgende Frameworks eingesetzt:",[18,17126,17127,17128,17130],{},"Als Technologie im Backend wurde ",[573,17129,8936],{}," 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.",[18,17132,17133,17134,17137],{},"Als Datenbank wurde ",[573,17135,17136],{},"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.",[18,17139,17140,17143],{},[573,17141,17142],{},"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.",[18,17145,17146,17149],{},[573,17147,17148],{},"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.",[18,17151,17152,17155],{},[573,17153,17154],{},"Bower"," wird benutzt, um Front-End-Abhängigkeiten für unsere Anwendung wie z.B. AngularJS flexibel laden zu können.",[18,17157,17158,17161],{},[573,17159,17160],{},"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.",[18,17163,17164,17167],{},[573,17165,17166],{},"Karma"," ist ein Plug-In für Grunt, welches für das Ausführen von Tests verantwortlich ist.",[18,17169,17170,1628,17173,17176],{},[573,17171,17172],{},"JUnit",[573,17174,17175],{},"Jasmine"," sind etablierte Frameworks für Unit-Tests in Java bzw. JavaScript.",[18,17178,17179],{},[27,17180,17181],{},"Zusammenarbeit mit synyx",[18,17183,17184],{},"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.",[18,17186,17187],{},"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.",[18,17189,17190],{},"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.",[18,17192,17193],{},[27,17194,17195],{},"Unsere Eindrücke vom AWP",[18,17197,17198],{},"Im Folgenden einige Gedanken aus unserem Erfahrungsbericht zum AWP:",[18,17200,17201],{},"Andre Eberhard:",[18,17203,17204],{},[573,17205,17206],{},"„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.“",[18,17208,17209],{},"Volkan Güllü:",[18,17211,17212],{},[573,17213,17214],{},"„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.“",[18,17216,17217],{},"Marvin Lackus:",[18,17219,17220],{},[573,17221,17222],{},"„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…“",[18,17224,17225],{},"Tamara Tunczinger:",[18,17227,17228],{},[573,17229,17230],{},"„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.“",[18,17232,17233],{},[27,17234,17235],{},"Schlusswort",[18,17237,17238],{},"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.",[18,17240,17241],{},"Die Ergebnisse wurden am Ende Februar bei synyx vorgestellt und anschließend mit einem angenehmen Grillabend gefeiert.",[18,17243,17244],{},"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":48,"searchDepth":86,"depth":86,"links":17246},[],[614],"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":17076,"description":17088},"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",[17256,17257,17258,17259,17260],"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.…","GiUYkAC3ZLs58bIjU1xOZZ8DtTzKorqpoyS-wHamXIU",{"id":17264,"title":17265,"author":17266,"body":17267,"category":17849,"date":17850,"description":17851,"extension":617,"link":17852,"meta":17853,"navigation":499,"path":17854,"seo":17855,"slug":17856,"stem":17857,"tags":17858,"teaser":17859,"__hash__":17860},"blog/blog/springboot-reactjs-server-side-rendering.md","springboot & reactjs #1 | server side rendering",[5191],{"type":11,"value":17268,"toc":17842},[17269,17272,17275,17277,17279,17339,17341,17344,17347,17352,17355,17358,17361,17364,17367,17371,17376,17379,17382,17385,17393,17396,17399,17407,17413,17439,17442,17451,17457,17460,17463,17468,17475,17480,17495,17502,17513,17527,17531,17545,17549,17556,17571,17574,17577,17582,17597,17602,17609,17612,17636,17641,17651,17655,17676,17679,17681,17684,17697,17702,17705,17708,17711,17746,17757,17763,17768,17774,17777,17790,17796,17798,17820,17826,17828,17836,17840],[14,17270,17265],{"id":17271},"springboot-reactjs-1-server-side-rendering",[18,17273,17274],{},"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.",[16670,17276],{},[2352,17278,16675],{"id":16674},[3525,17280,17281,17327,17335,17337],{},[580,17282,17283,17284],{},"server side rendering ✅\n",[577,17285,17286,17292,17298,17304,17310,17316,17322],{},[580,17287,17288],{},[585,17289,17291],{"href":17290},"#why-java","Why Java for the backend?",[580,17293,17294],{},[585,17295,17297],{"href":17296},"#why-reactjs","Why ReactJS for the client?",[580,17299,17300],{},[585,17301,17303],{"href":17302},"#springboot","Spring Boot Initializr",[580,17305,17306],{},[585,17307,17309],{"href":17308},"#backend","Backend",[580,17311,17312],{},[585,17313,17315],{"href":17314},"#frontend","Frontend",[580,17317,17318],{},[585,17319,17321],{"href":17320},"#running-the-app","Running the app",[580,17323,17324],{},[585,17325,16714],{"href":17326},"#what-we-have-learned-so-far",[580,17328,17329,17334],{},[585,17330,17333],{"href":17331,"rel":17332},"https://synyx.de/2016/04/springboot-reactjs-progressive-enhancement-based-on-list-sorting/",[589],"progressive enhancement based on list sorting","\n🆕",[580,17336,16717],{},[580,17338,16720],{},[16670,17340],{},[18,17342,17343],{},"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.",[18,17345,17346],{},"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.",[18,17348,17349],{},[27,17350,17351],{},"Benefits of universal applications",[18,17353,17354],{},"thanks to server side rendered markup",[18,17356,17357],{},"– the app is fully functional from the start",[18,17359,17360],{},"– the app is usable instantly",[18,17362,17363],{},"JavaScript just enhances the features",[18,17365,17366],{},"– ajax calls without full page reloading are super fast",[18,17368,17369],{},[27,17370,16733],{},[18,17372,17373],{},[585,17374,16740],{"href":16738,"rel":17375},[589],[2352,17377,17291],{"id":17378},"why-java-for-the-backend",[18,17380,17381],{},"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.",[18,17383,17384],{},"So to answer the question",[577,17386,17387,17390],{},[580,17388,17389],{},"team knowledge (Spring, …)",[580,17391,17392],{},"battle tested solutions (Spring Security, Spring MVC, …)",[2352,17394,17297],{"id":17395},"why-reactjs-for-the-client",[18,17397,17398],{},"Well, personally I am a fan of react and it’s ecosystem. That’s it! 😎",[18,17400,17401,17402,986],{},"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 ",[585,17403,17406],{"href":17404,"rel":17405},"https://web.archive.org/web/20160411023634/https://egghead.io/series/getting-started-with-redux",[589],"redux",[18,17408,17409,17410],{},"Furthermore it supports server side rendering quite well. ",[573,17411,17412],{},"(afaik Angular 2 and Ember could also be used, or cyclejs,\nor …)",[5221,17414,17415,17420],{},[18,17416,17417],{},[27,17418,17419],{},"Disclaimer",[18,17421,17422,17423,17428,17429,17433,17434,17438],{},"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 ",[585,17424,17427],{"href":17425,"rel":17426},"http://javascriptplayground.com/blog/2016/02/the-react-webpack-tooling-problem",[589],"how to start with react"," (\nwithout\nusing webpack). Additionally I recommend to have a look at the official documentation\nfor ",[585,17430,17069],{"href":17431,"rel":17432},"https://facebook.github.io/react/docs/thinking-in-react.html",[589]," as well as ",[585,17435,1010],{"href":17436,"rel":17437},"https://spring.io/docs",[589],"\nof\ncourse.",[14,17440,17303],{"id":17441},"spring-boot-initializr",[18,17443,17444,17445,17450],{},"At first we’re going to generate a bootstrap project with the awesome ",[585,17446,17449],{"href":17447,"rel":17448},"https://start.spring.io",[589],"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.",[18,17452,17453],{},[1773,17454],{"alt":17455,"src":17456},"springboot-initializr","https://media.synyx.de/uploads//2016/02/springboot-initializer.png",[14,17458,17309],{"id":17459},"backend",[18,17461,17462],{},"Starting with the backend we have to create the following files.",[18,17464,17465],{},[27,17466,17467],{},"index.html",[18,17469,17470,17471,17474],{},"The html template is as simple as it could be. We just need a div that acts as container for our ReactJS app.\n",[50,17472,17473],{},"th:utext=\"${content}\""," is thymeleaf specific and injects the content attribute of the view model as unescaped string.",[18,17476,17477],{},[27,17478,17479],{},"React.java",[18,17481,17482,17483,17486,17487,17490,17491,17494],{},"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 ",[50,17484,17485],{},"window"," object nor a\n",[50,17488,17489],{},"console"," for logging. But since the latter one is required by ReactJS we have to load a ",[50,17492,17493],{},"nashorn-polyfill.js"," file\nbefore anything else.",[18,17496,17497,17498,17501],{},"We are loading our JavaScript sources into Nashorn with ",[50,17499,17500],{},"nashornScriptEngine.eval (\"load ('...')\")",". This is the same as\nincluding a script tag in a html document.",[18,17503,17504,17505,17508,17509,17512],{},"However, we could also call ",[50,17506,17507],{},"nashorn.eval (new InputStreamReader (...))"," to load the JavaScript files instead of using\nthe Nashorn specific ",[573,17510,17511],{},"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 😉",[18,17514,17515,17516,17518,17519,17522,17523,17526],{},"Furthermore we have to implement a method ",[573,17517,16802],{}," which will invoke a global ",[50,17520,17521],{},"renderServer"," function\ndefined in ",[573,17524,17525],{},"app.bundle.js"," to create the rendered html string.",[18,17528,17529],{},[27,17530,17493],{},[18,17532,17533,17534,17537,17538,17540,17541,17544],{},"The polyfill for nashorn has to define a ",[27,17535,17536],{},"global"," variable (for reasons I will explain later) and the already\nmentioned ",[27,17539,17489],{},". ",[573,17542,17543],{},"print"," is a Nashorn function that logs on stdout.",[18,17546,17547],{},[27,17548,16926],{},[18,17550,17551,17552,17555],{},"The ProductController is responsible for getting the products and for setting the rendered html string as the ",[50,17553,17554],{},"content","\nattribute of the view model.",[18,17557,17558,17559,17564,17565,17570],{},"Additionally we need\na ",[585,17560,17563],{"href":17561,"rel":17562},"https://github.com/synyx/springboot-reactjs-demo/blob/031a52fee5cc49c91988227b6b29b9857e5fed86/src/main/java/de/synyx/tutorials/spring/reactjs/demo/product/Product.java",[589],"Product.java","\nPOJO and\na ",[585,17566,17569],{"href":17567,"rel":17568},"https://github.com/synyx/springboot-reactjs-demo/blob/031a52fee5cc49c91988227b6b29b9857e5fed86/src/main/java/de/synyx/tutorials/spring/reactjs/demo/product/ProductRepository.java",[589],"ProductRepository.java",".\nI think this is very straight forward and code snippets are obsolete here.",[14,17572,17315],{"id":17573},"frontend",[18,17575,17576],{},"With the backend part ready we can start with the frontend.",[18,17578,17579,17580,986],{},"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",[573,17581,17479],{},[43,17583,17585],{"className":13667,"code":17584,"language":13669,"meta":48,"style":48},"$ npm init\n$ npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom\n\n",[50,17586,17587,17592],{"__ignoreMap":48},[53,17588,17589],{"class":55,"line":56},[53,17590,17591],{},"$ npm init\n",[53,17593,17594],{"class":55,"line":86},[53,17595,17596],{},"$ npm i --save-dev webpack babel-core babel-loader babel-preset-es2015 babel-preset-react react react-dom\n",[18,17598,17599],{},[27,17600,17601],{},"webpack.config.js",[18,17603,17604,17605,17608],{},"Next we configure webpack to generate a bundle of our JavaScript files including the ReactJS library and our app\nbusiness logic. Please note ",[27,17606,17607],{},"output.filename"," which is the file loaded by React.java.",[18,17610,17611],{},"Webpack can then simply be used to create the bundle by a npm task.",[43,17613,17615],{"className":13667,"code":17614,"language":13669,"meta":48,"style":48},"// package.json\n\"scripts\": {\n \"build\": \"webpack\"\n}\n\n",[50,17616,17617,17622,17627,17632],{"__ignoreMap":48},[53,17618,17619],{"class":55,"line":56},[53,17620,17621],{},"// package.json\n",[53,17623,17624],{"class":55,"line":86},[53,17625,17626],{},"\"scripts\": {\n",[53,17628,17629],{"class":55,"line":126},[53,17630,17631],{}," \"build\": \"webpack\"\n",[53,17633,17634],{"class":55,"line":163},[53,17635,282],{},[18,17637,17638],{},[27,17639,17640],{},"ProducList.js",[18,17642,17643,17644,17646,17647,17650],{},"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 ",[573,17645,17563],{},"?). Therefore we\nimplement a function that takes our products and returns the representational markup. To avoid\n",[50,17648,17649],{},"\"Cannot read property 'map' of undefined\""," type errors we simply assign an empty array to the products by default.",[18,17652,17653],{},[27,17654,16763],{},[18,17656,17657,17658,17660,17661,17663,17664,17666,17667,17669,17670,17672,17673,17675],{},"Next we need the entry point of our ReactJS app to define the ",[27,17659,17521],{}," function invoked by Nashorn. Remember the\n",[27,17662,17536],{}," variable set in ",[573,17665,17493],{},"? We use this variable now to “export” our ",[573,17668,17521],{}," function. If\nyou are familiar with the NodeJS environment, you already know that the ",[573,17671,17536],{}," object is the equivalent to the\n",[573,17674,17485],{}," object available in the browser. And Nashorn is our equivalent of NodeJS 😉",[14,17677,17321],{"id":17678},"running-the-app",[18,17680,16930],{},[18,17682,17683],{},"Now we can run our first universal server side rendered springboot application to admire our graceful product list. Go\non, run",[43,17685,17687],{"className":13667,"code":17686,"language":13669,"meta":48,"style":48},"$ npm run build\n$ ./gradlew bootRun\n",[50,17688,17689,17693],{"__ignoreMap":48},[53,17690,17691],{"class":55,"line":56},[53,17692,16827],{},[53,17694,17695],{"class":55,"line":86},[53,17696,16832],{},[18,17698,17699,17700,986],{},"open your Browser and load ",[50,17701,16812],{},[18,17703,17704],{},"Just…",[18,17706,17707],{},"to see…",[18,17709,17710],{},"a wonderful stacktrace…",[43,17712,17714],{"className":13667,"code":17713,"language":13669,"meta":48,"style":48},"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",[50,17715,17716,17721,17726,17731,17736,17741],{"__ignoreMap":48},[53,17717,17718],{"class":55,"line":56},[53,17719,17720],{},"jdk.nashorn.internal.runtime.ECMAException: TypeError:\n",[53,17722,17723],{"class":55,"line":86},[53,17724,17725],{},"[de.synyx...Product@553287f8, Product@65ae29e6] has no such function \"map\"\n",[53,17727,17728],{"class":55,"line":126},[53,17729,17730],{}," at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:58) ~[nashorn.jar:na]\n",[53,17732,17733],{"class":55,"line":163},[53,17734,17735],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:214) ~[nashorn.jar:na]\n",[53,17737,17738],{"class":55,"line":186},[53,17739,17740],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:186) ~[nashorn.jar:na]\n",[53,17742,17743],{"class":55,"line":221},[53,17744,17745],{}," at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:173) ~[nashorn.jar:na]\n",[18,17747,17748,17749,17752,17753,17756],{},"The reason is that Nashorn interprets Java objects as, surprise, Java objects. As you remember, our ReactJS component\n",[50,17750,17751],{},"\u003CProductList />"," expects a list of products (actually a JavaScript array). But currently the type of products is a\n",[50,17754,17755],{},"java.util.List"," which doesn’t have the map method. Note the datatype of products in the image below.",[18,17758,17759],{},[1773,17760],{"alt":17761,"src":17762},"nashorn-debugging","https://media.synyx.de/uploads//2016/03/nashorn-debugging.png",[5221,17764,17765],{},[18,17766,17767],{},"“Given a Java array or Collection, this function returns a JavaScript array with a shallow copy of its contents”",[18,17769,17770,17771,17773],{},"So our renderServer function defined in ",[50,17772,16763],{}," must be extended to:",[18,17775,17776],{},"Now we’re ready to go 🙂",[18,17778,17779,17780,17783,17784,17789],{},"Rebuild the frontend with ",[50,17781,17782],{},"npm run build",", restart the Spring Boot application, reload ",[573,17785,17786],{},[585,17787,16812],{"href":16812,"rel":17788},[589]," and\nadmire our awesome product list.",[18,17791,17792],{},[1773,17793],{"alt":17794,"src":17795},"awesome-product-list-001","https://media.synyx.de/uploads//2016/03/awesome-product-list-001.png",[2352,17797,17001],{"id":17000},[577,17799,17800,17803,17809,17817],{},[580,17801,17802],{},"using Nashorn is no rocket science",[580,17804,17805,17806,17808],{},"load js files via ",[50,17807,17500],{}," to enable debugging (at least in IntelliJ)",[580,17810,17811,17813,17814],{},[573,17812,17755],{}," must be converted to JavaScript array with ",[50,17815,17816],{},"Java.from",[580,17818,17819],{},"manually rebuilding and reloading the ReactJS app sucks (autoreload would be cool, right)",[18,17821,17822],{},[1773,17823],{"alt":17824,"src":17825},"js-webpack-nashorn","https://media.synyx.de/uploads//2016/03/js-webpack-nashorn.png",[2352,17827,17035],{"id":17034},[577,17829,17830,17833],{},[580,17831,17832],{},"using webpack to enhance developer experience",[580,17834,17835],{},"implementing the sorting feature",[18,17837,17838],{},[27,17839,17047],{},[607,17841,989],{},{"title":48,"searchDepth":86,"depth":86,"links":17843},[17844,17845,17846,17847,17848],{"id":16674,"depth":86,"text":16675},{"id":17378,"depth":86,"text":17291},{"id":17395,"depth":86,"text":17297},{"id":17000,"depth":86,"text":17001},{"id":17034,"depth":86,"text":17035},[613],"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":17265,"description":17274},"springboot-reactjs-server-side-rendering","blog/springboot-reactjs-server-side-rendering",[290,6991,17069,17070,1010,17071],"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":17862,"title":17863,"author":17864,"body":17866,"category":18092,"date":18093,"description":48,"extension":617,"link":18094,"meta":18095,"navigation":499,"path":18096,"seo":18097,"slug":17870,"stem":18098,"tags":18099,"teaser":18103,"__hash__":18104},"blog/blog/nagios-benachrichtigungsmanagement.md","Nagios Benachrichtigungsmanagement",[17865],"klem",{"type":11,"value":17867,"toc":18080},[17868,17871,17875,17878,17881,17885,17888,17891,17895,17898,17905,17908,17911,17919,17922,17924,17927,17933,17935,17938,17941,17948,17951,17954,17957,17960,17963,17966,17969,17972,17975,17982,17984,17987,17991,17994,17998,18001,18004,18007,18010,18013,18016,18022,18025,18031,18034,18040,18044,18047,18050,18054,18057,18060,18063,18067,18070,18074,18077],[14,17869,17863],{"id":17870},"nagios-benachrichtigungsmanagement",[2352,17872,17874],{"id":17873},"warum-wird-ein-benachrichtigungsmanagement-benötigt","Warum wird ein Benachrichtigungsmanagement benötigt?",[18,17876,17877],{},"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.",[18,17879,17880],{},"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.",[2352,17882,17884],{"id":17883},"was-ist-mit-benachrichtigungsmanagement-gemeint","Was ist mit Benachrichtigungsmanagement gemeint?",[18,17886,17887],{},"Mit dem Benachrichtigungsmanagement ist eine Webanwendung gemeint, die es ermöglicht, aus einem Pool an Personen,\nGruppen zu erstellen.",[18,17889,17890],{},"Diese Gruppen können wiederum für bestimmte Nagios Checks registriert werden, über deren Ergebnis die Mitglieder der\nGruppe anschließend Benachrichtigt werden.",[2352,17892,17894],{"id":17893},"was-muss-an-nagios-vorgenommen-werden-um-das-benachrichtigungsmanagement-zu-verwenden","Was muss an Nagios vorgenommen werden, um das Benachrichtigungsmanagement zu verwenden?",[18,17896,17897],{},"Nagios wird zur Verwendung des Benachrichtigungsmanagements um zwei “commands” erweitert.",[18,17899,17900],{},[585,17901,17904],{"href":17902,"rel":17903},"https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/objectdefinitions.html#command",[589],"Siehe Nagios Dokumentation: Command Definition",[18,17906,17907],{},"`define command {",[18,17909,17910],{},"command_name service-notify-by-notification-management",[18,17912,17913,17914,17918],{},"command_line curl -X POST -H \"Content-Type:\napplication/json\" \"",[585,17915,17916],{"href":17916,"rel":17917},"http://benachrichtigungs-management.synyx.de/notifications",[589],"\" -d '{\"nagiosCheck\":{\"\nhostname\":\"$HOSTALIAS$\",\"servicename\":\"$SERVICEDESC$\"}, \"type\":\"$NOTIFICATIONTYPE$\", \"state\":\"$SERVICESTATE$\"}'",[18,17920,17921],{},"}`",[18,17923,17907],{},[18,17925,17926],{},"command_name host-notify-by-notification-management",[18,17928,17913,17929,17932],{},[585,17930,17916],{"href":17916,"rel":17931},[589],"\" -d '{\"nagiosCheck\":{\"\nhostname\":\"$HOSTNAME$\"}, \"type\": \"$HOSTSTATE$\"}'",[18,17934,17921],{},[18,17936,17937],{},"Diese sorgen dafür, dass das Ergebnis eines Nagios Checks per HTTP-POST an das Benachrichtigungsmanagement übertragen\nwird.",[18,17939,17940],{},"Nun muss noch ein “contact” definiert werden, der diese commands verwendet.",[18,17942,17943],{},[585,17944,17947],{"href":17945,"rel":17946},"https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/objectdefinitions.html#contact",[589],"Siehe Nagios Dokumentation: Contact Definition",[18,17949,17950],{},"`define contact {",[18,17952,17953],{},"contact_name notificationManagement",[18,17955,17956],{},"alias notificationManagement",[18,17958,17959],{},"service_notification_period 24x7",[18,17961,17962],{},"host_notification_period 24x7",[18,17964,17965],{},"service_notification_options w,u,c,r",[18,17967,17968],{},"host_notification_options d,r",[18,17970,17971],{},"service_notification_commands service-notify-by-notification-management",[18,17973,17974],{},"host_notification_commands host-notify-by-notification-management",[18,17976,17977,17978],{},"email ",[585,17979,17981],{"href":17980},"mailto:admin@synyx.de","admin@synyx.de",[18,17983,17921],{},[18,17985,17986],{},"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.",[2352,17988,17990],{"id":17989},"wie-funktioniert-dieses-benachrichtigungsmanagement","Wie funktioniert dieses Benachrichtigungsmanagement?",[18,17992,17993],{},"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.",[649,17995,17997],{"id":17996},"gruppenverwaltung","Gruppenverwaltung",[18,17999,18000],{},"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.",[18,18002,18003],{},"Sollten diese nicht ausreichen, lassen sich weitere Gruppen über die API oder über die graphische Oberfläche bilden.",[18,18005,18006],{},"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.",[18,18008,18009],{},"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.",[18,18011,18012],{},"Dies ist ebenfalls, sowohl über die API als auch über die graphische Oberfläche möglich.",[18,18014,18015],{},"Um dies zu veranschaulichen, folgt nun ein Beispiel.",[18,18017,18018],{},[1773,18019],{"alt":18020,"src":18021},"\"NNM_Checks\"","https://media.synyx.de/uploads//2016/03/NNM_Checks.png",[18,18023,18024],{},"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.",[18,18026,18027],{},[1773,18028],{"alt":18029,"src":18030},"\"NNM_Map_UV\"","https://media.synyx.de/uploads//2016/03/NNM_Map_UV.png",[18,18032,18033],{},"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.",[18,18035,18036],{},[1773,18037],{"alt":18038,"src":18039},"\"NNM_UV_Mapped\"","https://media.synyx.de/uploads//2016/03/NNM_UV_Mapped.png",[649,18041,18043],{"id":18042},"limitierung-von-nachrichten","Limitierung von Nachrichten",[18,18045,18046],{},"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.",[18,18048,18049],{},"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.",[649,18051,18053],{"id":18052},"priorisierung-von-nachrichten","Priorisierung von Nachrichten",[18,18055,18056],{},"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.",[18,18058,18059],{},"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.",[18,18061,18062],{},"Die Verwaltung von Tags und Regeln ist ebenfalls sowohl über die API als auch über die graphische Oberfläche möglich.",[649,18064,18066],{"id":18065},"temporäres-deaktivieren-von-nagios-checks-via-ticketsystem","Temporäres deaktivieren von Nagios Checks via Ticketsystem",[18,18068,18069],{},"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.",[2352,18071,18073],{"id":18072},"was-ist-noch-denkbar","Was ist noch denkbar?",[18,18075,18076],{},"Ü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.",[18,18078,18079],{},"Ebenso ist eine nutzerspezifische Priorisierung von expliziten Nagios Checks denkbar.",{"title":48,"searchDepth":86,"depth":86,"links":18081},[18082,18083,18084,18085,18091],{"id":17873,"depth":86,"text":17874},{"id":17883,"depth":86,"text":17884},{"id":17893,"depth":86,"text":17894},{"id":17989,"depth":86,"text":17990,"children":18086},[18087,18088,18089,18090],{"id":17996,"depth":126,"text":17997},{"id":18042,"depth":126,"text":18043},{"id":18052,"depth":126,"text":18053},{"id":18065,"depth":126,"text":18066},{"id":18072,"depth":86,"text":18073},[614],"2016-03-04T09:53:43","https://synyx.de/blog/nagios-benachrichtigungsmanagement/",{},"/blog/nagios-benachrichtigungsmanagement",{"title":17863,"description":48},"blog/nagios-benachrichtigungsmanagement",[18100,18101,18102],"monitoring","nagios","notifications","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…","Wao5IVero4mEM3A4tDQms2ao3vtXlhOVMnrdqjh1EaQ",{"id":18106,"title":18107,"author":18108,"body":18110,"category":18198,"date":18199,"description":18200,"extension":617,"link":18201,"meta":18202,"navigation":499,"path":18203,"seo":18204,"slug":18114,"stem":18206,"tags":18207,"teaser":18209,"__hash__":18210},"blog/blog/heute-sprechen-wir-python-und-scratch.md","Heute sprechen wir Python und Scratch!",[18109],"bechtold",{"type":11,"value":18111,"toc":18196},[18112,18115,18129,18132,18135,18144,18147,18161,18164,18178,18181,18184,18190,18193],[14,18113,18107],{"id":18114},"heute-sprechen-wir-python-und-scratch",[18,18116,18117,18118,18122,18123,18128],{},"Am Samstag fand der erste IT-Workshop mit Unterstützung der ",[585,18119,18121],{"href":12167,"rel":18120},[589],"Devoxx4Kids","\nim ",[585,18124,18127],{"href":18125,"rel":18126},"https://www.internationaler-bund.de/angebote/standort/202305",[589],"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.",[18,18130,18131],{},"Einige dieser Schüler, sowie andere IT-interessierte Migranten aus dem Landkreis Karlsruhe, fanden sich um 10:30 Uhr\nzum Start der Workshops ein.",[18,18133,18134],{},"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.",[18,18136,18137,18138,18143],{},"Zusammen mit der ",[585,18139,18142],{"href":18140,"rel":18141},"https://www.codecentric.de/",[589],"codecentric AG"," hatten wir eine ausreichend große Gruppe von Mentoren, um\ndie jungen Erwachsenen zwischen 20 – 25 Jahren bei den Workshops zu betreuen.",[18,18145,18146],{},"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.",[18,18148,18149,18153,18157],{},[1773,18150],{"alt":18151,"src":18152},"\"ib_baden_08\"","https://media.synyx.de/uploads//2016/01/ib_baden_08.jpg",[1773,18154],{"alt":18155,"src":18156},"\"ib_baden_03\"","https://media.synyx.de/uploads//2016/01/ib_baden_03.jpg",[1773,18158],{"alt":18159,"src":18160},"\"ib_baden_05\"","https://media.synyx.de/uploads//2016/01/ib_baden_05.jpg",[18,18162,18163],{},"Im Anschluss folgte ein Minecraft Workshop, der die in Scratch neu erworbenen Logikkenntnisse bzw. Programmierkonzepte\ndirekt auf eine harte Probe stellte 🙂",[18,18165,18166,18170,18174],{},[1773,18167],{"alt":18168,"src":18169},"\"ib_baden_02\"","https://media.synyx.de/uploads//2016/01/ib_baden_02.jpg",[1773,18171],{"alt":18172,"src":18173},"\"ib_baden_07\"","https://media.synyx.de/uploads//2016/01/ib_baden_07.jpg",[1773,18175],{"alt":18176,"src":18177},"\"ib_baden_01\"","https://media.synyx.de/uploads//2016/01/ib_baden_01.jpg",[18,18179,18180],{},"Zur Stärkung gab es dann Pizza für alle!",[18,18182,18183],{},"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 🙂",[18,18185,18186],{},[1773,18187],{"alt":18188,"src":18189},"\"ib_baden_06\"","https://media.synyx.de/uploads//2016/01/ib_baden_06.jpg",[18,18191,18192],{},"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.",[18,18194,18195],{},"Unser Feedback zu einzelnen Teilnehmern hat sie aufgenommen, um deren individuelle Entwicklung besser fördern und\nunterstützen zu können.",{"title":48,"searchDepth":86,"depth":86,"links":18197},[],[614],"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":18107,"description":18205},"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",[11755,18208],"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…","Fan9wqmIZbQjnY_edJfbd5zw5kqKmzXPfSRArwZ25Vg",{"id":18212,"title":18213,"author":18214,"body":18215,"category":18315,"date":18316,"description":18317,"extension":617,"link":18318,"meta":18319,"navigation":499,"path":18320,"seo":18321,"slug":18219,"stem":18322,"tags":18323,"teaser":18330,"__hash__":18331},"blog/blog/it-wertbeitrag-ganz-pragmatisch.md","IT-Wertbeitrag ganz pragmatisch?!",[13114],{"type":11,"value":18216,"toc":18313},[18217,18220,18223,18226,18231,18234,18237,18240,18245,18248,18251,18256,18259,18262,18265,18276,18279,18284,18289,18292,18295,18306],[14,18218,18213],{"id":18219},"it-wertbeitrag-ganz-pragmatisch",[18,18221,18222],{},"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!",[18,18224,18225],{},"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.",[18,18227,18228],{},[27,18229,18230],{},"Aus Gründen",[18,18232,18233],{},"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.",[18,18235,18236],{},"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.",[18,18238,18239],{},"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.",[18,18241,18242],{},[27,18243,18244],{},"Objektivität ist der Knackpunkt",[18,18246,18247],{},"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 …",[18,18249,18250],{},"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“!",[18,18252,18253],{},[27,18254,18255],{},"Alternativen?",[18,18257,18258],{},"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.",[18,18260,18261],{},"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?",[18,18263,18264],{},"Stellenwert und Wichtigkeit der IT:",[3525,18266,18267,18270,18273],{},[580,18268,18269],{},"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?",[580,18271,18272],{},"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.",[580,18274,18275],{},"IT-Know-how des Managements durch Kennzahlen, regelmäßige Meetings, Schulungen, externe Beratung, … verbessern und\nPotentiale aufzeigen.",[18,18277,18278],{},"Restrukturierung von IT und Umschichten von IT-Investitionen:",[3525,18280,18281],{},[580,18282,18283],{},"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.",[18,18285,18286],{},[27,18287,18288],{},"IT is still the next big thing!",[18,18290,18291],{},"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.",[18,18293,18294],{},"Gründe für den Ausbau von IT-Investitionen:",[3525,18296,18297,18300,18303],{},[580,18298,18299],{},"Mit IT Prozessabläufe automatisieren = Prozesskostensenkung, Steigerung von\nOutput/Geschwindigkeit/Nachvollziehbarkeit und Qualität, Kunden / Lieferanten Integrieren",[580,18301,18302],{},"Mit IT mehr Daten sammeln und auswerten = Generierung, Verarbeitung und Auswertung von Daten / großen Datenmengen,\nbessere Entscheidungsfindung.",[580,18304,18305],{},"Mit IT die Kommunikation ausbauen = Maschine zu Maschine, B2B oder B2C.",[18,18307,18308,18309,18312],{},"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 ",[27,18310,18311],{},"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":48,"searchDepth":86,"depth":86,"links":18314},[],[614],"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":18213,"description":18222},"blog/it-wertbeitrag-ganz-pragmatisch",[18324,13490,18325,18326,18327,18328,18329],"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":18333,"title":18334,"author":18335,"body":18336,"category":18394,"date":18395,"description":18396,"extension":617,"link":18397,"meta":18398,"navigation":499,"path":18399,"seo":18400,"slug":18340,"stem":18401,"tags":18402,"teaser":18407,"__hash__":18408},"blog/blog/lichtschwerterundso-mit-synyx-ins-kino.md","#lichtschwerterundso – Mit synyx ins Kino",[13114],{"type":11,"value":18337,"toc":18392},[18338,18341,18344,18350,18353,18359,18362,18368,18371,18374,18380,18383,18389],[14,18339,18334],{"id":18340},"lichtschwerterundso-mit-synyx-ins-kino",[18,18342,18343],{},"Am Montagabend, den 21.12.2015 um 18 Uhr ging es endlich los, unser Kino Event mit #lichtschwerterundso in der Kurbel\nin Karlsruhe.",[18,18345,18346],{},[1773,18347],{"alt":18348,"src":18349},"\"Stormtrooper persönlich\"","https://media.synyx.de/uploads//2016/01/IMG_0409-1.jpg",[18,18351,18352],{},"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.",[18,18354,18355],{},[1773,18356],{"alt":18357,"src":18358},"\"Viel los\"","https://media.synyx.de/uploads//2016/01/IMG_0430-1.jpg",[18,18360,18361],{},"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“.",[18,18363,18364],{},[1773,18365],{"alt":18366,"src":18367},"\"Im Kinosaal\"","https://media.synyx.de/uploads//2016/01/DSC_0188.jpg",[18,18369,18370],{},"Achtung nun folgt ein kleiner Spoiler:",[18,18372,18373],{},"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.",[18,18375,18376],{},[1773,18377],{"alt":18378,"src":18379},"\"Verlosung\"","https://media.synyx.de/uploads//2016/01/IMG_0475.jpg",[18,18381,18382],{},"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 😉",[18,18384,18385],{},[1773,18386],{"alt":18387,"src":18388},"\"Lego Preise\"","https://media.synyx.de/uploads//2016/01/SYNEMA.jpg",[18,18390,18391],{},"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":48,"searchDepth":86,"depth":86,"links":18393},[],[614],"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":18334,"description":18343},"blog/lichtschwerterundso-mit-synyx-ins-kino",[18403,18404,18405,18406],"kino","lichtschwerterundso","synema","synyx-event","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…","G1WeRI2RKGJkbnOJI6_tK2VsxiNVO37_p9aYyBu0GQM",{"id":18410,"title":18411,"author":18412,"body":18414,"category":18523,"date":18524,"description":18525,"extension":617,"link":18526,"meta":18527,"navigation":499,"path":18528,"seo":18529,"slug":18418,"stem":18531,"tags":18532,"teaser":18536,"__hash__":18537},"blog/blog/visual-thinking-synyx-sketcht.md","Visual Thinking – synyx sketcht",[18413,11420],"weigel",{"type":11,"value":18415,"toc":18521},[18416,18419,18434,18437,18443,18446,18449,18452,18458,18461,18464,18467,18470,18476,18479,18482,18487,18493,18499,18503,18509,18515],[14,18417,18411],{"id":18418},"visual-thinking-synyx-sketcht",[18,18420,18421,18422,18427,18428,18433],{},"Anstatt immer wieder zur Fortbildung auf Konferenzen zu fahren hatten wir dieses Jahr noch eine andere Idee: Wir hatten\neinen Inhouse-Workshop zum Thema ",[585,18423,18426],{"href":18424,"rel":18425},"http://www.frauhoelle.com/sketchnotes/",[589],"“Visual Thinking”"," mit Tanja\nalias ",[585,18429,18432],{"href":18430,"rel":18431},"https://twitter.com/frauhoelle",[589],"@frauhoelle",". Ziel des Workshops war es, den Teilnehmern die Bildsprache näher zu\nbringen und dadurch die Kommunikation zu verbessern.",[18,18435,18436],{},"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.",[18,18438,18439],{},[1773,18440],{"alt":18441,"src":18442},"\"sketch_kombiniert\"","https://media.synyx.de/uploads//2015/12/sketch_kombiniert-e1450077980406.jpg",[18,18444,18445],{},"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.",[18,18447,18448],{},"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.",[18,18450,18451],{},"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.",[18,18453,18454],{},[1773,18455],{"alt":18456,"src":18457},"\"Jeder macht mit\"","https://media.synyx.de/uploads//2015/12/alle_am_tisch-e1449841973591.jpg",[18,18459,18460],{},"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.",[18,18462,18463],{},"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.",[18,18465,18466],{},"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.",[18,18468,18469],{},"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.",[18,18471,18472],{},[1773,18473],{"alt":18474,"src":18475},"\"EIndrücke aus dem Büro\"","https://media.synyx.de/uploads//2015/12/buero-e1449847979285.jpg",[18,18477,18478],{},"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.",[18,18480,18481],{},"Ab sofort haben wir immer einen Schattenstift in der Tasche!",[18,18483,18484],{},[27,18485,18486],{},"Zum Schluss noch paar Eindrücke vom Workshop…",[18,18488,18489],{},[1773,18490],{"alt":18491,"src":18492},"\"Alle mit dabei\"","https://media.synyx.de/uploads//2015/12/brainstorm-e1449841965214.jpg",[18,18494,18495],{},[1773,18496],{"alt":18497,"src":18498},"\"Kreatives Chaos\"","https://media.synyx.de/uploads//2015/12/chaotisch.jpg",[18,18500,18501],{},[1773,18502],{"alt":18456,"src":18457},[18,18504,18505],{},[1773,18506],{"alt":18507,"src":18508},"\"spassdabei\"","https://media.synyx.de/uploads//2015/12/spassdabei-e1449842435714.jpg",[18,18510,18511],{},[1773,18512],{"alt":18513,"src":18514},"\"Ergebnis einer Übungsaufgabe\"","https://media.synyx.de/uploads//2015/12/whiteboard-e1449841924365.jpg",[18,18516,18517],{},[1773,18518],{"alt":18519,"src":18520},"\"Fazit\"","https://media.synyx.de/uploads//2015/12/wasnehmenwirmit-e1449841945934.jpg",{"title":48,"searchDepth":86,"depth":86,"links":18522},[],[613],"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.","https://synyx.de/blog/visual-thinking-synyx-sketcht/",{},"/blog/visual-thinking-synyx-sketcht",{"title":18411,"description":18530},"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",[4230,6608,18533,18534,18535,11768],"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…","BgXvHMdUrC7pnmgTFg60lYGkFIjmmLWyZZkZMlr7038",{"id":18539,"title":18540,"author":18541,"body":18542,"category":18772,"date":18773,"description":18774,"extension":617,"link":18775,"meta":18776,"navigation":499,"path":18777,"seo":18778,"slug":18546,"stem":18780,"tags":18781,"teaser":18786,"__hash__":18787},"blog/blog/a-conference-series-that-matters-from-nosql-to-distributed.md","A conference series that matters – From NoSQL to distributed",[8052],{"type":11,"value":18543,"toc":18766},[18544,18547,18570,18574,18577,18580,18604,18617,18628,18635,18646,18650,18664,18668,18675,18687,18720,18744,18761,18763],[14,18545,18540],{"id":18546},"a-conference-series-that-matters-from-nosql-to-distributed",[18,18548,18549,18550,18555,18556,18559,18560,18563,18564,18569],{},"Since November 2013 I have been attending six conferences of the ",[585,18551,18554],{"href":18552,"rel":18553},"https://distributed-matters.org/",[589],"conference series","\nformerly known as ",[27,18557,18558],{},"NoSQL matters",", and that now runs by the name ",[27,18561,18562],{},"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 ",[585,18565,18568],{"href":18566,"rel":18567},"http://www.uab-casaconvalescencia.org/ca/index.php",[589],"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.",[2352,18571,18573],{"id":18572},"why-i-like-the-conference-series","Why I like the conference series",[18,18575,18576],{},"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.",[18,18578,18579],{},"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:",[18,18581,18582,18583,18586,18587,18592,18593,18598,18599,4562],{},"In 2013 in Barcelona there was a talk by ",[27,18584,18585],{},"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 ",[585,18588,18591],{"href":18589,"rel":18590},"https://synyx.de/2014/06/a-very-brief-history-of-the-nosql-development/",[589],"blog post",", and later in an article series\nfor the ",[585,18594,18597],{"href":18595,"rel":18596},"https://jaxenter.de/eine-kleine-reise-durch-nosql-28451",[589],"German Java Magazin"," (also available at\nthe ",[585,18600,18603],{"href":18601,"rel":18602},"https://synyx.de/unternehmen/publikationen/",[589],"synyx homepage",[18,18605,18606,18607,18610,18611,18616],{},"The following year, 2014, ",[27,18608,18609],{},"Ellen Friedman"," beautifully told a story on the emergence of big data and the usage of *\n*time series databases** of which we ",[585,18612,18615],{"href":18613,"rel":18614},"https://synyx.de/2014/11/time-series-data-is-the-the-new-big-data/",[589],"blogged"," a\nshort recap that became relatively popular.",[18,18618,18619,18620,18623,18624,18627],{},"And this year ",[27,18621,18622],{},"Pablo Chacin"," gave a talk on ",[573,18625,18626],{},"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).",[18,18629,18630,18631,18634],{},"And I do not forget to mention the talks of ",[27,18632,18633],{},"Uwe Friedrichsen",", 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.",[18,18636,18637,18638,18641,18642,18645],{},"The new trend is going from distribution to microservices, and ",[27,18639,18640],{},"microservice"," is, like ",[27,18643,18644],{},"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.",[2352,18647,18649],{"id":18648},"the-conference-organization","The conference organization",[18,18651,18652,18653,17019,18656,18659,18660,18663],{},"To my opinion, the conferences are always very well organized. From my side a big thanks to ",[27,18654,18655],{},"Katja Keil",[27,18657,18658],{},"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 ",[573,18661,18662],{},"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.",[2352,18665,18667],{"id":18666},"dmconf15-in-barcelona","#dmconf15 in Barcelona",[18,18669,18670,18671,18674],{},"A few impressions on ",[27,18672,18673],{},"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.",[18,18676,18677,18678,18681,18682,18686],{},"The conference key note was held by ",[27,18679,18680],{},"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 ",[585,18683,4132],{"href":18684,"rel":18685},"http://kubernetes.io/",[589],", an open source project that serves the purpose.",[18,18688,18689,18694,18695,18700,18701,18706,18707,18712,18713,17019,18716,18719],{},[585,18690,18693],{"href":18691,"rel":18692},"http://de.slideshare.net/dmconf/f1-the-distributed-sql-database-supporting-googles-ad-business-bart-samwell",[589],"B. Samwell",",\nalso from Google, gave an introduction to ",[585,18696,18699],{"href":18697,"rel":18698},"http://research.google.com/pubs/pub38125.html",[589],"Googles F1",", a distributed\ndatabase build atop ",[585,18702,18705],{"href":18703,"rel":18704},"http://research.google.com/archive/spanner.html",[589],"Spanner",". F1 is a hybrid database that takles\nreplication inside and across datacenters, and combines the scalability\nof ",[585,18708,18711],{"href":18709,"rel":18710},"http://research.google.com/archive/bigtable.html",[589],"BigTable"," with the usability of a SQL database. F1 provides\nscaling without NoSQL and focuses on ",[27,18714,18715],{},"OLTP",[27,18717,18718],{},"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.",[18,18721,18722,18727,18728,18731,18732,18737,18738,18743],{},[585,18723,18726],{"href":18724,"rel":18725},"http://de.slideshare.net/dmconf/a-tale-of-two-microservices-pablo-chacin",[589],"P. Chacin"," told the interesting ",[573,18729,18730],{},"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. ",[585,18733,18736],{"href":18734,"rel":18735},"https://michaelfeathers.silvrback.com/microservices-until-macro-complexity",[589],"M. Feathers article","), and the role of\nsize and usage of microservices (cp.\nthe ",[585,18739,18742],{"href":18740,"rel":18741},"https://www.tigerteam.dk/2014/micro-services-its-not-only-the-size-that-matters-its-also-how-you-use-them-part-1/",[589],"blog series by J. Cramon",").\nTalk and mentioned article are definitely recommendable.",[18,18745,18746,18747,18752,18753,18756,18757,18760],{},"Again, ",[585,18748,18751],{"href":18749,"rel":18750},"http://de.slideshare.net/dmconf/microservices-stressfree-and-without-increased-heartattack-risk-uwe-friedrichsen",[589],"U. Friedrichsen","\nwas present as speaker in Barcelona. He warned of the way down microservice road towards ",[27,18754,18755],{},"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’ ",[27,18758,18759],{},"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..",[2352,18762,12763],{"id":12762},[18,18764,18765],{},"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":48,"searchDepth":86,"depth":86,"links":18767},[18768,18769,18770,18771],{"id":18572,"depth":86,"text":18573},{"id":18648,"depth":86,"text":18649},{"id":18666,"depth":86,"text":18667},{"id":12762,"depth":86,"text":12763},[614],"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":18540,"description":18779},"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",[18782,18783,18784,18785],"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":18789,"title":18790,"author":18791,"body":18792,"category":18858,"date":18859,"description":18799,"extension":617,"link":18860,"meta":18861,"navigation":499,"path":18862,"seo":18863,"slug":18796,"stem":18864,"tags":18865,"teaser":18867,"__hash__":18868},"blog/blog/marshmallow-challenge-bei-synyx.md","Marshmallow Challenge bei synyx",[8053],{"type":11,"value":18793,"toc":18856},[18794,18797,18800,18803,18812,18815,18823,18826,18829,18843,18846],[14,18795,18790],{"id":18796},"marshmallow-challenge-bei-synyx",[18,18798,18799],{},"Man nehme…",[18,18801,18802],{},"20 Spaghetti – 1 Marshmallow – 1 Meter Schnur – 1 Meter Krepp-Klebeband",[18,18804,18805,18806,18811],{},"…und fertig ist ein verrücktes Pasta-Rezept – wäre eine Möglichkeit. Die andere nennt\nsich ",[585,18807,18810],{"href":18808,"rel":18809},"http://marshmallowchallenge.com/Welcome.html",[589],"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.",[18,18813,18814],{},"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.",[18,18816,18817,18818,986],{},"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 ",[585,18819,18822],{"href":18820,"rel":18821},"https://www.youtube.com/watch?v=H0_yKBitO8M",[589],"zahlreichen Beispielen",[18,18824,18825],{},"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).",[18,18827,18828],{},"Aus der anschließenden Diskussion mit den drei Teams ergaben sich folgende Erkenntnisse:",[577,18830,18831,18834,18837,18840],{},[580,18832,18833],{},"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).",[580,18835,18836],{},"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?",[580,18838,18839],{},"Ein Team, das gut zusammenarbeitet und mit allen seinen Sinnen dabei ist, kann sehr gute Ergebnisse erzielen.",[580,18841,18842],{},"Spaß an der Arbeit ist zwar nicht notwendig, kann aber ein Garant für Erfolg sein.",[18,18844,18845],{},"Vielen Dank an die drei Teams für die tolle Mitarbeit und guten Ergebnisse!",[18,18847,18848,3566,18852],{},[1773,18849],{"alt":18850,"src":18851},"\"IMG_3336\"","https://media.synyx.de/uploads//2015/11/IMG_3336.jpg",[1773,18853],{"alt":18854,"src":18855},"\"IMG_3341\"","https://media.synyx.de/uploads//2015/11/IMG_3341.jpg",{"title":48,"searchDepth":86,"depth":86,"links":18857},[],[614],"2015-11-12T12:59:33","https://synyx.de/blog/marshmallow-challenge-bei-synyx/",{},"/blog/marshmallow-challenge-bei-synyx",{"title":18790,"description":18799},"blog/marshmallow-challenge-bei-synyx",[4221,18866,4232,5743],"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…","wylKlNW6dEUN1cOMdVRf8Ymi7jsEd8zSk4bKdsl5ILc",{"id":18870,"title":18871,"author":18872,"body":18873,"category":18971,"date":18972,"description":18973,"extension":617,"link":18974,"meta":18975,"navigation":499,"path":18976,"seo":18977,"slug":18978,"stem":18979,"tags":18980,"teaser":18982,"__hash__":18983},"blog/blog/spende-fuer-fluechtlinge-in-karlsruhe.md","Spende für Flüchtlinge in Karlsruhe",[18413],{"type":11,"value":18874,"toc":18969},[18875,18878,18881,18884,18892,18895,18901,18916,18922,18925,18928,18931,18937,18946],[14,18876,18871],{"id":18877},"spende-für-flüchtlinge-in-karlsruhe",[18,18879,18880],{},"Der Aufruf eines Bekannten, ihm statt eines Geschenkes zum Geburtstag Sachspenden für Flüchtlinge zu überreichen, hat\neine beachtliche Welle geschlagen.",[18,18882,18883],{},"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.",[18,18885,3736,18886,18891],{},[585,18887,18890],{"href":18888,"rel":18889},"https://web.archive.org/web/20150518192118/http://vanguar.de:80/cafe/",[589],"Café Vanguarde",", wo auch die letzte\nsynyx-Winterfeier stattfand, erklärte sich sofort bereit,",[18,18893,18894],{},"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.",[18,18896,18897],{},[1773,18898],{"alt":18899,"src":18900},"\"Sammelstelle Café Vanguarde\"","https://media.synyx.de/uploads//2015/09/IMG_1754.jpg",[18,18902,18903,18904,18909,18910,18915],{},"Kleine Bedenken, geschürt durch\neinen ",[585,18905,18908],{"href":18906,"rel":18907},"https://web.archive.org/web/20210422135731/https://fluechtlingshilfe-karlsruhe.de/kleidung-moebel-2/",[589],"Artikel","\nüber aktuell nicht mehr benötigte Sachspenden konnten durch einen persönlichen Besuch vorort\nim ",[585,18911,18914],{"href":18912,"rel":18913},"https://fluechtlingshilfe.net.kit.edu/",[589],"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.",[18,18917,18918],{},[1773,18919],{"alt":18920,"src":18921},"\"Vorsortierung der Spenden\"","https://media.synyx.de/uploads//2015/09/IMG_1752.jpg",[18,18923,18924],{},"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.",[18,18926,18927],{},"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.",[18,18929,18930],{},"Die relativ spontane Aktion ist geglückt und hat sich gelohnt!",[18,18932,18933],{},[1773,18934],{"alt":18935,"src":18936},"\"Erfolgreiche Spendenaktion\"","https://media.synyx.de/uploads//2015/09/IMG_1758.jpg",[18,18938,18939,18940,18945],{},"Man kann an vielen Stellen helfen wenn man Interesse hat. Einige Flüchtlingsheime werden schon ausreichend versorgt. Vor\nallem ",[585,18941,18944],{"href":18942,"rel":18943},"https://www.facebook.com/kriegsstrasse200",[589],"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:",[577,18947,18948,18955,18962],{},[580,18949,18950],{},[585,18951,18954],{"href":18952,"rel":18953},"http://fluechtlingshilfe-karlsruhe.de/",[589],"Flüchtlingshilfe Karlsruhe",[580,18956,18957],{},[585,18958,18961],{"href":18959,"rel":18960},"http://www.amnesty-karlsruhe.de/",[589],"Amnesty Karlsruhe",[580,18963,18964],{},[585,18965,18968],{"href":18966,"rel":18967},"http://www.menschenrechtszentrum.de/",[589],"Menschenrechtszentrum",{"title":48,"searchDepth":86,"depth":86,"links":18970},[],[614],"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":18871,"description":18880},"spende-fuer-fluechtlinge-in-karlsruhe","blog/spende-fuer-fluechtlinge-in-karlsruhe",[18981,18208],"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…","IIG37FuBFRo9c5UJWmPiaTlzK6-Hxu4poI5z2-jpWG0",{"id":18985,"title":18986,"author":18987,"body":18988,"category":19338,"date":19339,"description":19340,"extension":617,"link":19341,"meta":19342,"navigation":499,"path":19343,"seo":19344,"slug":18992,"stem":19345,"tags":19346,"teaser":19348,"__hash__":19349},"blog/blog/synyx-berlin-expert-days-2015.md","synyx @ Berlin Expert Days 2015",[8328],{"type":11,"value":18989,"toc":19333},[18990,18993,19002,19005,19011,19015,19018,19024,19027,19030,19033,19036,19040,19055,19064,19067,19084,19091,19098,19117,19127,19130,19136,19154,19175,19181,19198,19202,19205,19228,19246,19260,19271,19277,19307,19318,19324,19327,19330],[14,18991,18986],{"id":18992},"synyx-berlin-expert-days-2015",[18,18994,18995,18996,19001],{},"Am 17. und 18. September 2015 fanden die fünften ",[585,18997,19000],{"href":18998,"rel":18999},"http://bed-con.org/2015/",[589],"Berlin Expert Days"," (kurz BED-Con) statt.",[18,19003,19004],{},"Erst beim Schreiben dieses Blog Posts habe ich mit Erstaunen festgestellt, dass es für mich bereits die vierte BED-Con\nwar…",[18,19006,19007,19008,986],{},"Rückt näher an den Kamin, Kinder, nehmt euch einen Keks, ich werde euch nun die Geschichte erzählen von ",[573,19009,19010],{},"Neun synyxern,\ndie auszogen, die BED-Con unsicher zu machen",[649,19012,19014],{"id":19013},"mittwoch-16-september-anreise","Mittwoch, 16. September – Anreise",[18,19016,19017],{},"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.",[18,19019,19020],{},[1773,19021],{"alt":19022,"src":19023},"\"Nao\"","https://media.synyx.de/uploads//2015/09/Nao-e1443019500387.jpg",[18,19025,19026],{},"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.",[18,19028,19029],{},"Leider haben wir ein winziges Detail übersehen: unsere Platzreservierungen befinden sich im Ruhebereich des ICE.",[18,19031,19032],{},"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.",[18,19034,19035],{},"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.",[649,19037,19039],{"id":19038},"donnerstag-17-september-erster-konferenztag","Donnerstag, 17. September – Erster Konferenztag",[18,19041,19042,19043,19048,19049,19054],{},"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 ",[585,19044,19047],{"href":19045,"rel":19046},"http://www.fu-berlin.de/",[589],"Freien Universität Berlin"," findet die Java-Konferenz\ndieses Jahr in der ",[585,19050,19053],{"href":19051,"rel":19052},"http://www.urania.de/",[589],"Urania Berlin"," statt.",[18,19056,19057,19058,19063],{},"Ich habe einen typischen Berliner Anfahrtsweg von ungefähr 50 Minuten vor mir. Die anderen haben sich in\neinem ",[585,19059,19062],{"href":19060,"rel":19061},"http://www.motel-one.com/de/hotels/berlin/hotel-berlin-tiergarten/",[589],"Hotel"," eingenistet, von dem aus man einen\nextrem langen Fußmarsch von 2 Minuten hinlegen muss, um die Urania zu erreichen.",[18,19065,19066],{},"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.",[18,19068,19069,19070,19073,19074,19077,19078,19083],{},"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 ",[27,19071,19072],{},"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 ",[27,19075,19076],{},"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 ",[585,19079,19082],{"href":19080,"rel":19081},"http://www.moskito.org/",[589],"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.",[18,19085,19086,19087,19090],{},"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 ",[573,19088,19089],{},"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…",[18,19092,19093,19094,19097],{},"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 ",[13401,19095,19096],{},"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.",[18,19099,19100,19101,19106,19107,19110,19111,19116],{},"Im Zuge der ",[585,19102,19105],{"href":19103,"rel":19104},"http://jug-karlsruhe.de/",[589],"Java User Group Karlsruhe"," hatte ",[27,19108,19109],{},"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 ",[585,19112,19115],{"href":19113,"rel":19114},"http://pitest.org/",[589],"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.",[18,19118,19119,19120,19122,19123,19126],{},"Kaffee und Wasser fassen und ab in den Humboldtsaal! ",[27,19121,3410],{}," erörtert, warum ",[27,19124,19125],{},"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.",[18,19128,19129],{},"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.",[18,19131,19132],{},[1773,19133],{"alt":19134,"src":19135},"\"Devoxx4Kids_Bedcon\"","https://media.synyx.de/uploads//2015/09/Devoxx4Kids_Bedcon.jpg",[18,19137,19138,19139,19141,19142,1628,19145,19148,19149,19153],{},"Für mich als synyxer ist es quasi eine heilige Pflicht, dem Vortrag über die ",[27,19140,18121],{}," beizuwohnen, der von\nunseren Kollegen ",[27,19143,19144],{},"Katja Arrasz-Schepanski",[27,19146,19147],{},"Christian Mennerich"," 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 ",[585,19150,12169],{"href":19151,"rel":19152},"http://www.devoxx4kids.de/veranstaltungen/",[589]," Sprössling\nheranwachsen sehen werden.",[18,19155,19156,19157,19160,19161,19166,19167,19170,19171,19174],{},"Im Anschluss (bequemerweise muss ich nicht einmal den Raum wechseln) stellt ",[27,19158,19159],{},"Oliver Wehrens"," die Leitplanken\nder ",[585,19162,19165],{"href":19163,"rel":19164},"http://www.epost.de",[589],"E-POST"," bei der ",[27,19168,19169],{},"Entwicklung verteilter Systeme"," vor. Ich stelle fest, dass wir in manchen\nProjekten ziemlich ähnliche ",[13401,19172,19173],{},"Probleme"," Herausforderungen haben, bin allerdings heilfroh, dass zumindest die\nStrukturen/Hierarchien bei uns anderer Art sind.",[18,19176,19177],{},[1773,19178],{"alt":19179,"src":19180},"\"UX_im_Projekt_Bedcon\"","https://media.synyx.de/uploads//2015/09/UX_im_Projekt_Bedcon.jpg",[18,19182,19183,19184,1628,19187,19190,19191,19194,19195,986],{},"Zum Abschluss des ersten Konferenztages lehne ich mich in einem der bequemen Sessel des Humboldtsaals zurück und lausche\n",[27,19185,19186],{},"Nicole Charlier",[27,19188,19189],{},"Philipp Kumar"," über ihre Erfahrungen von ",[27,19192,19193],{},"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): ",[573,19196,19197],{},"Man\nmuss nur begreiflich machen, dass es das wert ist, wenn man sich durch eine hohe Qualität abheben will",[649,19199,19201],{"id":19200},"freitag-18-september-zweiter-konferenztag","Freitag, 18. September – Zweiter Konferenztag",[18,19203,19204],{},"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.",[18,19206,19207,19208,19211,19212,19215,19216,19221,19222,19227],{},"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 ",[27,19209,19210],{},"Jeremias Rößler"," mit dem erfrischenden Titel ",[27,19213,19214],{},"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 ",[585,19217,19220],{"href":19218,"rel":19219},"http://www.retest.de/",[589],"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 ",[585,19223,19226],{"href":19224,"rel":19225},"https://github.com/marmelab/gremlins.js/blob/master/README.md",[589],"Gremlins.js",". Eine Horde Gremlins auf unsere\nAnwendungen loslassen, das klingt ganz schön verlockend.",[18,19229,19230,19233,19234,19236,19237,19240,19241,19245],{},[573,19231,19232],{},"Und täglich grüßen die Microservices…"," Wieder ein Votrag von ",[27,19235,3410],{}," über Microservices, diesmal allerdings\nsteht eher die Technologie im Vordergrund. Es wird gezeigt, wie einfach man ",[27,19238,19239],{},"Microservices mit Spring Boot und Spring\nCloud"," erstellen kann. Ich staune einmal mehr über die immense Magie, die\nin ",[585,19242,8936],{"href":19243,"rel":19244},"http://projects.spring.io/spring-boot/",[589]," steckt. Und noch mehr, als ich höre, dass man Spring Boot\nApplikationen inzwischen sogar total easy-going als Unix Services ausführen kann.",[18,19247,19248,19251,19252,19255,19256,19259],{},[573,19249,19250],{},"Loft is in the air…"," Ich erklimme den weiten Weg nach oben ins Loft, um mir einen Vortrag über ",[27,19253,19254],{},"Docker für\nIntegrationtests"," von ",[27,19257,19258],{},"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.",[18,19261,19262,19263,19266,19267,19270],{},"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 ",[27,19264,19265],{},"Jochen Mader"," wohl sehr gute Talks halten soll. Daher folge ich\nmeinen Kollegen in seinen Vortrag über ",[27,19268,19269],{},"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.",[18,19272,19273],{},[1773,19274],{"alt":19275,"src":19276},"\"Java_Testing_Bingo\"","https://media.synyx.de/uploads//2015/09/Java_Testing_Bingo.jpg",[18,19278,19279,19280,19283,19284,19287,19288,1628,19291,19294,19295,19300,19301,19306],{},"Nach mehreren Runden Buzzword-Bingo (",[573,19281,19282],{},"It’s all about Microservices!",") habe ich nun auch Lust auf eine Runde ",[27,19285,19286],{},"Java\nTesting Bingo",". Tatsächlich verteilen ",[27,19289,19290],{},"Martin Klose",[27,19292,19293],{},"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 ",[585,19296,19299],{"href":19297,"rel":19298},"http://joel-costigliola.github.io/assertj/",[589],"AssertJ"," als Alternative\nzu ",[585,19302,19305],{"href":19303,"rel":19304},"http://hamcrest.org/",[589],"Hamcrest"," zu Gemüte führen werde.",[18,19308,19309,19310,19313,19314,19317],{},"…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. ",[27,19311,19312],{},"Timmo Freudl-Gierke"," stellt uns in seinem Vortrag ",[27,19315,19316],{},"Sharding Weather"," die\nDomäne Wetter genauer vor und erläutert seine Erfahrungen zum Aufbewahren von großen Datenmengen wie Wetterdaten.",[18,19319,19320],{},[1773,19321],{"alt":19322,"src":19323},"\"Die BED-Con Truppe\"","https://media.synyx.de/uploads//2015/09/Bedcon_Truppe.jpg",[18,19325,19326],{},"Eine weitere BED-Con geht zu Ende…aber die nächste kommt bestimmt 🙂",[18,19328,19329],{},"Bis dahin kann man noch eine Weile zehren von den Erkenntnissen, Ideen und Gedankenanstößen, die man so mit nach Hause\nnimmt.",[18,19331,19332],{},"Es hat sich mal wieder gelohnt!",{"title":48,"searchDepth":86,"depth":86,"links":19334},[19335,19336,19337],{"id":19013,"depth":126,"text":19014},{"id":19038,"depth":126,"text":19039},{"id":19200,"depth":126,"text":19201},[614],"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":18986,"description":19340},"blog/synyx-berlin-expert-days-2015",[19347,9500,4231,5743],"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…","rsFcwgNWbV3hUYF44I1lgKIVe8gs8HsxL5lwkMpttu4",{"id":19351,"title":19352,"author":19353,"body":19354,"category":19524,"date":19525,"description":19526,"extension":617,"link":19527,"meta":19528,"navigation":499,"path":19529,"seo":19530,"slug":19358,"stem":19531,"tags":19532,"teaser":19539,"__hash__":19540},"blog/blog/how-to-monitor-jaxrsjersey-applications.md","How to monitor JAXRS/Jersey applications",[16566],{"type":11,"value":19355,"toc":19522},[19356,19359,19362,19373,19381,19386,19389,19392,19401,19404,19463,19466,19473,19476,19493,19503,19510,19517,19520],[14,19357,19352],{"id":19358},"how-to-monitor-jaxrsjersey-applications",[18,19360,19361],{},"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.",[577,19363,19364,19367,19370],{},[580,19365,19366],{},"Who is the audience, who uses the API in what version?",[580,19368,19369],{},"How can I economize resources, but for specific cases only?",[580,19371,19372],{},"How can I use the data to prevent accidents or control specific nodes?",[18,19374,19375,19376,19380],{},"The code can be found at ",[585,19377,13838],{"href":19378,"rel":19379},"https://github.com/synyx/meter.git",[589]," and this article shall, simply spoken, show the\nmotivation behind it. So, in one sentence I would say:",[5221,19382,19383],{},[18,19384,19385],{},"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.",[18,19387,19388],{},"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.",[18,19390,19391],{},"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.",[18,19393,19394,19395,19400],{},"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 ",[585,19396,19399],{"href":19397,"rel":19398},"https://hk2.java.net/2.2.0/aop-example.html",[589],"aop-example",". That’s a pretty good news and a mighty joinpoint for\nmetrics, a quality library to gather runtime statistics.",[18,19402,19403],{},"Right now, our recipe contains Jersey, Metrics, AOP, and Java annotations as markup and if we plug everything together\nwe achieve something like this:",[43,19405,19407],{"className":288,"code":19406,"language":290,"meta":48,"style":48}," @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",[50,19408,19409,19414,19419,19424,19429,19434,19439,19444,19449,19454,19459],{"__ignoreMap":48},[53,19410,19411],{"class":55,"line":56},[53,19412,19413],{}," @GET\n",[53,19415,19416],{"class":55,"line":86},[53,19417,19418],{}," @Metric (\n",[53,19420,19421],{"class":55,"line":126},[53,19422,19423],{}," timers = @Timer,\n",[53,19425,19426],{"class":55,"line":163},[53,19427,19428],{}," histograms = @Histogram (value = \"#size\", measure = BambooResponseSize.class),\n",[53,19430,19431],{"class":55,"line":186},[53,19432,19433],{}," counters = @Counter (value = \"{color}\", kind = Kind.Error)\n",[53,19435,19436],{"class":55,"line":221},[53,19437,19438],{}," )\n",[53,19440,19441],{"class":55,"line":242},[53,19442,19443],{}," @Path (\"{name}\")\n",[53,19445,19446],{"class":55,"line":273},[53,19447,19448],{}," public String echo (@PathParam (\"name\") String name, @QueryParam (\"locale\") String locale, @DefaultValue (\"Green\") @QueryParam (\"color\") Color color) {\n",[53,19450,19451],{"class":55,"line":279},[53,19452,19453],{}," validate (color);\n",[53,19455,19456],{"class":55,"line":496},[53,19457,19458],{}," return service.call (name + \"::\" + locale);\n",[53,19460,19461],{"class":55,"line":503},[53,19462,860],{},[18,19464,19465],{},"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 :).",[18,19467,19468,19469,19472],{},"So what do I mean with ",[573,19470,19471],{},"a partition at runtime"," then?",[18,19474,19475],{},"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",[577,19477,19478,19481,19484,19487,19490],{},[580,19479,19480],{},"build metrics which are partitioned by customer levels : .., silver, gold, platinum",[580,19482,19483],{},"build metrics for versioned APIs: /v1/, …, /v9/ — anyone using v1, costs?",[580,19485,19486],{},"build metrics to track clients by geography, cookie, header",[580,19488,19489],{},"build metrics that measure errors only",[580,19491,19492],{},"build metrics for if-you-can-name-it-you-can-measure-it things.",[18,19494,19495],{},[573,19496,19497,19498],{},"A conversion takes place prior, so you can run every custom evaluation\nbeforehand: ",[585,19499,19502],{"href":19500,"rel":19501},"https://jersey.java.net/documentation/latest/user-guide.html#d0e2152",[589],"userguide",[18,19504,19505,19506,19509],{},"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 ",[27,19507,19508],{},"usecase"," you want.",[18,19511,19512,19513,19516],{},"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 ",[585,19514,13838],{"href":19378,"rel":19515},[589],", or even if you want to try out the example\nproject to get a first impression.",[18,19518,19519],{},"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.",[607,19521,989],{},{"title":48,"searchDepth":86,"depth":86,"links":19523},[],[613],"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":19352,"description":19361},"blog/how-to-monitor-jaxrsjersey-applications",[19533,19534,290,19535,19536,19537,19538],"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":19542,"title":19543,"author":19544,"body":19546,"category":19728,"date":19729,"description":19730,"extension":617,"link":19731,"meta":19732,"navigation":499,"path":19733,"seo":19734,"slug":19550,"stem":19736,"tags":19737,"teaser":19744,"__hash__":19745},"blog/blog/devoxx-poland-2015-summary.md","Devoxx Poland 2015 Summary",[19545],"szulc",{"type":11,"value":19547,"toc":19726},[19548,19551,19572,19578,19581,19586,19589,19608,19614,19619,19622,19625,19628,19634,19639,19642,19645,19651,19656,19659,19662,19667,19670,19673,19678,19681,19684,19687,19692,19695,19698,19701,19704,19707,19711,19714,19723],[14,19549,19543],{"id":19550},"devoxx-poland-2015-summary",[18,19552,19553,19554,19559,19560,19565,19566,19571],{},"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 ",[585,19555,19558],{"href":19556,"rel":19557},"http://devoxx.pl",[589],"Devoxx Poland"," (previously known\nas ",[585,19561,19564],{"href":19562,"rel":19563},"http://2014.33degree.org",[589],"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 ",[585,19567,19570],{"href":19568,"rel":19569},"http://www.icekrakow.pl/",[589],"ICE Conference Center",", which is located directly by the Vistula\nriver, with beautiful view over the Wawel Royal Castle.",[18,19573,19574],{},[1773,19575],{"alt":19576,"src":19577},"\"Inside the ICE Conference Center \"","https://media.synyx.de/uploads//2015/06/IMG_20150624_110556.jpg",[18,19579,19580],{},"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.",[18,19582,19583],{},[27,19584,19585],{},"1. The rise of Microservices",[18,19587,19588],{},"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.",[18,19590,19591,19592,19595,19596,19601,19602,19607],{},"I think we are in the peak phase. I remember two years ago at",[27,19593,19594],{},"GeeCON 2013","as I saw the\nfirst ",[585,19597,19600],{"href":19598,"rel":19599},"http://2013.geecon.org/speakers/sam-newman.html",[589],"talk"," 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 ",[585,19603,19606],{"href":19604,"rel":19605},"https://web.archive.org/web/20150503201315/http://cfp.devoxx.pl:80/2015/talk/MZA-9564/Modularity_in_post_microservice_world",[589],"one","\nlike this.",[18,19609,19610],{},[1773,19611],{"alt":19612,"src":19613},"\"Microservices Live-Coding Demo\"","https://media.synyx.de/uploads//2015/06/IMG_20150623_154822.jpg",[18,19615,19616],{},[27,19617,19618],{},"2. Reactive and Resilient by default",[18,19620,19621],{},"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.",[18,19623,19624],{},"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.",[18,19626,19627],{},"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.",[18,19629,19630],{},[1773,19631],{"alt":19632,"src":19633},"\"Main Room\"","https://media.synyx.de/uploads//2015/06/PANO_20150622_155838.jpg",[18,19635,19636],{},[27,19637,19638],{},"3. Functional Programming breaks (slowly) though",[18,19640,19641],{},"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.",[18,19643,19644],{},"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.",[18,19646,19647],{},[1773,19648],{"alt":19649,"src":19650},"\"View over Wawel Castle from ICE Center\"","https://media.synyx.de/uploads//2015/06/PANO_20150624_174232.jpg",[18,19652,19653],{},[27,19654,19655],{},"4. Java 8 throttles the rise of the new languages",[18,19657,19658],{},"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.",[18,19660,19661],{},"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.",[18,19663,19664],{},[27,19665,19666],{},"5. Java-Community focus on backend",[18,19668,19669],{},"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.",[18,19671,19672],{},"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”.",[18,19674,19675],{},[27,19676,19677],{},"6. Big Data and NoSQL are here",[18,19679,19680],{},"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.",[18,19682,19683],{},"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.",[18,19685,19686],{},"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.",[18,19688,19689],{},[27,19690,19691],{},"7. Spring is all what you need",[18,19693,19694],{},"It was probably for the first time I didn’t see any talk about alternative approach to Spring-Based Technologies (Java\nEE excluding).",[18,19696,19697],{},"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.",[18,19699,19700],{},"The same applies for the data-persistence solutions like ORMs for instance.",[18,19702,19703],{},"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.",[18,19705,19706],{},"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.",[18,19708,19709],{},[27,19710,12763],{},[18,19712,19713],{},"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.",[18,19715,19716,19717,19722],{},"I think it might be true that the Java Industry and the IT world in general is in\nthe",[585,19718,19721],{"href":19719,"rel":19720},"https://vimeo.com/130981099#t=2m56s",[589],"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.",[18,19724,19725],{},"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":48,"searchDepth":86,"depth":86,"links":19727},[],[613],"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":19543,"description":19735},"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",[19738,19739,19740,290,6991,18784,19741,19742,19743,1010],"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…","QWK3ox8PnfXTyuA0bTKcDvbny5tTEOTpJwT99Bdaw6g",{"id":19747,"title":19748,"author":19749,"body":19750,"category":19850,"date":19851,"description":19852,"extension":617,"link":19853,"meta":19854,"navigation":499,"path":19855,"seo":19856,"slug":19754,"stem":19858,"tags":19859,"teaser":19861,"__hash__":19862},"blog/blog/lets-add-some-value-part2.md","Let's add some value (part2)",[13202],{"type":11,"value":19751,"toc":19848},[19752,19755,19765,19768,19771,19774,19777,19780,19783,19786,19789,19792,19797,19806,19809,19812,19817,19820,19823,19828,19831,19834,19837,19840,19843],[14,19753,19748],{"id":19754},"lets-add-some-value-part2",[18,19756,19757,19758,19764],{},"In the ",[585,19759,19763],{"href":19760,"rel":19761,"title":19762},"http://blog.synyx.de/2014/11/lets-add-some-value/",[589],"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.",[18,19766,19767],{},"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’.",[18,19769,19770],{},"**#1 “We waste time and money by developing things which we do not need anymore once we finish the envisioned feature.”\n**",[18,19772,19773],{},"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.",[18,19775,19776],{},"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.",[18,19778,19779],{},"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.",[18,19781,19782],{},"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.",[18,19784,19785],{},"The lessons we should learn:",[18,19787,19788],{},"– Having a usable product early is worth some extra effort.",[18,19790,19791],{},"– An interim solution may prove to become a viable long-term solution.",[18,19793,19794],{},[27,19795,19796],{},"#2 “By starting with a small and simple implementation to solve problems early we can not get to a sophisticated\nfinal solution.”",[18,19798,19799,19800,19805],{},"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 ",[585,19801,19804],{"href":19802,"rel":19803},"http://en.wikipedia.org/wiki/Lean_manufacturing",[589],"lean manufacturing",") and while\nhigh code quality generates value by ensuring expandability and maintainability, no one needs artificially bloated\nsolutions.",[18,19807,19808],{},"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.",[18,19810,19811],{},"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!",[18,19813,19814],{},[27,19815,19816],{},"#3 “Only the complete feature will be adapted because partial solutions do not add enough value compared to existing\nsolutions”",[18,19818,19819],{},"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!",[18,19821,19822],{},"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.",[18,19824,19825],{},[27,19826,19827],{},"#4 “My problem can not be broken down because it is just too complex”",[18,19829,19830],{},"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.",[18,19832,19833],{},"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.",[18,19835,19836],{},"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.",[18,19838,19839],{},"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.",[18,19841,19842],{},"Let us surprise our customers by delivering working software instead of explaining why they have to wait some more time\nfor it.",[18,19844,19845],{},[27,19846,19847],{},"Finding ways to generate real value early may be hard but it is worth the effort.",{"title":48,"searchDepth":86,"depth":86,"links":19849},[],[613],"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":19748,"description":19857},"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",[4221,19860,4232],"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":19864,"title":19865,"author":19866,"body":19867,"category":19935,"date":19936,"description":19937,"extension":617,"link":19938,"meta":19939,"navigation":499,"path":19940,"seo":19941,"slug":19871,"stem":19943,"tags":19944,"teaser":19950,"__hash__":19951},"blog/blog/auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg.md","Auch die zweite Devoxx4Kids in Karlsruhe war ein Erfolg",[11619],{"type":11,"value":19868,"toc":19933},[19869,19872,19885,19891,19894,19897,19903,19906,19915,19924,19930],[14,19870,19865],{"id":19871},"auch-die-zweite-devoxx4kids-in-karlsruhe-war-ein-erfolg",[18,19873,19874,19875,19879,19880,19884],{},"Am 11.04.2015 fand die zweite ",[585,19876,18121],{"href":19877,"rel":19878},"http://devoxx4kids.org/deutschland",[589]," in Karlsruhe statt. Die Workshops\nwaren die gleichen wie beim letzten Mal im\nSeptember (",[585,19881,3449],{"href":19882,"rel":19883},"http://blog.synyx.de/2014/09/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids/",[589]," 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.",[18,19886,19887],{},[1773,19888],{"alt":19889,"src":19890},"\"kids_ges\"","https://media.synyx.de/uploads//2015/04/kids_ges.jpg",[18,19892,19893],{},"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.",[18,19895,19896],{},"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.",[18,19898,19899],{},[1773,19900],{"alt":19901,"src":19902},"\"bild_2\"","https://media.synyx.de/uploads//2015/04/bild_2.jpg",[18,19904,19905],{},"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 😉",[18,19907,19908,19909,19914],{},"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 ",[585,19910,19913],{"href":19911,"rel":19912},"http://www.tinkerforge.com/de",[589],"Tinkerforge"," 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.",[18,19916,19917,19918,19923],{},"Aber auch den Sponsoren ist zu danken. ",[585,19919,19922],{"href":19920,"rel":19921},"http://www.exensio.de/",[589],"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.",[18,19925,19926],{},[1773,19927],{"alt":19928,"src":19929},"\"copter\"","https://media.synyx.de/uploads//2015/04/copter.jpg",[18,19931,19932],{},"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":48,"searchDepth":86,"depth":86,"links":19934},[],[614],"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":19865,"description":19942},"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",[11755,19945,19946,19947,12320,19948,19949],"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…","PandpV-fJlmjwjmouRWWwOSMEtOqjvwAUzuniOYYpNI",{"id":19953,"title":19954,"author":19955,"body":19956,"category":20203,"date":20204,"description":20205,"extension":617,"link":20206,"meta":20207,"navigation":499,"path":20208,"seo":20209,"slug":19960,"stem":20211,"tags":20212,"teaser":20214,"__hash__":20215},"blog/blog/entwicklertag-frankfurt-2015.md","Entwicklertag Frankfurt 2015",[9507,11620],{"type":11,"value":19957,"toc":20196},[19958,19961,20006,20010,20024,20030,20034,20042,20051,20066,20075,20079,20087,20093,20096,20099,20105,20114,20117,20121,20163,20169,20171,20174,20182,20191],[14,19959,19954],{"id":19960},"entwicklertag-frankfurt-2015",[18,19962,19963,19964,19969,19970,19975,19976,19981,19982,19987,19988,19993,19994,19999,20000,20005],{},"Während der ",[585,19965,19968],{"href":19966,"rel":19967},"http://www.entwicklertag.de/",[589],"Karlsruher Entwicklertag"," der ",[585,19971,19974],{"href":19972,"rel":19973},"http://www.andrena.de/",[589],"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 ",[585,19977,19980],{"href":19978,"rel":19979},"http://sdq.ipd.kit.edu/people/ralf_reussner/",[589],"Professor Ralf Reussner","\nvom ",[585,19983,19986],{"href":19984,"rel":19985},"http://www.kit.edu/index.php",[589],"KIT"," einen\neindrucksvollen ",[585,19989,19992],{"href":19990,"rel":19991},"https://entwicklertag.de/frankfurt/2015/opening-session-was-brauchen-softwaretechniker-um-ingenieure-zu-werden",[589],"Einblick in die Informatik-Forschung","\nbot. Mit den von ihm betreuten Forschungsprojekten ",[585,19995,19998],{"href":19996,"rel":19997},"http://www.palladio-simulator.com/",[589],"Palladio","\nund ",[585,20001,20004],{"href":20002,"rel":20003},"https://sdqweb.ipd.kit.edu/wiki/Vitruvius",[589],"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.",[649,20007,20009],{"id":20008},"leadership-hacks","Leadership Hacks",[18,20011,20012,20013,20018,20019,20023],{},"Eine der originellsten und interessantesten Sessions war ",[585,20014,20017],{"href":20015,"rel":20016},"https://twitter.com/benjamin",[589],"Benjamin Reitzammers"," Talk\nüber ",[585,20020,20009],{"href":20021,"rel":20022},"https://entwicklertag.de/frankfurt/2015/5-leadership-hacks-oder-wie-ich-meine-ideen-umgesetzt-bekomme",[589],".\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!",[18,20025,20026],{},[1773,20027],{"alt":20028,"src":20029},"\"rockstar\"","https://media.synyx.de/uploads//2015/02/rockstar.png",[649,20031,20033],{"id":20032},"pecha-kucha","Pecha Kucha",[18,20035,20036,20037,20041],{},"Eine für uns neue Vortragstechnik, welche wir auf den Frankfurter Entwicklertagen kennenlernen durften, war\ndas ",[585,20038,20033],{"href":20039,"rel":20040},"http://de.wikipedia.org/wiki/Pecha_Kucha",[589],". 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.",[18,20043,20044,20045,20050],{},"Dr. Michael Eichberg gelang es in seinem\nVortrag ",[585,20046,20049],{"href":20047,"rel":20048},"http://www.entwicklertag.de/frankfurt/2015/your-jdk-devil-dark",[589],"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.",[18,20052,20053,20054,20059,20060,20065],{},"Im Votrag ",[585,20055,20058],{"href":20056,"rel":20057},"http://www.entwicklertag.de/frankfurt/2015/its-all-about-fun",[589],"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 ",[585,20061,20064],{"href":20062,"rel":20063},"http://de.wikipedia.org/wiki/Pomodoro-Technik",[589],"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.",[18,20067,20068,20069,20074],{},"Jeder hat ihn in der Firma und kennt ihn nur zu gut.\nDen ",[585,20070,20073],{"href":20071,"rel":20072},"http://www.entwicklertag.de/frankfurt/2015/der-prozess-sheriff-eine-unliebsame-spezies",[589],"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.",[649,20076,20078],{"id":20077},"antifragile-software-für-die-welt-des-21-jahrhunderts","Antifragile Software für die Welt des 21. Jahrhunderts",[18,20080,20081,20082,20086],{},"Der Nachmittag startete mit einem sehr spannenden Vortrag von Johannes\nLink, ",[585,20083,20078],{"href":20084,"rel":20085},"https://entwicklertag.de/frankfurt/2015/keynote-antifragile-software-f%C3%BCr-die-welt-des-21-jahrhunderts",[589],",\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.",[18,20088,20089],{},[1773,20090],{"alt":20091,"src":20092},"\"fragil\"","https://media.synyx.de/uploads//2015/02/fragil.png",[18,20094,20095],{},"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.",[18,20097,20098],{},"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?",[18,20100,20101],{},[1773,20102],{"alt":20103,"src":20104},"\"hormesis\"","https://media.synyx.de/uploads//2015/02/hormesis2.png",[18,20106,20107,20108,20113],{},"In der Natur wird dieses Phänomen ",[585,20109,20112],{"href":20110,"rel":20111},"http://de.wikipedia.org/wiki/Hormesis",[589],"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.",[18,20115,20116],{},"Zusammengefasst ein sehr guter Vortrag und ein spannendes Themengebiet. Mehr davon!",[649,20118,20120],{"id":20119},"weitere-vorträge","Weitere Vorträge",[18,20122,20123,20124,20127,20128,20133,20134,19999,20139,20144,20145,20150,20151,20156,20157,20162],{},"Auch von den restlichen Vorträgen waren einige erwähnenswert. Hagen Buchwald vom\nVeranstalter ",[585,20125,19974],{"href":19972,"rel":20126},[589]," erläuterte auf hohem Niveau und mit vielen Erfahrungen im\nGepäck inwiefern der Pfad eines Unternehmens zur Agilität von\nder ",[585,20129,20132],{"href":20130,"rel":20131},"https://entwicklertag.de/frankfurt/2015/agile-transition-core-culture-matters",[589],"Unternehmenskultur"," abhängt. Den\ngoldenen Pixel für den kreativsten Vortrag bekommen ",[585,20135,20138],{"href":20136,"rel":20137},"https://twitter.com/jensbroos",[589],"Jens Broos",[585,20140,20143],{"href":20141,"rel":20142},"https://twitter.com/mrupilo",[589],"Martin Ruprecht"," verliehen, die durch eine beeindruckende Anzahl von Analogien\nbelegten, was\ndie ",[585,20146,20149],{"href":20147,"rel":20148},"https://entwicklertag.de/frankfurt/2015/was-wir-software-entwickler-vom-modernen-fussball-lernen-k%C3%B6nnen",[589],"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 ",[585,20152,20155],{"href":20153,"rel":20154},"https://twitter.com/dasniko",[589],"Niko Köbler",", der verschiedene Wege und\nToolchains\naufzeigte, ",[585,20158,20161],{"href":20159,"rel":20160},"https://entwicklertag.de/frankfurt/2015/nodejs-auf-der-jvm-nodyn-und-avatarjs-im-vergleich",[589],"Node.js auf der JVM","\nzu nutzen, wobei anscheinend Nashörner aller Art eine größere Rolle spielen.",[18,20164,20165],{},[1773,20166],{"alt":20167,"src":20168},"\"aufstellung\"","https://media.synyx.de/uploads//2015/02/aufstellung.png",[649,20170,969],{"id":968},[18,20172,20173],{},"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.",[18,20175,20176,20177,20181],{},"Die Qualität der Talks war hoch, die Atmosphäre angenehm und das Catering ließ kaum Wünsche offen. Die Folien\nsind ",[585,20178,3449],{"href":20179,"rel":20180},"http://www.entwicklertag.de/frankfurt/2015/programm",[589]," ö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:",[18,20183,20184],{},[585,20185,20188],{"href":20186,"rel":20187},"http://xkcd.com/1447/",[589],[1773,20189],{"alt":48,"src":20190},"http://imgs.xkcd.com/comics/meta-analysis.png",[18,20192,20193],{},[573,20194,20195],{},"Quelle: xkcd.com",{"title":48,"searchDepth":86,"depth":86,"links":20197},[20198,20199,20200,20201,20202],{"id":20008,"depth":126,"text":20009},{"id":20032,"depth":126,"text":20033},{"id":20077,"depth":126,"text":20078},{"id":20119,"depth":126,"text":20120},{"id":968,"depth":126,"text":969},[613],"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":19954,"description":20210},"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",[4221,5579,3491,20213,4231,4232,6503],"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.…","Btw4IgxkifwFP3VIqPjGqsCL8uf60AJ4xKsGG3mj-_E",{"id":20217,"title":20218,"author":20219,"body":20221,"category":20470,"date":20471,"description":20472,"extension":617,"link":20473,"meta":20474,"navigation":499,"path":20475,"seo":20476,"slug":20225,"stem":20478,"tags":20479,"teaser":20481,"__hash__":20482},"blog/blog/javascript-linting-tool-evaluation.md","Javascript Linting Tool Evaluation",[20220,11620],"mueller",{"type":11,"value":20222,"toc":20461},[20223,20226,20239,20264,20273,20276,20280,20348,20351,20354,20367,20382,20385,20402,20405,20408,20419,20423,20426,20429,20432,20443,20447,20450,20456,20458],[14,20224,20218],{"id":20225},"javascript-linting-tool-evaluation",[18,20227,20228,20229,20234,20235,20238],{},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘",[585,20230,20233],{"href":20231,"rel":20232},"http://www.javaposse.com",[589],"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 ",[585,20236,5743],{"href":11728,"rel":20237},[589],", using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:",[577,20240,20241,20249,20256],{},[580,20242,20243,20248],{},[585,20244,20247],{"href":20245,"rel":20246},"http://jslint.com",[589],"JSLint"," by Doug Crockford himself",[580,20250,20251],{},[585,20252,20255],{"href":20253,"rel":20254},"https://developers.google.com/closure/utilities/",[589],"Closure Linter by Google",[580,20257,20258,20263],{},[585,20259,20262],{"href":20260,"rel":20261},"http://eslint.org",[589],"ESLint",", the new kid on the block",[18,20265,20266,20267,20272],{},"…as well as ",[585,20268,20271],{"href":20269,"rel":20270},"http://jshint.com/",[589],"JSHint"," itself, of course.",[18,20274,20275],{},"We drew up a quick spreadsheet for evaluating the tools and came up with the following.",[2352,20277,20279],{"id":20278},"criteria","Criteria",[577,20281,20282,20288,20294,20300,20306,20312,20318,20324,20330,20336,20342],{},[580,20283,20284,20287],{},[27,20285,20286],{},"Performance"," How long does it take to run over our example project, a single page webapp with a couple of thousands\nof JavaScript LOC?",[580,20289,20290,20293],{},[27,20291,20292],{},"Licensing"," Does the license meet our requirements (and those of our customers, of course)?",[580,20295,20296,20299],{},[27,20297,20298],{},"Project health/adoption"," How healthy is the project? Is it on Github, and is it well maintained?",[580,20301,20302,20305],{},[27,20303,20304],{},"Completeness of configurations"," Does the tool cover all our use-cases for a linting tool?",[580,20307,20308,20311],{},[27,20309,20310],{},"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?",[580,20313,20314,20317],{},[27,20315,20316],{},"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?",[580,20319,20320,20323],{},[27,20321,20322],{},"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)?",[580,20325,20326,20329],{},[27,20327,20328],{},"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?",[580,20331,20332,20335],{},[27,20333,20334],{},"Integration with build tool"," Is it possible to integrate the linting tool into your build chain to receive direct\nfeedback?",[580,20337,20338,20341],{},[27,20339,20340],{},"ES6 support"," How well does the project support future versions of the language?",[580,20343,20344,20347],{},[27,20345,20346],{},"Pluggable"," Is it possible to extend the given rule set with custom checks?",[2352,20349,20271],{"id":20350},"jshint",[18,20352,20353],{},"The first tool we looked at was the already-familiar JSHint. We already knew what was bothering us about it:",[577,20355,20356,20364],{},[580,20357,20358,20359,20363],{},"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, ",[585,20360,20361],{"href":20361,"rel":20362},"http://jshint.com/docs/options/",[589]," 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.",[580,20365,20366],{},"More often than not, using JSHint can be frustrating. For example, we had ‘maxdepth’",[18,20368,20369,20370,20373,20374,20377,20378,20381],{},"set to 3, meaning a maximum of three nested blocks of code was allowed. In case one of those blocks was a ‘",[573,20371,20372],{},"for … in","‘\nstatement, JSHint would (correctly) complain that its body should be wrapped in an ‘",[573,20375,20376],{},"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 ‘",[573,20379,20380],{},"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).",[18,20383,20384],{},"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.",[577,20386,20387],{},[580,20388,20389,20390,20395,20396,20401],{},"Being a fork of JSLint, JSHint has the same license containing the\ninfamous ",[585,20391,20394],{"href":20392,"rel":20393,"title":20394},"http://en.wikipedia.org/wiki/JSLint#License",[589],"JSLint License"," ‘Good, not evil’ statement.\nWhile we understand its humorous intent (and being\na ",[585,20397,20400],{"href":20398,"rel":20399,"title":20400},"https://synyx.de/unternehmen/verantwortung_csr/",[589],"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.",[2352,20403,20247],{"id":20404},"jslint",[18,20406,20407],{},"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):",[577,20409,20410,20413,20416],{},[580,20411,20412],{},"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.",[580,20414,20415],{},"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.",[580,20417,20418],{},"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).",[2352,20420,20422],{"id":20421},"closure-linter","Closure Linter",[18,20424,20425],{},"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.",[2352,20427,20262],{"id":20428},"eslint",[18,20430,20431],{},"When looking at ESLint, we were quick to decide that we might be looking at a potential winner:",[577,20433,20434,20437,20440],{},[580,20435,20436],{},"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.",[580,20438,20439],{},"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.",[580,20441,20442],{},"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.",[2352,20444,20446],{"id":20445},"evaluation","Evaluation",[18,20448,20449],{},"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.",[18,20451,20452],{},[1773,20453],{"alt":20454,"src":20455},"\"jsLinting\"","https://media.synyx.de/uploads//2015/02/jsLinting2.png",[2352,20457,12763],{"id":12762},[18,20459,20460],{},"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":48,"searchDepth":86,"depth":86,"links":20462},[20463,20464,20465,20466,20467,20468,20469],{"id":20278,"depth":86,"text":20279},{"id":20350,"depth":86,"text":20271},{"id":20404,"depth":86,"text":20247},{"id":20421,"depth":86,"text":20422},{"id":20428,"depth":86,"text":20262},{"id":20445,"depth":86,"text":20446},{"id":12762,"depth":86,"text":12763},[613,996],"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":20218,"description":20477},"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",[7337,20428,20445,6991,13483,20350,20404,20480,6503],"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…","QcXdToJb-JTf6znqD4o0GbdwGfu2XcNA6y-tOvH75ck",{"id":20484,"title":20485,"author":20486,"body":20488,"category":20751,"date":20752,"description":20753,"extension":617,"link":20754,"meta":20755,"navigation":499,"path":20756,"seo":20757,"slug":20492,"stem":20759,"tags":20760,"teaser":20766,"__hash__":20767},"blog/blog/time-series-data-is-the-the-new-big-data.md","Time Series Data is the the new Big Data",[8052,20487],"kesler",{"type":11,"value":20489,"toc":20738},[20490,20493,20511,20532,20536,20545,20549,20567,20576,20580,20598,20602,20613,20627,20630,20634,20637,20645,20648,20652,20661,20665,20668,20672,20681,20685,20688,20721,20724],[14,20491,20485],{"id":20492},"time-series-data-is-the-the-new-big-data",[18,20494,20495,20496,20501,20502,20506,20507],{},"On 22 November 2014, the ",[585,20497,20500],{"href":20498,"rel":20499},"https://2014.nosql-matters.org/bcn/homepage/",[589],"NoSQL matters conference"," took place in\nBarcelona at the ",[585,20503,18568],{"href":20504,"rel":20505},"http://www.uab-casaconvalescencia.org/en/index.php",[589],", 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.",[1773,20508],{"alt":20509,"src":20510},"\"20141122_090344\"","https://media.synyx.de/uploads//2014/11/20141122_090344.jpg",[18,20512,20513,20514,20519,20520,20525,20526,20531],{},"This year, Ellen Friedman (",[585,20515,20518],{"href":20516,"rel":20517},"https://twitter.com/ellen_friedman",[589],"@Ellen_Friedman",") and Ted\nDunning (",[585,20521,20524],{"href":20522,"rel":20523},"https://twitter.com/ted_dunning",[589],"@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 ",[585,20527,20530],{"href":20528,"rel":20529},"http://mapr.com",[589],"MapR"," at the moment.\nMapR also held a training at the training day of NoSQL matters Barcelona.",[2352,20533,20535],{"id":20534},"there-is-time-series-data-everywhere","There is (time series) data everywhere",[18,20537,20538,20539,20544],{},"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 ",[585,20540,20543],{"href":20541,"rel":20542},"http://en.wikipedia.org/wiki/Internet_of_Things",[589],"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?",[649,20546,20548],{"id":20547},"the-history-of-time-series-data","The history of Time Series Data",[18,20550,20551,20552,99,20555,17019,20557,20560,20561,20566],{},"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 ",[27,20553,20554],{},"crowdsourced",[27,20556,11042],{},[27,20558,20559],{},"big data"," analysis project dates back about 170\nyears: ",[585,20562,20565],{"href":20563,"rel":20564},"http://en.wikipedia.org/wiki/Matthew_Fontaine_Maury",[589],"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!",[18,20568,20569,20570,20575],{},"His thorough analysis resulted in his ",[585,20571,20574],{"href":20572,"rel":20573},"http://icoads.noaa.gov/maury.pdf",[589],"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.",[649,20577,20579],{"id":20578},"see-with-your-eyes-closed-think-with-your-eyes-open","See with your eyes closed – think with your eyes open",[18,20581,20582,20583,20586,20587,20590,20591,20594,20595,986],{},"This example does not only show how time series data analysis can be used for informed ",[27,20584,20585],{},"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",[573,20588,20589],{},"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 ",[573,20592,20593],{},"eyes closed\nseeing",", his keen observation and focus on the details ",[573,20596,20597],{},"eyes open thinking",[649,20599,20601],{"id":20600},"big-data-in-the-blink-of-an-eye","Big data in the blink of an eye",[18,20603,20604,20605,20608,20609,20612],{},"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 ",[27,20606,20607],{},"before"," 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. ",[27,20610,20611],{},"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",[577,20614,20615,20618,20621,20624],{},[580,20616,20617],{},"prognostication",[580,20619,20620],{},"introspection",[580,20622,20623],{},"prediction and",[580,20625,20626],{},"diagnosis",[18,20628,20629],{},"might get possible.",[2352,20631,20633],{"id":20632},"how-to-use-time-series-databases","How to use time series databases",[18,20635,20636],{},"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",[577,20638,20639,20642],{},[580,20640,20641],{},"a huge amount of (time series) data, and",[580,20643,20644],{},"queries mostly based on time or time ranges",[18,20646,20647],{},"you might think about using a time series databases (TSDB) that enables you to efficiently analyze the data.",[649,20649,20651],{"id":20650},"why-not-use-a-relational-database-system","Why not use a relational database system?",[18,20653,20654,20655,20660],{},"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 ",[585,20656,20659],{"href":20657,"rel":20658},"http://opentsdb.net/",[589],"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.",[649,20662,20664],{"id":20663},"where-to-go-from-here","Where to go from here?",[18,20666,20667],{},"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.",[2352,20669,20671],{"id":20670},"and-this-is-how-nosql-changes-society","And this is how NoSQL changes society",[18,20673,20674,20675,20680],{},"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 ",[585,20676,20679],{"href":20677,"rel":20678},"http://en.wikipedia.org/wiki/Unique_Identification_Authority_of_India",[589],"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.",[2352,20682,20684],{"id":20683},"further-readings","Further readings",[18,20686,20687],{},"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. 😉",[577,20689,20690,20698,20706,20713],{},[580,20691,20692,20697],{},[585,20693,20696],{"href":20694,"rel":20695},"https://web.archive.org/web/20150502150101/http://info.mapr.com:80/resources-ebook-Time-Series-Databases.html",[589],"Time series databases","\nby Ted Dunning and Ellen Friedman",[580,20699,20700,20705],{},[585,20701,20704],{"href":20702,"rel":20703},"http://shop.oreilly.com/product/0636920034650.do",[589],"Practical Machine Learning: A New Look at Anomaly Detection"," by Ted\nDunning and Ellen Friedman",[580,20707,20708,20705],{},[585,20709,20712],{"href":20710,"rel":20711},"https://www.mapr.com/practical-machine-learning",[589],"Practical Machine Learning: Innovations in Recommendation",[580,20714,20715,20716,20720],{},"The talks by Friedman and Dunning on the topic given at ",[585,20717,18558],{"href":20718,"rel":20719},"https://2014.nosql-matters.org/bcn/abstracts/",[589],"\nin Barcelona",[18,20722,20723],{},"Or just download some software and try it out yourself:",[577,20725,20726,20731],{},[580,20727,20728],{},[585,20729,20659],{"href":20657,"rel":20730},[589],[580,20732,20733],{},[585,20734,20737],{"href":20735,"rel":20736},"https://github.com/mapr-demos/opentsdb",[589],"Open source MapR extensions",{"title":48,"searchDepth":86,"depth":86,"links":20739},[20740,20745,20749,20750],{"id":20534,"depth":86,"text":20535,"children":20741},[20742,20743,20744],{"id":20547,"depth":126,"text":20548},{"id":20578,"depth":126,"text":20579},{"id":20600,"depth":126,"text":20601},{"id":20632,"depth":86,"text":20633,"children":20746},[20747,20748],{"id":20650,"depth":126,"text":20651},{"id":20663,"depth":126,"text":20664},{"id":20670,"depth":86,"text":20671},{"id":20683,"depth":86,"text":20684},[613],"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":20485,"description":20758},"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",[20761,20762,20763,20764,20765],"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…","TjyEHNiqN5_YzqUT-k2YU7jRmPtAZH1WQKtcKhSx31s",{"id":20769,"title":20770,"author":20771,"body":20772,"category":20936,"date":20937,"description":48,"extension":617,"link":20938,"meta":20939,"navigation":499,"path":20940,"seo":20941,"slug":20776,"stem":20942,"tags":20943,"teaser":20944,"__hash__":20945},"blog/blog/lets-add-some-value.md","Let's add some value!",[13202],{"type":11,"value":20773,"toc":20932},[20774,20777,20780,20788,20808,20811,20817,20821,20824,20827,20844,20847,20852,20855,20858,20861,20884,20887,20890,20901,20910,20913,20916,20925,20928],[14,20775,20770],{"id":20776},"lets-add-some-value",[2352,20778,16733],{"id":20779},"tldr",[18,20781,20782,20783,12136],{},"Follow principles #1 and #3 from the ",[585,20784,20787],{"href":20785,"rel":20786},"http://agilemanifesto.org/",[589],"agile manifesto",[5221,20789,20790,20793,20796,20799,20802,20805],{},[18,20791,20792],{},"“Our highest priority is to satisfy the customer",[18,20794,20795],{},"through early and continuous delivery",[18,20797,20798],{},"of valuable software. ”",[18,20800,20801],{},"“Deliver working software frequently, from a",[18,20803,20804],{},"couple of weeks to a couple of months, with a",[18,20806,20807],{},"preference to the shorter timescale.”",[18,20809,20810],{},"Or in my own words:",[18,20812,20813,20814],{},"In each iteration ask yourself: ",[27,20815,20816],{},"While working towards a big vision can we solve at least one real problem by the end\nof this iteration?",[2352,20818,20820],{"id":20819},"part-1-the-problem-with-technical-stories","Part 1: The problem with technical stories",[18,20822,20823],{},"In Scrum we deliver a potential shippable product increment in each iteration. Following this directive has many\nadvantages for us:",[18,20825,20826],{},"We can…",[577,20828,20829,20832,20835,20838,20841],{},[580,20830,20831],{},"detect errors or misunderstandings very early through feedback from stakeholders",[580,20833,20834],{},"fail fast by discovering critical problems as early as possible",[580,20836,20837],{},"find out what users really want/need by experimenting with early versions of a feature",[580,20839,20840],{},"generate value by solving real problems with each new version of the product",[580,20842,20843],{},"change priorities based on new circumstances",[18,20845,20846],{},"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.",[18,20848,20849],{},[27,20850,20851],{},"A partially finished feature has no value but a smaller feature which solves part of a problem has.",[18,20853,20854],{},"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.",[18,20856,20857],{},"So we start to create tasks to fulfil the epic:",[18,20859,20860],{},"We…",[577,20862,20863,20866,20869,20872,20875,20878,20881],{},[580,20864,20865],{},"analyse the problem",[580,20867,20868],{},"design a possible solution",[580,20870,20871],{},"evaluate technology",[580,20873,20874],{},"implement the frontend with backend mockups",[580,20876,20877],{},"implement the backend while mocking calls to 3rd party services",[580,20879,20880],{},"integrate the real 3rd party services",[580,20882,20883],{},"… and the list goes on",[18,20885,20886],{},"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.",[18,20888,20889],{},"We can still…",[577,20891,20892,20895,20898],{},[580,20893,20894],{},"try out early versions of the software in an experimental environment",[580,20896,20897],{},"get feedback from stakeholders by showing them design sketches, frontend mockups or partially implemented features",[580,20899,20900],{},"fail fast if we discover that a chosen technology does not work as expected",[18,20902,20903,20904,20909],{},"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 ",[585,20905,20908],{"href":20906,"rel":20907},"http://en.wikipedia.org/wiki/Time_to_market",[589],"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.",[18,20911,20912],{},"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.",[18,20914,20915],{},"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?",[18,20917,20918,20919,20924],{},"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 ",[585,20920,20923],{"href":20921,"rel":20922},"http://en.wikipedia.org/wiki/Technical_debt",[589],"technical debt"," which must eventually be repaid.",[18,20926,20927],{},"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:",[18,20929,20930],{},[27,20931,19847],{},{"title":48,"searchDepth":86,"depth":86,"links":20933},[20934,20935],{"id":20779,"depth":86,"text":16733},{"id":20819,"depth":86,"text":20820},[614],"2014-11-12T17:05:06","https://synyx.de/blog/lets-add-some-value/",{},"/blog/lets-add-some-value",{"title":20770,"description":48},"blog/lets-add-some-value",[4221,19860,4232],"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":20947,"title":20948,"author":20949,"body":20950,"category":21088,"date":21089,"description":21090,"extension":617,"link":21091,"meta":21092,"navigation":499,"path":21093,"seo":21094,"slug":21096,"stem":21097,"tags":21098,"teaser":21100,"__hash__":21101},"blog/blog/devoxx4kids-in-karlsruhe-programmieren-und-elektronik-fur-kids.md","Devoxx4Kids in Karlsruhe – Programmieren und Elektronik für Kids",[8052],{"type":11,"value":20951,"toc":21083},[20952,20955,20972,20976,20979,20991,21004,21010,21017,21023,21031,21035,21055,21059,21077,21080],[14,20953,20948],{"id":20954},"devoxx4kids-in-karlsruhe-programmieren-und-elektronik-für-kids",[18,20956,5442,20957,20961,20962,20967,20968,20971],{},[585,20958,18121],{"href":20959,"rel":20960},"http://www.devoxx4kids.org/deutschland/",[589]," 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 (",[585,20963,20966],{"href":20964,"rel":20965},"http://www.bejug.org/",[589],"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 ",[585,20969,11735],{"href":11733,"rel":20970},[589]," 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.",[2352,20973,20975],{"id":20974},"die-workshops","Die Workshops",[18,20977,20978],{},"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’.",[18,20980,20981,20985,20990],{},[1773,20982],{"alt":20983,"src":20984},"\"IMG_9017\"","https://media.synyx.de/uploads//2014/09/IMG_9017.jpg",[585,20986,20989],{"href":20987,"rel":20988},"http://scratch.mit.edu/",[589],"Scratch","\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.",[18,20992,20993,20997,20998,21003],{},[1773,20994],{"alt":20995,"src":20996},"\"IMG_9083\"","https://media.synyx.de/uploads//2014/09/IMG_9083.jpg","\nDer ",[585,20999,21002],{"href":21000,"rel":21001},"https://web.archive.org/web/20160201111355/https://www.aldebaran.com/en",[589],"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).",[18,21005,21006],{},[1773,21007],{"alt":21008,"src":21009},"\"IMG_8947\"","https://media.synyx.de/uploads//2014/09/IMG_8947.jpg",[18,21011,21012,21013,21016],{},"Als Piloten agierten die Kinder im ",[27,21014,21015],{},"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.",[18,21018,21019],{},[1773,21020],{"alt":21021,"src":21022},"\"IMG_8994\"","https://media.synyx.de/uploads//2014/09/IMG_8994.jpg",[18,21024,21025,21026,21030],{},"Eine Einführung in das ‘Internet der Dinge’ gab der Tinkerforge-Workshop. ",[585,21027,19913],{"href":21028,"rel":21029},"http://www.tinkerforge.com",[589],"\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.",[2352,21032,21034],{"id":21033},"betreuung-und-sponsoren","Betreuung und Sponsoren",[18,21036,21037,21041,21042,21044,21045,21048,21049,21051,21052,21054],{},[1773,21038],{"alt":21039,"src":21040},"\"IMG_9163\"","https://media.synyx.de/uploads//2014/09/IMG_9163.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 ",[27,21043,5743],{},", aber weitere Sponsoren haben mit zum Erfolg beigetragen. Vielen\nDank hier an ",[27,21046,21047],{},"Citrix"," (Goldsponsor), ",[27,21050,19913],{}," (die den Kindern jeweils ein Elektronikset mit nach Hause gaben)\nund die ",[27,21053,11735],{}," (die ihre tollen Räume zur Verfügung stellte).",[2352,21056,21058],{"id":21057},"nachhaltiger-lernspaß","Nachhaltiger Lernspaß",[18,21060,21061,21065,21066,21070,21071,21076],{},[1773,21062],{"alt":21063,"src":21064},"\"IMG_9143\"","https://media.synyx.de/uploads//2014/09/IMG_9143.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 ",[585,21067,21069],{"href":20959,"rel":21068,"title":18121},[589],"Homepage"," und\nauf ",[585,21072,13838],{"href":21073,"rel":21074,"title":21075},"https://github.com/devoxx4kids/",[589],"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!",[18,21078,21079],{},"Vielen Dank an alle Beteiligten und bis zum nächsten Mal sagt Euer",[18,21081,21082],{},"Devoxx4Kids Team aus Deutschland",{"title":48,"searchDepth":86,"depth":86,"links":21084},[21085,21086,21087],{"id":20974,"depth":86,"text":20975},{"id":21033,"depth":86,"text":21034},{"id":21057,"depth":86,"text":21058},[614],"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":20948,"description":21095},"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",[11755,19945,21099,19947,12320,5743,19948,11768],"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…","oP2EdVF_2t-8XpvKtpQEvPtlKUh9kJRYiSvTsgKBqMU",{"id":21103,"title":21104,"author":21105,"body":21106,"category":21425,"date":21426,"description":21427,"extension":617,"link":21428,"meta":21429,"navigation":499,"path":21430,"seo":21431,"slug":21110,"stem":21432,"tags":21433,"teaser":21439,"__hash__":21440},"blog/blog/the-qt-framework-solid-fun-in-many-languages.md","The Qt framework: solid fun in many languages",[12327],{"type":11,"value":21107,"toc":21423},[21108,21111,21114,21131,21137,21140,21143,21147,21150,21158,21164,21167,21175,21178,21182,21185,21193,21199,21202,21210,21213,21216,21308,21316,21320,21328,21331,21340,21349,21358,21367,21376,21385,21394,21403,21412,21421],[14,21109,21104],{"id":21110},"the-qt-framework-solid-fun-in-many-languages",[18,21112,21113],{},"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.",[18,21115,21116,21117,3566,21120,3566,21123,21126,21127,21130],{},"While written in C++, Qt has many language bindings",[585,21118,7598],{"href":21119},"#sdfootnote1sym",[585,21121,7607],{"href":21122},"#sdfootnote2sym",[585,21124,7615],{"href":21125},"#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",[585,21128,7633],{"href":21129},"#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.",[18,21132,21133],{},[1773,21134],{"alt":21135,"src":21136},"\"qt_imagecomposer_qt-creator\"","https://media.synyx.de/uploads//2014/09/qt_imagecomposer_qt-creator.jpg",[18,21138,21139],{},"Screenshot 1: Image Composition sample application running on top of Qt Creator IDE.",[18,21141,21142],{},"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).",[14,21144,21146],{"id":21145},"getting-started","Getting started",[18,21148,21149],{},"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.",[18,21151,21152,21153,21157],{},"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",[585,21154,21156],{"href":21155},"#sdfootnote5sym","5",", which\ngoes one step further and even loads a JQuery instance into the JavaScript runtime to perform HTML manipulation.",[18,21159,21160],{},[1773,21161],{"alt":21162,"src":21163},"\"qt_fancybrowser\"","https://media.synyx.de/uploads//2014/09/qt_fancybrowser.jpg",[18,21165,21166],{},"Screenshot 2: Fancy Browser example application.",[18,21168,21169,21170,21174],{},"For a hobby project I took this basic concept and made a more full-featured browser",[585,21171,21173],{"href":21172},"#sdfootnote6sym","6",", 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.",[18,21176,21177],{},"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.",[14,21179,21181],{"id":21180},"enter-qml","Enter QML",[18,21183,21184],{},"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.",[18,21186,21187,21188,21192],{},"Places where QML is used include (outside of mobile/embedded) KDE and the Unity UI (as of version 8",[585,21189,21191],{"href":21190},"#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).",[18,21194,21195],{},[1773,21196],{"alt":21197,"src":21198},"\"Qt PhotoViewer sample\"","https://media.synyx.de/uploads//2014/09/qt_photo_viewer.jpg",[18,21200,21201],{},"Screenshot 3: Photo Viewer example. QML with minimal JavaScript.",[18,21203,21204,21205,21209],{},"The Photo Viewer QML example application",[585,21206,21208],{"href":21207},"#sdfootnote8sym","8"," 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.",[18,21211,21212],{},"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.",[18,21214,21215],{},"QML isn’t just about static content either. Using Qt’s multimedia features one can for example quickly set up a video\nplayer:",[43,21217,21219],{"className":13667,"code":21218,"language":13669,"meta":48,"style":48},"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",[50,21220,21221,21226,21231,21236,21241,21246,21251,21256,21261,21266,21271,21276,21280,21284,21289,21294,21299,21304],{"__ignoreMap":48},[53,21222,21223],{"class":55,"line":56},[53,21224,21225],{},"import QtQuick 2.0\n",[53,21227,21228],{"class":55,"line":86},[53,21229,21230],{},"import QtMultimedia 5.0\n",[53,21232,21233],{"class":55,"line":126},[53,21234,21235],{},"Video {\n",[53,21237,21238],{"class":55,"line":163},[53,21239,21240],{}," id: video\n",[53,21242,21243],{"class":55,"line":186},[53,21244,21245],{}," width : 800\n",[53,21247,21248],{"class":55,"line":221},[53,21249,21250],{}," height : 600\n",[53,21252,21253],{"class":55,"line":242},[53,21254,21255],{}," source: \"video.avi\"\n",[53,21257,21258],{"class":55,"line":273},[53,21259,21260],{}," \u003Ca class=\"broken_link\" href=\"http://qt-project.org/doc/qt-5/qml-qtquick-mousearea.html\">MouseArea\u003C/a> {\n",[53,21262,21263],{"class":55,"line":279},[53,21264,21265],{}," anchors.fill: parent\n",[53,21267,21268],{"class":55,"line":496},[53,21269,21270],{}," onClicked: {\n",[53,21272,21273],{"class":55,"line":503},[53,21274,21275],{}," video.play()\n",[53,21277,21278],{"class":55,"line":509},[53,21279,12712],{},[53,21281,21282],{"class":55,"line":515},[53,21283,860],{},[53,21285,21286],{"class":55,"line":521},[53,21287,21288],{}," focus: true\n",[53,21290,21291],{"class":55,"line":527},[53,21292,21293],{}," Keys.onSpacePressed: video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play()\n",[53,21295,21296],{"class":55,"line":533},[53,21297,21298],{}," Keys.onLeftPressed: video.seek(video.position - 5000)\n",[53,21300,21301],{"class":55,"line":539},[53,21302,21303],{}," Keys.onRightPressed: video.seek(video.position + 5000)\n",[53,21305,21306],{"class":55,"line":545},[53,21307,282],{},[18,21309,21310,21311,21315],{},"This sample, taken from the Qt Video QML type documentation",[585,21312,21314],{"href":21313},"#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.",[14,21317,21319],{"id":21318},"wrapping-up","Wrapping up",[18,21321,21322,21323,21327],{},"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",[585,21324,21326],{"href":21325},"#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.",[18,21329,21330],{},"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.",[18,21332,21333,21336],{},[585,21334,7598],{"href":21335},"#sdfootnote1anc",[585,21337,21338],{"href":21338,"rel":21339},"http://qt-project.org/wiki/Category:LanguageBindings",[589],[18,21341,21342,21345],{},[585,21343,7607],{"href":21344},"#sdfootnote2anc",[585,21346,21347],{"href":21347,"rel":21348},"http://en.wikipedia.org/wiki/List_of_language_bindings_for_Qt_4",[589],[18,21350,21351,21354],{},[585,21352,7615],{"href":21353},"#sdfootnote3anc",[585,21355,21356],{"href":21356,"rel":21357},"http://en.wikipedia.org/wiki/List_of_language_bindings_for_Qt_5",[589],[18,21359,21360,21363],{},[585,21361,7633],{"href":21362},"#sdfootnote4anc",[585,21364,21365],{"href":21365,"rel":21366},"http://qt-project.org/doc/qt-5/supported-platforms.html",[589],[18,21368,21369,21372],{},[585,21370,21156],{"href":21371},"#sdfootnote5anc",[585,21373,21374],{"href":21374,"rel":21375},"http://qt-project.org/doc/qt-5/qtwebkitexamples-webkitwidgets-fancybrowser-example.html",[589],[18,21377,21378,21381],{},[585,21379,21173],{"href":21380},"#sdfootnote6anc",[585,21382,21383],{"href":21383,"rel":21384},"http://mayaposch.com/wildfox.php",[589],[18,21386,21387,21390],{},[585,21388,21191],{"href":21389},"#sdfootnote7anc",[585,21391,21392],{"href":21392,"rel":21393},"https://unity.ubuntu.com/getinvolved/development/unity8/",[589],[18,21395,21396,21399],{},[585,21397,21208],{"href":21398},"#sdfootnote8anc",[585,21400,21401],{"href":21401,"rel":21402},"http://qt-project.org/doc/qt-5/qtquick-demos-photoviewer-example.html",[589],[18,21404,21405,21408],{},[585,21406,21314],{"href":21407},"#sdfootnote9anc",[585,21409,21410],{"href":21410,"rel":21411},"http://qt-project.org/doc/qt-5/qml-qtmultimedia-video.html",[589],[18,21413,21414,21417],{},[585,21415,21326],{"href":21416},"#sdfootnote10anc",[585,21418,21419],{"href":21419,"rel":21420},"http://qt-project.org/",[589],[607,21422,989],{},{"title":48,"searchDepth":86,"depth":86,"links":21424},[],[613],"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":21104,"description":21113},"blog/the-qt-framework-solid-fun-in-many-languages",[12568,21434,6991,21435,21436,21437,21438],"cross-platform","mobile","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…","pEQ_OlddqNbA7CWP-ltLaMJp7EGQfBl6i6zBkOADnbs",{"id":21442,"title":21443,"author":21444,"body":21445,"category":22390,"date":22391,"description":21452,"extension":617,"link":22392,"meta":22393,"navigation":499,"path":22394,"seo":22395,"slug":22396,"stem":22397,"tags":22398,"teaser":22401,"__hash__":22402},"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",[12581],{"type":11,"value":21446,"toc":22388},[21447,21450,21453,21467,21480,21483,21486,21489,21492,21497,21520,21525,21618,21623,21689,21692,21697,21744,21747,21752,21884,21887,21902,21905,21910,22036,22041,22115,22136,22142,22149,22153,22167,22173,22177,22252,22257,22347,22358,22361,22364,22371,22378,22386],[14,21448,21443],{"id":21449},"spock-testing-from-the-unit-up-to-the-integration-level",[18,21451,21452],{},"There are a number of reasons to use the Spock testing framework:",[18,21454,21455,21456,21459,21460,17019,21463,21466],{},"First, tests – ",[573,21457,21458],{},"specifications"," in Spock speak – written in Spock are well structured, expressive and therefore provide\ngood readability. In addition, Spock has built-in features like ",[573,21461,21462],{},"data driven testing",[573,21464,21465],{},"interaction based testing"," (\nmocking). Data driven testing allows your test code to be reused, i.e. to be applied multiple times with different\nparameters.",[18,21468,21469,21470,21473,21474,21479],{},"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. ",[573,21471,21472],{},"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 ",[585,21475,21478],{"href":21476,"rel":21477},"https://web.archive.org/web/20150313003201/http://groovy.codehaus.org:80/modules/http-builder/home.html",[589],"HTTPBuilder","\nenhances the HttpComponents HttpClient by providing features like various builders & parsers and a streamlined REST\nclient.",[18,21481,21482],{},"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.",[18,21484,21485],{},"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.",[18,21487,21488],{},"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.",[18,21490,21491],{},"We start by defining a service interface, its implementation and a controller class:",[18,21493,21494],{},[50,21495,21496],{},"src/main/java/prime/service/PrimeService.java",[43,21498,21500],{"className":288,"code":21499,"language":290,"meta":48,"style":48},"\npublic interface PrimeService {\n boolean isPrime(int number);\n}\n\n",[50,21501,21502,21506,21511,21516],{"__ignoreMap":48},[53,21503,21504],{"class":55,"line":56},[53,21505,500],{"emptyLinePlaceholder":499},[53,21507,21508],{"class":55,"line":86},[53,21509,21510],{},"public interface PrimeService {\n",[53,21512,21513],{"class":55,"line":126},[53,21514,21515],{}," boolean isPrime(int number);\n",[53,21517,21518],{"class":55,"line":163},[53,21519,282],{},[18,21521,21522],{},[50,21523,21524],{},"src/main/java/prime/service/PrimeServiceImpl.java",[43,21526,21528],{"className":288,"code":21527,"language":290,"meta":48,"style":48},"\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",[50,21529,21530,21534,21539,21544,21548,21553,21558,21563,21567,21572,21577,21581,21586,21591,21596,21601,21605,21610,21614],{"__ignoreMap":48},[53,21531,21532],{"class":55,"line":56},[53,21533,500],{"emptyLinePlaceholder":499},[53,21535,21536],{"class":55,"line":86},[53,21537,21538],{},"@Service\n",[53,21540,21541],{"class":55,"line":126},[53,21542,21543],{},"public class PrimeServiceImpl implements PrimeService {\n",[53,21545,21546],{"class":55,"line":163},[53,21547,13033],{},[53,21549,21550],{"class":55,"line":186},[53,21551,21552],{}," public boolean isPrime(int number) {\n",[53,21554,21555],{"class":55,"line":221},[53,21556,21557],{}," if (number \u003C 0) {\n",[53,21559,21560],{"class":55,"line":242},[53,21561,21562],{}," throw new IllegalArgumentException(\"argument must not be negative\");\n",[53,21564,21565],{"class":55,"line":273},[53,21566,12712],{},[53,21568,21569],{"class":55,"line":279},[53,21570,21571],{}," if (number \u003C= 2) {\n",[53,21573,21574],{"class":55,"line":496},[53,21575,21576],{}," return number == 2 ? true : false;\n",[53,21578,21579],{"class":55,"line":503},[53,21580,12712],{},[53,21582,21583],{"class":55,"line":509},[53,21584,21585],{}," for (int i = 2; i \u003C Math.sqrt(number) + 1; i++) {\n",[53,21587,21588],{"class":55,"line":515},[53,21589,21590],{}," if (number % i == 0) {\n",[53,21592,21593],{"class":55,"line":521},[53,21594,21595],{}," return false;\n",[53,21597,21598],{"class":55,"line":527},[53,21599,21600],{}," }\n",[53,21602,21603],{"class":55,"line":533},[53,21604,12712],{},[53,21606,21607],{"class":55,"line":539},[53,21608,21609],{}," return true;\n",[53,21611,21612],{"class":55,"line":545},[53,21613,860],{},[53,21615,21616],{"class":55,"line":2070},[53,21617,282],{},[18,21619,21620],{},[50,21621,21622],{},"src/main/groovy/prime/web/PrimeController.groovy",[43,21624,21626],{"className":288,"code":21625,"language":290,"meta":48,"style":48},"\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",[50,21627,21628,21632,21637,21642,21647,21652,21657,21662,21666,21671,21676,21681,21685],{"__ignoreMap":48},[53,21629,21630],{"class":55,"line":56},[53,21631,500],{"emptyLinePlaceholder":499},[53,21633,21634],{"class":55,"line":86},[53,21635,21636],{},"@RestController\n",[53,21638,21639],{"class":55,"line":126},[53,21640,21641],{},"class PrimeController {\n",[53,21643,21644],{"class":55,"line":163},[53,21645,21646],{}," @Autowired PrimeService primeService;\n",[53,21648,21649],{"class":55,"line":186},[53,21650,21651],{}," @ExceptionHandler(Exception)\n",[53,21653,21654],{"class":55,"line":221},[53,21655,21656],{}," String handleError() {\n",[53,21658,21659],{"class":55,"line":242},[53,21660,21661],{}," 'error';\n",[53,21663,21664],{"class":55,"line":273},[53,21665,860],{},[53,21667,21668],{"class":55,"line":279},[53,21669,21670],{}," @RequestMapping('/prime')\n",[53,21672,21673],{"class":55,"line":496},[53,21674,21675],{}," String isPrime(@RequestParam int n) {\n",[53,21677,21678],{"class":55,"line":503},[53,21679,21680],{}," primeService.isPrime(n) ? 'prime' : 'not prime';\n",[53,21682,21683],{"class":55,"line":509},[53,21684,860],{},[53,21686,21687],{"class":55,"line":515},[53,21688,282],{},[18,21690,21691],{},"Since the application is based on Spring Boot we also add this class...",[18,21693,21694],{},[50,21695,21696],{},"src/main/java/prime/Application.java",[43,21698,21700],{"className":288,"code":21699,"language":290,"meta":48,"style":48},"\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",[50,21701,21702,21706,21711,21716,21721,21726,21731,21736,21740],{"__ignoreMap":48},[53,21703,21704],{"class":55,"line":56},[53,21705,500],{"emptyLinePlaceholder":499},[53,21707,21708],{"class":55,"line":86},[53,21709,21710],{},"@Configuration\n",[53,21712,21713],{"class":55,"line":126},[53,21714,21715],{},"@EnableAutoConfiguration\n",[53,21717,21718],{"class":55,"line":163},[53,21719,21720],{},"@ComponentScan\n",[53,21722,21723],{"class":55,"line":186},[53,21724,21725],{},"public class Application {\n",[53,21727,21728],{"class":55,"line":221},[53,21729,21730],{}," public static void main(String[] args) {\n",[53,21732,21733],{"class":55,"line":242},[53,21734,21735],{}," SpringApplication.run(Application.class, args);\n",[53,21737,21738],{"class":55,"line":273},[53,21739,7109],{},[53,21741,21742],{"class":55,"line":279},[53,21743,282],{},[18,21745,21746],{},"... and a build script:",[18,21748,21749],{},[50,21750,21751],{},"src/build.gradle",[43,21753,21755],{"className":288,"code":21754,"language":290,"meta":48,"style":48},"\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",[50,21756,21757,21761,21766,21771,21776,21780,21785,21790,21794,21798,21803,21808,21813,21818,21823,21827,21832,21837,21841,21846,21851,21856,21861,21866,21870,21875,21880],{"__ignoreMap":48},[53,21758,21759],{"class":55,"line":56},[53,21760,500],{"emptyLinePlaceholder":499},[53,21762,21763],{"class":55,"line":86},[53,21764,21765],{},"buildscript {\n",[53,21767,21768],{"class":55,"line":126},[53,21769,21770],{}," repositories {\n",[53,21772,21773],{"class":55,"line":163},[53,21774,21775],{}," mavenCentral()\n",[53,21777,21778],{"class":55,"line":186},[53,21779,7109],{},[53,21781,21782],{"class":55,"line":221},[53,21783,21784],{}," dependencies {\n",[53,21786,21787],{"class":55,"line":242},[53,21788,21789],{}," classpath(\"org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE\")\n",[53,21791,21792],{"class":55,"line":273},[53,21793,7109],{},[53,21795,21796],{"class":55,"line":279},[53,21797,282],{},[53,21799,21800],{"class":55,"line":496},[53,21801,21802],{},"apply plugin: 'groovy'\n",[53,21804,21805],{"class":55,"line":503},[53,21806,21807],{},"apply plugin: 'spring-boot'\n",[53,21809,21810],{"class":55,"line":509},[53,21811,21812],{},"jar {\n",[53,21814,21815],{"class":55,"line":515},[53,21816,21817],{}," baseName = 'prime'\n",[53,21819,21820],{"class":55,"line":521},[53,21821,21822],{}," version = '0.10.0'\n",[53,21824,21825],{"class":55,"line":527},[53,21826,282],{},[53,21828,21829],{"class":55,"line":533},[53,21830,21831],{},"repositories {\n",[53,21833,21834],{"class":55,"line":539},[53,21835,21836],{}," mavenCentral()\n",[53,21838,21839],{"class":55,"line":545},[53,21840,282],{},[53,21842,21843],{"class":55,"line":2070},[53,21844,21845],{},"dependencies {\n",[53,21847,21848],{"class":55,"line":2075},[53,21849,21850],{}," compile(\"org.codehaus.groovy:groovy-all:2.3.6\")\n",[53,21852,21853],{"class":55,"line":2081},[53,21854,21855],{}," compile(\"org.springframework.boot:spring-boot-starter-jetty\")\n",[53,21857,21858],{"class":55,"line":2087},[53,21859,21860],{}," compile(\"org.springframework.boot:spring-boot-starter-web\") {\n",[53,21862,21863],{"class":55,"line":2092},[53,21864,21865],{}," exclude module: \"spring-boot-starter-tomcat\"\n",[53,21867,21868],{"class":55,"line":2097},[53,21869,7109],{},[53,21871,21872],{"class":55,"line":2103},[53,21873,21874],{}," testCompile(\"org.springframework.boot:spring-boot-starter-test\")\n",[53,21876,21877],{"class":55,"line":2109},[53,21878,21879],{}," testCompile(\"org.spockframework:spock-core:0.7-groovy-2.0\")\n",[53,21881,21882],{"class":55,"line":2115},[53,21883,282],{},[18,21885,21886],{},"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.",[18,21888,21889,21890,21893,21894,21897,21898,21901],{},"If Groovy is used in production code we have to include the ",[27,21891,21892],{},"groovy-all"," dependency to the ",[50,21895,21896],{},"compile"," configuration,\notherwise this dependency should be added to the ",[50,21899,21900],{},"testCompile"," configuration.",[18,21903,21904],{},"Now we write unit specifications which verify the correctness of service implementation and controller:",[18,21906,21907],{},[50,21908,21909],{},"src/test/groovy/prime/service/PrimeServiceImplSpec.groovy",[43,21911,21913],{"className":288,"code":21912,"language":290,"meta":48,"style":48},"\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",[50,21914,21915,21919,21924,21929,21934,21939,21944,21949,21954,21959,21964,21969,21974,21979,21984,21989,21994,21998,22003,22008,22013,22018,22023,22028,22032],{"__ignoreMap":48},[53,21916,21917],{"class":55,"line":56},[53,21918,500],{"emptyLinePlaceholder":499},[53,21920,21921],{"class":55,"line":86},[53,21922,21923],{},"class PrimeServiceImplSpec extends Specification {\n",[53,21925,21926],{"class":55,"line":126},[53,21927,21928],{}," PrimeServiceImpl sut = new PrimeServiceImpl();\n",[53,21930,21931],{"class":55,"line":163},[53,21932,21933],{}," def \"test if the given number is prime\"() {\n",[53,21935,21936],{"class":55,"line":186},[53,21937,21938],{}," expect:\n",[53,21940,21941],{"class":55,"line":221},[53,21942,21943],{}," sut.isPrime(n) == prime\n",[53,21945,21946],{"class":55,"line":242},[53,21947,21948],{}," where:\n",[53,21950,21951],{"class":55,"line":273},[53,21952,21953],{}," n | prime\n",[53,21955,21956],{"class":55,"line":279},[53,21957,21958],{}," 0 | false\n",[53,21960,21961],{"class":55,"line":496},[53,21962,21963],{}," 1 | false\n",[53,21965,21966],{"class":55,"line":503},[53,21967,21968],{}," 2 | true\n",[53,21970,21971],{"class":55,"line":509},[53,21972,21973],{}," 3 | true\n",[53,21975,21976],{"class":55,"line":515},[53,21977,21978],{}," 4 | false\n",[53,21980,21981],{"class":55,"line":521},[53,21982,21983],{}," 5 | true\n",[53,21985,21986],{"class":55,"line":527},[53,21987,21988],{}," 6 | false\n",[53,21990,21991],{"class":55,"line":533},[53,21992,21993],{}," 7 | true\n",[53,21995,21996],{"class":55,"line":539},[53,21997,860],{},[53,21999,22000],{"class":55,"line":545},[53,22001,22002],{}," def \"check method argument constraints\"() {\n",[53,22004,22005],{"class":55,"line":2070},[53,22006,22007],{}," when:\n",[53,22009,22010],{"class":55,"line":2075},[53,22011,22012],{}," sut.isPrime(-1)\n",[53,22014,22015],{"class":55,"line":2081},[53,22016,22017],{}," then:\n",[53,22019,22020],{"class":55,"line":2087},[53,22021,22022],{}," def e = thrown(IllegalArgumentException)\n",[53,22024,22025],{"class":55,"line":2092},[53,22026,22027],{}," e.message == 'argument must not be negative'\n",[53,22029,22030],{"class":55,"line":2097},[53,22031,860],{},[53,22033,22034],{"class":55,"line":2103},[53,22035,282],{},[18,22037,22038],{},[50,22039,22040],{},"src/test/groovy/prime/web/PrimeControllerSpec.groovy",[43,22042,22044],{"className":288,"code":22043,"language":290,"meta":48,"style":48},"\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",[50,22045,22046,22050,22055,22060,22065,22070,22074,22079,22083,22088,22093,22098,22102,22107,22111],{"__ignoreMap":48},[53,22047,22048],{"class":55,"line":56},[53,22049,500],{"emptyLinePlaceholder":499},[53,22051,22052],{"class":55,"line":86},[53,22053,22054],{},"class PrimeControllerSpec extends Specification {\n",[53,22056,22057],{"class":55,"line":126},[53,22058,22059],{}," def \"returns string 'prime' when service detects prime number\"() {\n",[53,22061,22062],{"class":55,"line":163},[53,22063,22064],{}," int p = 3\n",[53,22066,22067],{"class":55,"line":186},[53,22068,22069],{}," def stub = { it == p ? true : false }\n",[53,22071,22072],{"class":55,"line":221},[53,22073,21938],{},[53,22075,22076],{"class":55,"line":242},[53,22077,22078],{}," new PrimeController(primeService: stub).isPrime(p) == 'prime'\n",[53,22080,22081],{"class":55,"line":273},[53,22082,860],{},[53,22084,22085],{"class":55,"line":279},[53,22086,22087],{}," def \"returns 'not prime' when service detects non-prime number\"() {\n",[53,22089,22090],{"class":55,"line":496},[53,22091,22092],{}," int n = 4\n",[53,22094,22095],{"class":55,"line":503},[53,22096,22097],{}," def stub = { it == n ? false : true }\n",[53,22099,22100],{"class":55,"line":509},[53,22101,21938],{},[53,22103,22104],{"class":55,"line":515},[53,22105,22106],{}," new PrimeController(primeService: stub).isPrime(n) == 'not prime'\n",[53,22108,22109],{"class":55,"line":521},[53,22110,860],{},[53,22112,22113],{"class":55,"line":527},[53,22114,282],{},[18,22116,22117,22118,22121,22122,22125,22126,22128,22129,22132,22133,986],{},"The first ",[573,22119,22120],{},"feature"," method in ",[50,22123,22124],{},"PrimeServiceImplSpec"," shows how Spocks ",[573,22127,21462],{}," concept works and in\n",[50,22130,22131],{},"PrimeControllerSpec"," we create service stubs by ",[573,22134,22135],{},"closure coercion",[18,22137,22138,22139,22141],{},"Spock does also provide a means for ",[573,22140,21465],{},", i.e. mocking and stubbing.",[18,22143,22144,22145,22148],{},"Before we implement an integration specification to verify the applications behaviour, we have to add another dependency\nin the build script. The ",[27,22146,22147],{},"spock-spring"," dependency enables to use the Spring TestContext framework together with\nSpock which is required for our integration specification.",[18,22150,22151],{},[50,22152,21751],{},[43,22154,22156],{"className":288,"code":22155,"language":290,"meta":48,"style":48},"\ntestCompile(\"org.spockframework:spock-spring:0.7-groovy-2.0\")\n\n",[50,22157,22158,22162],{"__ignoreMap":48},[53,22159,22160],{"class":55,"line":56},[53,22161,500],{"emptyLinePlaceholder":499},[53,22163,22164],{"class":55,"line":86},[53,22165,22166],{},"testCompile(\"org.spockframework:spock-spring:0.7-groovy-2.0\")\n",[18,22168,22169,22170],{},"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 ",[50,22171,22172],{},"gradle integTest",[18,22174,22175],{},[50,22176,21751],{},[43,22178,22180],{"className":288,"code":22179,"language":290,"meta":48,"style":48},"\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",[50,22181,22182,22186,22191,22196,22201,22206,22210,22214,22219,22224,22229,22233,22238,22243,22248],{"__ignoreMap":48},[53,22183,22184],{"class":55,"line":56},[53,22185,500],{"emptyLinePlaceholder":499},[53,22187,22188],{"class":55,"line":86},[53,22189,22190],{},"sourceSets {\n",[53,22192,22193],{"class":55,"line":126},[53,22194,22195],{}," integTest {\n",[53,22197,22198],{"class":55,"line":163},[53,22199,22200],{}," compileClasspath += main.output + test.output\n",[53,22202,22203],{"class":55,"line":186},[53,22204,22205],{}," runtimeClasspath += main.output + test.output\n",[53,22207,22208],{"class":55,"line":221},[53,22209,860],{},[53,22211,22212],{"class":55,"line":242},[53,22213,282],{},[53,22215,22216],{"class":55,"line":273},[53,22217,22218],{},"configurations {\n",[53,22220,22221],{"class":55,"line":279},[53,22222,22223],{}," integTestCompile.extendsFrom testCompile\n",[53,22225,22226],{"class":55,"line":496},[53,22227,22228],{}," integTestRuntime.extendsFrom testRuntime\n",[53,22230,22231],{"class":55,"line":503},[53,22232,282],{},[53,22234,22235],{"class":55,"line":509},[53,22236,22237],{},"task integTest(type: Test) {\n",[53,22239,22240],{"class":55,"line":515},[53,22241,22242],{}," testClassesDir = sourceSets.integTest.output.classesDir\n",[53,22244,22245],{"class":55,"line":521},[53,22246,22247],{}," classpath = sourceSets.integTest.runtimeClasspath\n",[53,22249,22250],{"class":55,"line":527},[53,22251,282],{},[18,22253,22254],{},[50,22255,22256],{},"src/integTest/groovy/prime/PrimeSpec.groovy",[43,22258,22260],{"className":288,"code":22259,"language":290,"meta":48,"style":48},"\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",[50,22261,22262,22266,22271,22276,22281,22286,22291,22296,22301,22305,22310,22315,22319,22324,22329,22334,22339,22343],{"__ignoreMap":48},[53,22263,22264],{"class":55,"line":56},[53,22265,500],{"emptyLinePlaceholder":499},[53,22267,22268],{"class":55,"line":86},[53,22269,22270],{},"@ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)\n",[53,22272,22273],{"class":55,"line":126},[53,22274,22275],{},"@WebAppConfiguration\n",[53,22277,22278],{"class":55,"line":163},[53,22279,22280],{},"@IntegrationTest\n",[53,22282,22283],{"class":55,"line":186},[53,22284,22285],{},"class PrimeSpec extends Specification {\n",[53,22287,22288],{"class":55,"line":221},[53,22289,22290],{}," @Value('${local.server.port}')\n",[53,22292,22293],{"class":55,"line":242},[53,22294,22295],{}," int port;\n",[53,22297,22298],{"class":55,"line":273},[53,22299,22300],{}," def \"server answers with 'prime' or 'not prime' or 'error'\"() {\n",[53,22302,22303],{"class":55,"line":279},[53,22304,21938],{},[53,22306,22307],{"class":55,"line":496},[53,22308,22309],{}," \"http://localhost:$port/prime?n=$n\"\n",[53,22311,22312],{"class":55,"line":503},[53,22313,22314],{}," .toURL().text == response\n",[53,22316,22317],{"class":55,"line":509},[53,22318,21948],{},[53,22320,22321],{"class":55,"line":515},[53,22322,22323],{}," n | response\n",[53,22325,22326],{"class":55,"line":521},[53,22327,22328],{}," 23 | 'prime'\n",[53,22330,22331],{"class":55,"line":527},[53,22332,22333],{}," 42 | 'not prime'\n",[53,22335,22336],{"class":55,"line":533},[53,22337,22338],{}," -1 | 'error'\n",[53,22340,22341],{"class":55,"line":539},[53,22342,860],{},[53,22344,22345],{"class":55,"line":545},[53,22346,282],{},[18,22348,22349,22350,22353,22354,22357],{},"In ",[50,22351,22352],{},"PrimeSpec"," the Spring Boot annotation ",[50,22355,22356],{},"@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.",[18,22359,22360],{},"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.",[18,22362,22363],{},"Links:",[18,22365,22366],{},[585,22367,22370],{"href":22368,"rel":22369},"https://code.google.com/p/spock/wiki/SpockBasics/",[589],"SpockBasics - Anatomy of a Spock specification",[18,22372,22373],{},[585,22374,22377],{"href":22375,"rel":22376},"http://spock-framework.readthedocs.org/en/latest/",[589],"Spock Framework Reference Documentation",[18,22379,22380,22385],{},[585,22381,22384],{"href":22382,"rel":22383},"http://de.slideshare.net/kousen/spock-friendly-testing",[589],"Spock: Test Well and Prosper"," by Ken Kousen",[607,22387,989],{},{"title":48,"searchDepth":86,"depth":86,"links":22389},[],[613],"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":21443,"description":21452},"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",[12893,22399,1010,22400],"spock","test","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":22404,"title":22405,"author":22406,"body":22407,"category":22511,"date":22512,"description":48,"extension":617,"link":22513,"meta":22514,"navigation":499,"path":22515,"seo":22516,"slug":22411,"stem":22517,"tags":22518,"teaser":22524,"__hash__":22525},"blog/blog/code-reviews.md","Code-Reviews",[11420],{"type":11,"value":22408,"toc":22506},[22409,22412,22418,22427,22430,22434,22437,22448,22458,22468,22471,22474,22478,22481,22484,22493,22503],[14,22410,22405],{"id":22411},"code-reviews",[649,22413,22415],{"id":22414},"zeig-mir-deinen-code-und-ich-sage-dir-wer-du-bist",[573,22416,22417],{},"Zeig’ mir Deinen Code und ich sage Dir wer Du bist.",[18,22419,22420,22421,986],{},"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 ",[585,22422,22426],{"href":22423,"rel":22424,"title":22425},"http://www.synyx.de/leistungen/code_clinic/",[589],"synyx' code clinic","Code-Clinic",[18,22428,22429],{},"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.",[649,22431,22433],{"id":22432},"vorgehensweise","Vorgehensweise",[18,22435,22436],{},"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:",[577,22438,22439,22442,22445],{},[580,22440,22441],{},"“Wo drückt der Schuh besonders?”",[580,22443,22444],{},"“Was sind Ihre Erwartungen an den Review?”",[580,22446,22447],{},"“Welche wichtigen Stakeholder sind beteiligt und was sind die Rahmenbedingungen?”",[18,22449,22450,22451,22457],{},"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 ",[585,22452,22456],{"href":22453,"rel":22454,"title":22455},"http://www.synyx.de/unternehmen/vision_mission/",[589],"Vision und Mission","Firmenphilosophie"," ein offenes und\ntransparentes Vorgehen im Gegensatz zu reinen “undercover Aktionen”.",[18,22459,22460,22461,22467],{},"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 ",[585,22462,22466],{"href":22463,"rel":22464,"title":22465},"http://www.sonarqube.org/",[589],"SonarQube zur Codeanalyse","SonarQube",")\nsinnvolle Ansatzpunkte. Nicht zuletzt lernt man mit der Zeit, Probleme und Schwachstellen im Quellcode schnell zu\nerkennen.",[18,22469,22470],{},"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.",[18,22472,22473],{},"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.",[649,22475,22477],{"id":22476},"ergebnisse","Ergebnisse",[18,22479,22480],{},"Auch wenn jedes Projekt, jedes Team und jeder Code anders ist, existieren oft sehr ähnliche Probleme.",[18,22482,22483],{},"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.",[18,22485,22486,22487,22492],{},"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 ",[585,22488,22491],{"href":22489,"rel":22490},"http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod",[589],"SOLID",") und führt letztlich zu monolithischen und\naufwändig wartbaren Anwendungen.",[18,22494,22495,22496,22502],{},"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 ",[585,22497,22501],{"href":22498,"rel":22499,"title":22500},"http://martinfowler.com/bliki/TestPyramid.html",[589],"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.",[18,22504,22505],{},"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":48,"searchDepth":86,"depth":86,"links":22507},[22508,22509,22510],{"id":22414,"depth":126,"text":22417},{"id":22432,"depth":126,"text":22433},{"id":22476,"depth":126,"text":22477},[613],"2014-08-22T10:30:06","https://synyx.de/blog/code-reviews/",{},"/blog/code-reviews",{"title":22405,"description":48},"blog/code-reviews",[50,22519,22520,22521,22522,22523,22400],"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":22527,"title":22528,"author":22529,"body":22530,"category":22688,"date":22689,"description":22539,"extension":617,"link":22690,"meta":22691,"navigation":499,"path":22692,"seo":22693,"slug":22534,"stem":22694,"tags":22695,"teaser":22697,"__hash__":22698},"blog/blog/javaforum-stuttgart-2014.md","JavaForum Stuttgart 2014",[8219],{"type":11,"value":22531,"toc":22686},[22532,22535,22540,22543,22546,22551,22554,22559,22568,22573,22576,22581,22584,22589,22604,22609,22618,22623,22626,22631,22639,22644,22647,22652,22661,22666,22675,22680,22683],[14,22533,22528],{"id":22534},"javaforum-stuttgart-2014",[18,22536,22537],{},[27,22538,22539],{},"Warum in die Ferne schweifen, wenn das Gute doch so nah ist?",[18,22541,22542],{},"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).",[18,22544,22545],{},"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.",[18,22547,22548],{},[27,22549,22550],{},"08:05 Uhr, Hauptbahnhof Stuttgart",[18,22552,22553],{},"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.",[18,22555,22556],{},[27,22557,22558],{},"8:45 Uhr, Hegel-Saal",[18,22560,22561,22562,22567],{},"Zum Warm-Up: Thorsten Maier\nmit “",[585,22563,22566],{"href":22564,"rel":22565},"http://www.java-forum-stuttgart.de/_data/D1_2014.pdf",[589],"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.",[18,22569,22570],{},[27,22571,22572],{},"9:50 Uhr, Raum Sylt",[18,22574,22575],{},"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…",[18,22577,22578],{},[27,22579,22580],{},"10:40 Uhr, Foyer",[18,22582,22583],{},"Wo gibt es die Croissants mit denen hier alle rum laufen?",[18,22585,22586],{},[27,22587,22588],{},"11:10 Uhr, Schiller-Saal",[18,22590,22591,22592,22597,22598,22603],{},"Im dritten Block geht es\num “",[585,22593,22596],{"href":22594,"rel":22595},"http://www.java-forum-stuttgart.de/_data/A3_2014.pdf",[589],"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 ",[585,22599,22602],{"href":22600,"rel":22601},"http://www.amazon.de/Enterprise-Integration-Patterns-Designing-Deploying/dp/0321200683/ref=sr_1_1?ie=UTF8",[589],"EAI Buch von Hohpe und Woolf","\nlesen, und dann die Patterns nicht selbst implementieren, sondern fertige Frameworks bzw. Tools verwenden.",[18,22605,22606],{},[27,22607,22608],{},"12:15 Uhr, Beethoven-Saal",[18,22610,22611,22612,22617],{},"Vor der Mittagspause noch was neues zu Java 8\nlernen: “",[585,22613,22616],{"href":22614,"rel":22615},"http://www.java-forum-stuttgart.de/_data/E4_2014.pdf",[589],"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.",[18,22619,22620],{},[27,22621,22622],{},"13:00 Uhr, Foyer",[18,22624,22625],{},"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.",[18,22627,22628],{},[27,22629,22630],{},"14:30 Uhr, Raum Usedom",[18,22632,1038,22633,22638],{},[585,22634,22637],{"href":22635,"rel":22636},"http://www.java-forum-stuttgart.de/_data/E5_2014.pdf",[589],"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.",[18,22640,22641],{},[27,22642,22643],{},"15:20 Uhr, Foyer",[18,22645,22646],{},"Bevor dass jemand verhungert: Zeit für lecker Kuchen!",[18,22648,22649],{},[27,22650,22651],{},"15:35 Uhr, Beethoven-Saal",[18,22653,22654,22655,22660],{},"In Zeiten sich täglich überschlagender Meldungen von geklauten Daten und Passwörter so aktuell wie\nnie: “",[585,22656,22659],{"href":22657,"rel":22658},"http://www.java-forum-stuttgart.de/_data/F6_2014.pdf",[589],"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.",[18,22662,22663],{},[27,22664,22665],{},"16:40 Uhr, Raum Usedom",[18,22667,22668,22669,22674],{},"Zum Endspurt noch einmal hinauf ins oberste Stockwerk\nzu “",[585,22670,22673],{"href":22671,"rel":22672},"http://www.java-forum-stuttgart.de/_data/A7_2014.pdf",[589],"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.",[18,22676,22677],{},[27,22678,22679],{},"17:40 Uhr, Stuttgart Innenstadt",[18,22681,22682],{},"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.",[18,22684,22685],{},"JavaForum Stuttgart – nächstes Jahr gerne wieder. Und nicht nur wegen dem Essen 😉",{"title":48,"searchDepth":86,"depth":86,"links":22687},[],[614],"2014-07-22T11:50:17","https://synyx.de/blog/javaforum-stuttgart-2014/",{},"/blog/javaforum-stuttgart-2014",{"title":22528,"description":22539},"blog/javaforum-stuttgart-2014",[3491,290,22696,4811,4231],"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":22700,"title":22701,"author":22702,"body":22703,"category":22817,"date":22818,"description":22712,"extension":617,"link":22819,"meta":22820,"navigation":499,"path":22821,"seo":22822,"slug":22707,"stem":22823,"tags":22824,"teaser":22830,"__hash__":22831},"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.",[18413],{"type":11,"value":22704,"toc":22815},[22705,22708,22713,22716,22719,22722,22725,22728,22731,22734,22739,22742,22745,22748,22751,22754,22759,22762,22769,22772,22781,22784,22791,22794,22798,22801,22812],[14,22706,22701],{"id":22707},"when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right",[18,22709,22710],{},[573,22711,22712],{},"tl;dr: When you are using Gerrit and Jenkins on the same machine, know what you’re doing!",[18,22714,22715],{},"In a recent project we decided to increase code quality by introducing Gerrit as Code Review Tool.",[18,22717,22718],{},"The configuration looks as follows:",[18,22720,22721],{},"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.",[18,22723,22724],{},"After a successful merge of the patchset another jenkins job is triggered for deployment purpose.",[18,22726,22727],{},"That job is not surprisingly configured with “ mvn clean install -U”.",[18,22729,22730],{},"Meaning Jenkins is cleaning up the working directory and building the project by using the newest snapshots and/or\nreleases.",[18,22732,22733],{},"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.",[18,22735,22736],{},[27,22737,22738],{},"So whats going on right here?",[18,22740,22741],{},"Analysing the setup we came across the usage of the “-U” Parameter of Maven. The Manual says:",[18,22743,22744],{},"`-U,--update-snapshots",[18,22746,22747],{},"Forces a check for updated releases and snapshots on remote repositories`",[18,22749,22750],{},"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.",[18,22752,22753],{},"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.",[18,22755,22756],{},[27,22757,22758],{},"So whats the solution?",[18,22760,22761],{},"There are three possibilities to overcome the flaw:",[18,22763,22764,22765,22768],{},"Of course you may use ",[27,22766,22767],{},"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.",[18,22770,22771],{},"Not only because of the costs, also the higher administrative effort might be a reason to look for other solutions.",[18,22773,22774,22775,22780],{},"Maven ships the goal dependency:* *",[585,22776,22779],{"href":22777,"rel":22778,"title":22779},"http://maven.apache.org/plugins/maven-dependency-plugin/examples/purging-local-repository.html",[589],"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.",[18,22782,22783],{},"Howerver this unfortunately removes the symptoms, but not the cause.",[18,22785,22786,22787,22790],{},"There is an other solution which is easier than you might thought. Just configure the Gerrit Jenkins job with “mvn clean\n",[27,22788,22789],{},"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.",[18,22792,22793],{},"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.",[18,22795,22796],{},[27,22797,12763],{},[18,22799,22800],{},"Let me point out the conclusion in three simple bullet points:",[577,22802,22803,22806,22809],{},[580,22804,22805],{},"Know your artifact lifecycle and its relevance as dependency",[580,22807,22808],{},"Be careful with different tools running on the same machine sharing resources",[580,22810,22811],{},"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",[18,22813,22814],{},"Did you have similar problems with that setup? Or other solutions? Don’t hesitate to comment your experience.",{"title":48,"searchDepth":86,"depth":86,"links":22816},[],[613],"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":22701,"description":22712},"blog/when-your-tooling-is-fooling-you-code-review-and-continuous-integration-with-gerrit-jenkins-done-right",[22825,22826,22827,22828,22829,10977],"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":22833,"title":22834,"author":22835,"body":22836,"category":23520,"date":23521,"description":23522,"extension":617,"link":23523,"meta":23524,"navigation":499,"path":23525,"seo":23526,"slug":22840,"stem":23528,"tags":23529,"teaser":23535,"__hash__":23536},"blog/blog/a-very-brief-history-of-the-nosql-development.md","A very brief history of the NoSQL development",[8052],{"type":11,"value":22837,"toc":23518},[22838,22841,22850,22888,22899,22919,22989,23015,23018,23033,23109,23133,23143,23169,23202,23205,23231,23251,23274,23307,23317,23323,23401,23403,23514,23516],[14,22839,22834],{"id":22840},"a-very-brief-history-of-the-nosql-development",[18,22842,22843,22844,22846,22847,22849],{},"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 ",[27,22845,18644],{},", 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 ",[27,22848,18558],{}," 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…",[18,22851,22852,22853,22855,22856,22859,22863,22864,22867,22868,22871,22872,22875,22876,22879,22880,22883,22884,22887],{},"A short story of three famous papers\nThere is a lot of research going on in the field of ",[27,22854,18644],{},". Three papers are frequently cited in this context: Codds\nworks on ",[27,22857,22858],{},"large shared databanks",[585,22860,22862],{"href":22861},"#References","[1]",", the fundamental work that describes the foundation of relational\ndatabase systems and the relational algebra as its query language. The ",[27,22865,22866],{},"proof of Brewer’s conjecture"," by Gilbert and\nLynch",[585,22869,22870],{"href":22861},"[2]",", who show that, within a reasonable network model and given some premises, CAP is a ",[573,22873,22874],{},"pick two\nout of three"," choice. And the criticism on a ",[27,22877,22878],{},"one size fits all"," philosophy by Stonebraker and Çetintemel",[585,22881,22882],{"href":22861},"[3]",", a paper stating, more or less, the end of relational databases as ",[573,22885,22886],{},"the jack of all trades devices"," for\nstorage related problems.",[18,22889,22890,22891,22894,22895,22898],{},"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 ",[27,22892,22893],{},"hierarchically ordered"," (i.e. tree structured) or ",[27,22896,22897],{},"arranged in\nnetworks"," (i.e. graph structured). Three major issues directly affect the applications that depended on the persisted\ndata in these systems:",[3525,22900,22901,22907,22913],{},[580,22902,22903,22906],{},[27,22904,22905],{},"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.",[580,22908,22909,22912],{},[27,22910,22911],{},"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.",[580,22914,22915,22918],{},[27,22916,22917],{},"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.",[18,22920,22921,22922,22925,22926,22929,22930,22933,22934,22937,22938,22941,22942,22945,22946,22949,22950,22953,22954,22957,22958,22961,22962,22965,22966,22969,22970,17019,22973,22976,22977,22980,22981,22984,22985,22988],{},"The relational data model: Separation of concerns and data consistency\nCodds basic idea to address these issues was the ",[27,22923,22924],{},"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 ",[27,22927,22928],{},"mathematical\nrelations",", structures that are, basically, sets of (ordered) ",[27,22931,22932],{},"tuples",". A ",[27,22935,22936],{},"database state",", in the simplest sense,\nis a time-varying collection of relations. To avoid anomalies, the ",[27,22939,22940],{},"database schema"," is ",[27,22943,22944],{},"normalized",", which leads\nto a distribution of the elements that constitute a single object of the domain onto several relations. The ",[27,22947,22948],{},"relational\nalgebra",", a set of operations defined on relations, is a suitable ",[27,22951,22952],{},"query language",", and as powerful (or expressive)\nas a ",[27,22955,22956],{},"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, ",[27,22959,22960],{},"SQL",", a language based on the relational algebra, is the standard query language for relational database\nsystems.\nRelational database systems offer the concept of ",[27,22963,22964],{},"referential integrity",", mechanisms to add ",[27,22967,22968],{},"semantics"," to the model\nby using ",[27,22971,22972],{},"keys",[27,22974,22975],{},"foreign key relationships"," to ensure ",[27,22978,22979],{},"data consistency",". Concurrent access to a database is\nadministered by using ",[27,22982,22983],{},"transactions",". The famous ",[27,22986,22987],{},"ACID paradigm"," is part of (almost) all relational database systems\nand guarantees transaction to be",[577,22990,22991,22997,23003,23009],{},[580,22992,22993,22996],{},[27,22994,22995],{},"A","tomic (all operations of a transaction are executed, or none is)",[580,22998,22999,23002],{},[27,23000,23001],{},"C","onsistent (a transaction begins with a consistent state, and leaves the database in a consistent state)",[580,23004,23005,23008],{},[27,23006,23007],{},"I","solated (a transaction is executed as if it were the only transaction in the system)",[580,23010,23011,23014],{},[27,23012,23013],{},"D","urable (all changes of a transaction will be persisted to the storage system)",[18,23016,23017],{},"These features are tunable to some extend, leading to different isolation and consistency concepts. This is an important\npoint, not only in distributed systems.",[18,23019,23020,23021,23024,23025,23028,23029,23032],{},"Performance issues\nThere are some issues to be discussed when it comes to ",[27,23022,23023],{},"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 ",[27,23026,23027],{},"order of the operations"," in a SQL statement can significantly affect the\nperformance. Equivalent reformulations of complex queries can immensely decrease response times. The ",[27,23030,23031],{},"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.",[18,23034,23035,23036,23039,23040,23043,23044,23047,23048,23051,23052,23055,23056,23059,23060,23062,23063,23066,23067,23070,23071,23074,23075,23078,23079,23082,23083,23086,23087,23090,23091,7040,23094,23097,23098,23100,23101,23104,23105,23108],{},"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 ",[27,23037,23038],{},"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 ",[27,23041,23042],{},"CAP theorem"," as stated as a conjecture by Brewer at PODC 2000",[585,23045,23046],{"href":22861},"[4]",": A\ndistributed systems cannot be (at the same time!!) ",[27,23049,23050],{},"consistent"," with all its data, ",[27,23053,23054],{},"available"," to serve all requests\nand ",[27,23057,23058],{},"tolerant to network partitioning",". The conjecture has been formally proven by Gilbert and Lynch in 2002 within a\nreasonable network model",[585,23061,22882],{"href":22861},". Hence: There is no free lunch with distributed data.",[585,23064,23065],{"href":22861},"[5]"," In\nCAP, the term ",[27,23068,23069],{},"consistency"," of a distributed system refers to a consistent view of all nodes in the system on the data\nthat is stored. ",[27,23072,23073],{},"Availability"," means that any request will get a response as long as there is no total network\nfailure. And ",[27,23076,23077],{},"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 ",[27,23080,23081],{},"P"," is often a premise, and the system can be\nchosen to be ",[27,23084,23085],{},"AP"," or ",[27,23088,23089],{},"CP",". Data consistency becomes an issue, and often ",[27,23092,23093],{},"BASE",[27,23095,23096],{},"B","asically ",[27,23099,22995],{},"vailable, soft\n",[27,23102,23103],{},"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",[585,23106,23107],{"href":22861},"[6]"," as well as by others.",[18,23110,23111,23112,23115,23116,23119,23120,23122,23123,23126,23127,23129,23130,23132],{},"One size does not fit all\nOver time, relational database systems with SQL as query language became the ",[27,23113,23114],{},"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: ",[27,23117,23118],{},"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",[585,23121,22882],{"href":22861},", 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 ",[573,23124,23125],{},"universal purpose","\nrelational database system by orders of magnitude, and state that ",[573,23128,22878],{}," can only be maintained as a\nmarketing illusion",[585,23131,22882],{"href":22861},". The paper and its sequel give several examples.",[18,23134,23135,23136,23139,23140,23142],{},"The rise of NoSQL\nAt this point NoSQL comes into play, by offering datastores optimized for ",[573,23137,23138],{},"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",[585,23141,22882],{"href":22861},", according to the properties they\noffer.",[18,23144,23145,23146,23149,23150,23152,23153,23156,23157,23160,23161,23164,23165,23168],{},"But: What exactly is NoSQL?\nIt is agreed on that NoSQL stands for ",[27,23147,23148],{},"not only SQL",". But there is still no common understanding about the concepts\nincluded into the term ",[27,23151,18644],{},". NoSQL stores are ",[27,23154,23155],{},"often"," schema-less, open source, non-relational, horizontally\nscalable, and use BASE as consistency mode. The term ",[27,23158,23159],{},"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, ",[27,23162,23163],{},"sometimes"," realized\natomically). Modelling with NoSQL datastores ",[27,23166,23167],{},"feels"," totally different than modelling in the the relational world, and\nfollows a different philosophy.",[18,23170,23171,23172,23175,23176,23178,23179,23181,23182,23185,23186,23189,23190,23193,23194,23197,23198,23201],{},"Common misunderstandings\nThe NoSQL world is still lacking a commonly accepted terminology, and there are frequent misunderstandings: Relational\ndatabases gained their name from ",[27,23173,23174],{},"mathematical relations",", not from relationships between data tables implemented by\nforeign keys and referential integrity. The meaning of ",[27,23177,23001],{},", i.e. the idea of ",[27,23180,22979],{},", differs in ACID and\nCAP by refering to referential integrity or data being the same on different nodes. A ",[27,23183,23184],{},"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. ",[27,23187,23188],{},"ACID"," and NoSQL are not inherently mutual exclusive (see e.g. the graph database Neo4j",[585,23191,23192],{"href":22861},"[8]",", or the approach FoundationDB",[585,23195,23196],{"href":22861},"[9]"," has taken). And the terms ",[27,23199,23200],{},"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.",[18,23203,23204],{},"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.",[18,23206,23207,23208,23211,23212,23214,23215,23218,23219,23222,23223,23226,23227,23230],{},"The graph database Neo4j\n",[27,23209,23210],{},"Neo4j",", developed and maintained by Neotechnology",[585,23213,23192],{"href":22861},", is the most famous ",[27,23216,23217],{},"graph database"," to date.\nNeo4j is on the ",[27,23220,23221],{},"CA"," side of the CAP triangle",[585,23224,23225],{"href":22861},"[7]",". 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 ",[27,23228,23229],{},"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:",[43,23232,23234],{"className":13667,"code":23233,"language":13669,"meta":48,"style":48},"START movie = node:\u003Cstrong>Movies\u003C/strong>(“title:Matrix”)\nMATCH movie\u003C-[:ACTS_IN]-actor\nRETURN actor.name;\n\n",[50,23235,23236,23241,23246],{"__ignoreMap":48},[53,23237,23238],{"class":55,"line":56},[53,23239,23240],{},"START movie = node:\u003Cstrong>Movies\u003C/strong>(“title:Matrix”)\n",[53,23242,23243],{"class":55,"line":86},[53,23244,23245],{},"MATCH movie\u003C-[:ACTS_IN]-actor\n",[53,23247,23248],{"class":55,"line":126},[53,23249,23250],{},"RETURN actor.name;\n",[18,23252,23253,23254,23257,23258,23262,23263,23265,23266,23269,23270,23273],{},"The first line of the statement reveals a direct dependency on an index called ",[573,23255,23256],{},"Movies"," (\ncompare ",[585,23259,23261],{"href":23260},"#CoddCritics","Codds issue"," no. 2.). Since version 2.0 of Neo4j this ",[573,23264,11335],{}," has been resolved. But how *\n_powerful** and ",[27,23267,23268],{},"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",[585,23271,23272],{"href":22861},"[10]",") 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.",[18,23275,23276,23277,23280,23283,23284,23287,23288,23290,23291,23294,23295,23298,23299,23302,23303,23306],{},"The wide column store Cassandra\n",[27,23278,23279],{},"Apache Cassandra",[585,23281,23282],{"href":22861},"[11]"," is a famous ",[27,23285,23286],{},"wide column store",", suited to hold tons of data and resides on\nthe AP side of the CAP triangle",[585,23289,23225],{"href":22861},". 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 ",[27,23292,23293],{},"single column family",", and hence, referential integrity is considered\na non-issue here. And the data modelling methodology is equally ",[27,23296,23297],{},"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 ",[27,23300,23301],{},"different goal",". In addition, Cassandra is shipped with the\nCassandra Query Language ",[27,23304,23305],{},"CQL",", that is in many ways similar to SQL:",[43,23308,23310],{"className":13667,"code":23309,"language":13669,"meta":48,"style":48},"SELECT name FROM employees WHERE department = 'Marketing';\n\n",[50,23311,23312],{"__ignoreMap":48},[53,23313,23314],{"class":55,"line":56},[53,23315,23316],{},"SELECT name FROM employees WHERE department = 'Marketing';\n",[18,23318,23319,23320,23322],{},"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 ",[585,23321,23261],{"href":23260}," 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.",[18,23324,23325,23326,23329,23330,23333,23334,23337,23338,23341,23342,23345,23346,23349,23350,23353,23354,23357,23358,23361,23362,23365,23366,23369,23370,23373,23374,23377,23378,23381,23382,23385,23386,23086,23389,23392,23393,23396,23397,23400],{},"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",[1773,23327],{"alt":23328,"src":22861},"[12]"," 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 ",[573,23331,23332],{},"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 ",[27,23335,23336],{},"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 ",[27,23339,23340],{},"revolutionary"," in the circumstances they were invented in. Looking back\nand learning seems to be the key to ",[27,23343,23344],{},"self-healing data"," and CRDTs as described by Shapiro et al",[585,23347,23348],{"href":22861},"[13]",",\nusing long known ideas from Lamports works",[585,23351,23352],{"href":22861},"[14]"," 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 ",[573,23355,23356],{},"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 ",[573,23359,23360],{},"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 ",[27,23363,23364],{},"honest solutions"," (as nicely pointed out in a FoundationDB blogpost",[585,23367,23368],{"href":22861},"[15]","), and that\nwould drastically simplify the choice of the correct tool from the more than 150 existing NoSQL datastores",[585,23371,23372],{"href":22861},"[16]",".\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 ",[573,23375,23376],{},"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 ",[573,23379,23380],{},"pre-relational\ntime","? Supposedly, we will experience, at least to some extent, a ",[573,23383,23384],{},"reinvention of the wheel"," at certain points, and\ndoing this ",[27,23387,23388],{},"knowingly",[27,23390,23391],{},"unknowingly"," could be the question to ask! Honest database solutions are needed. If you\nknow your ",[27,23394,23395],{},"drawbacks",", do not hide them. And point out ",[27,23398,23399],{},"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!",[18,23402,12540],{},[577,23404,23405,23410,23415,23420,23425,23430,23435,23444,23453,23462,23467,23476,23482,23488,23494,23504],{},[580,23406,23407,23409],{},[53,23408,7598],{}," E.F. Codd: A Relational Model of Data for Large Shared Data Banks, Communications of the ACM, Vol. 13:6, 1970.",[580,23411,23412,23414],{},[53,23413,7607],{}," 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.",[580,23416,23417,23419],{},[53,23418,7615],{}," 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.",[580,23421,23422,23424],{},[53,23423,7633],{}," E. Brewer: Towards Robust Distributed Systems, Keynote at PODC, 2000.",[580,23426,23427,23429],{},[53,23428,21156],{}," 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.",[580,23431,23432,23434],{},[53,23433,21173],{}," E. Brewer: CAP Twelve Years Later: How the “Rules” Have Changed, Computer, IEEE Computer Society, 2012.",[580,23436,23437,3566,23439],{},[53,23438,21191],{},[585,23440,23443],{"href":23441,"rel":23442},"http://blog.nahurst.com/visual-guide-to-nosql-systems",[589],"Nathan Hurst’s blog",[580,23445,23446,3566,23448],{},[53,23447,21208],{},[585,23449,23452],{"href":23450,"rel":23451},"http://www.neotechnology.com/",[589],"Neotechnology",[580,23454,23455,3566,23457],{},[53,23456,21314],{},[585,23458,23461],{"href":23459,"rel":23460},"https://www.foundationdb.org/",[589],"FoundationDB",[580,23463,23464,23466],{},[53,23465,21326],{}," M. A. Rodriguez and P. Neubauer: A Path Algebra for Multi-Relational Graphs. CoRR, 2010.",[580,23468,23469,3566,23472],{},[53,23470,23471],{},"11",[585,23473,23279],{"href":23474,"rel":23475},"http://cassandra.apache.org/",[589],[580,23477,23478,23481],{},[53,23479,23480],{},"12"," E. Redmond and J.R. Wilson: Seven databases in seven weeks: A Guide to Modern Databases and the NoSQL Movement,\nPragmatic Bookshelf, 2012.",[580,23483,23484,23487],{},[53,23485,23486],{},"13"," Shapiro et al: A comprehensive study of Convergent and Commutative Replicated Data Types. INRIA, RR-7506, 2011.",[580,23489,23490,23493],{},[53,23491,23492],{},"14"," L. Lamport: Time, clocks, and the ordering of events in a distributed system. Comm ACM 21, 7, pp 558-565, 1978.",[580,23495,23496,3566,23499],{},[53,23497,23498],{},"15",[585,23500,23503],{"href":23501,"rel":23502},"https://web.archive.org/web/20150526180306/http://blog.foundationdb.com:80/on-lowered-expectations-transactions-scaling-and-honesty",[589],"FoundationDB blog",[580,23505,23506,3566,23509],{},[53,23507,23508],{},"16",[585,23510,23513],{"href":23511,"rel":23512},"http://nosql-database.org/",[589],"nosql-database.org",[16670,23515],{},[607,23517,989],{},{"title":48,"searchDepth":86,"depth":86,"links":23519},[],[613],"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":22834,"description":23527},"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",[23530,23531,23532,23533,18783,18785,20764,23534],"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…","E4VQmp41A7N7DFb9IGkrEz5RxgxQ2mEmLeWZnOHb5bA",{"id":23538,"title":23539,"author":23540,"body":23541,"category":23737,"date":23738,"description":23739,"extension":617,"link":23740,"meta":23741,"navigation":499,"path":23742,"seo":23743,"slug":23545,"stem":23745,"tags":23746,"teaser":23749,"__hash__":23750},"blog/blog/nosql-still-matters.md","NoSQL still matters",[8052],{"type":11,"value":23542,"toc":23735},[23543,23546,23559,23570,23578,23581,23593,23617,23622,23638,23647,23670,23675,23682,23689,23712],[14,23544,23539],{"id":23545},"nosql-still-matters",[18,23547,23548,23549,23551,23552,23555,23558],{},"Vom 28. April bis zum 30. April fand die",[573,23550,18558],{}," in Köln statt. Austragungsort war das ",[27,23553,23554],{},"KOMED im MediaPark,",[27,23556,23557],{},"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.",[18,23560,23561,3566,23564,3566,23567],{},[27,23562,23563],{},"Was",[27,23565,23566],{},"gehört zu",[27,23568,23569],{},"NoSQL?",[18,23571,23572,23574,23575,23577],{},[27,23573,18644],{}," 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",[573,23576,18644],{}," 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.",[18,23579,23580],{},"Diskussionen über die folgenden drei Punkte sind mir in Köln wiederholt aufgefallen, die mir nicht immer gut und\neinheitlich verstanden schienen.",[18,23582,23583,23586,23587,3566,23590],{},[27,23584,23585],{},"Namensgebung r**","elationale*",[27,23588,23589],{},"*r",[27,23591,23592],{},"Datenbanksysteme",[18,23594,23595,23596,23599,23600,15970,23603,23606,23607,23609,23610,3566,23613,23616],{},"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 ",[27,23597,23598],{},"relationale Algebra",", bzw. äquivalente Kalküle wie der ",[27,23601,23602],{},"TRC",[27,23604,23605],{},"DRC",", sind die\nGrundlage für relationale Anfragesprachen (und damit letztlich auch für ",[27,23608,22960],{},"). Dieser Relationsbegriff hat erst\neinmal nichts zu tun mit referentieller Integrität, d.h. der ",[573,23611,23612],{},"inneren",[573,23614,23615],{},"Konsistenz"," der in einer relationalen Datenbank\ngespeicherten Tupel.",[18,23618,23619],{},[27,23620,23621],{},"Konsistenz: Das C in ACID und CAP",[18,23623,23624,23625,23627,23628,1628,23630,23633,23634,23637],{},"Der Buchstabe ",[27,23626,23001],{}," in den Akronymen ",[27,23629,23188],{},[27,23631,23632],{},"CAP"," steht beide Male für Konsistenz (engl. ",[27,23635,23636],{},"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.)",[18,23639,23640,23643,23644],{},[27,23641,23642],{},"Vielleicht konsistent**","e Systeme*",[27,23645,23646],{},"*?",[18,23648,23649,23650,23653,23654,23097,23656,23658,23659,23661,23662,23665,23666,23669],{},"In der NoSQL Welt wird häufig der Begriff der ",[27,23651,23652],{},"eventual consiste**","ncy** angetroffen (u.a. als Bestandteil des\nAkronyms BASE: ",[27,23655,23096],{},[27,23657,22995],{},"vailable, ",[27,23660,23103],{},"oft state, **E**ventual consistency). Das englische Wort ",[573,23663,23664],{},"eventual","\nist nicht mit dem deutschen Wort ",[573,23667,23668],{},"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.",[18,23671,23672],{},[27,23673,23674],{},"Diskussion",[18,23676,23677,23678,23681],{},"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 ",[573,23679,23680],{},"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!",[18,23683,23684,3566,23687],{},[27,23685,23686],{},"Ausgewählte",[27,23688,16440],{},[18,23690,23691,3566,23694,3566,23697,3566,23700,23707,23708,23711],{},[27,23692,23693],{},"Es gibt eine Vielzahl lesenswerter Artikel und",[27,23695,23696],{},"Bücher",[27,23698,23699],{},"zu den oben diskutierten Themen. Eine",[27,23701,23702,23703,23706],{},"nützliche\n** ",[27,23704,23705],{},"(**","wenn auch subjektive*","*)** ",[27,23709,23710],{},"Auswahl findet sich nachstehend. Auch d","ie in diesem Blog geäußerten\nGedanken und Ausführungen folgen teilweise diese Büchern und Artikeln.",[577,23713,23714,23717,23720,23723,23726,23729,23732],{},[580,23715,23716],{},"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",[580,23718,23719],{},"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",[580,23721,23722],{},"Eric Brewer, “CAP Twelve Years Later: How the “Rules” Have Changed,” Computer, vol. 45, no. 2, pp. 23-29, 2012",[580,23724,23725],{},"Seth Gilbert and Nancy A. Lynch. Perspectives on the CAP Theorem. Computer, vol. 45, no. 2, pp. 30-36, 2012",[580,23727,23728],{},"Pramod J. Sadalage and Martin Fowler. NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence.\nAddison-Wesley Professional. 2012",[580,23730,23731],{},"Eric Redmond and Jim R. Wilson. Seven Databases in Seven Weeks. Pragmatic Bookshelf. 2012",[580,23733,23734],{},"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":48,"searchDepth":86,"depth":86,"links":23736},[],[613],"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":23539,"description":23744},"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",[23530,23531,18785,23747,23748],"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":23752,"title":23753,"author":23754,"body":23755,"category":24022,"date":24023,"description":24024,"extension":617,"link":24025,"meta":24026,"navigation":499,"path":24027,"seo":24028,"slug":23759,"stem":24030,"tags":24031,"teaser":24036,"__hash__":24037},"blog/blog/synyx-berlin-expert-days-2014.md","synyx @ Berlin Expert Days 2014",[9507],{"type":11,"value":23756,"toc":24015},[23757,23760,23768,23770,23773,23777,23789,23792,23803,23806,23814,23818,23842,23845,23851,23855,23870,23911,23917,23921,23980,23984,23993,23996,24000,24009,24012],[14,23758,23753],{"id":23759},"synyx-berlin-expert-days-2014",[18,23761,23762,23763,23767],{},"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 ",[585,23764,19000],{"href":23765,"rel":23766},"http://bed-con.org/",[589],", 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.",[14,23769,9213],{"id":9212},[18,23771,23772],{},"The following talks were my (highly subjective) Top 3 of the conference:",[649,23774,23776],{"id":23775},"_1-eberhard-wolff-death-to-java-app-servers","1. Eberhard Wolff – Death to Java App Servers!",[18,23778,23779,23782,23783,23788],{},[585,23780,3410],{"href":3408,"rel":23781},[589]," started his talk with the provoking thesis\nthat ",[585,23784,23787],{"href":23785,"rel":23786},"http://www.slideshare.net/ewolff/java-application-servers-are-dead",[589],"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 :-(.",[18,23790,23791],{},"His key points among others were:",[577,23793,23794,23797,23800],{},[580,23795,23796],{},"In reality there is rarely such a thing as multiple applications running on one app server. The reason are numerous\nisolation issues",[580,23798,23799],{},"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",[580,23801,23802],{},"App servers lead to an unnecessary complex infrastructure with high turnaround and deployment costs, making continuous\nintegration and delivery really hard",[18,23804,23805],{},"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.",[649,23807,23809],{"id":23808},"bedconewolff",[585,23810,23813],{"href":23811,"rel":23812},"https://media.synyx.de/uploads//2014/04/BedCon.jpeg",[589],"BedConEWolff",[649,23815,23817],{"id":23816},"_2-jochen-mader-vertx-for-world-domination","2. Jochen Mader – VERT.X for World Domination",[18,23819,23820,23825,23826,23831,23832,23835,23836,23841],{},[585,23821,23824],{"href":23822,"rel":23823},"https://twitter.com/codepitbull",[589],"Jochen Mader’s"," talk about the distributed JVM platform ",[585,23827,23830],{"href":23828,"rel":23829},"http://vertx.io/",[589],"VERT.X"," was\nhighly informative and entertaining. While talking with incredible speed and a stylish outfit (wearing Star Trek\nt-shirt ",[27,23833,23834],{},"and"," socks) he presented\nhis ",[585,23837,23840],{"href":23838,"rel":23839},"http://www.slideshare.net/codepitbull/vertx-for-worlddomination",[589],"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.",[18,23843,23844],{},"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.",[18,23846,23847],{},[1773,23848],{"alt":23849,"src":23850},"\"worlddomination\"","https://media.synyx.de/uploads//2014/04/worlddomination.jpg",[649,23852,23854],{"id":23853},"_3-michael-plöd-caching-in-business-applications","3. Michael Plöd – Caching in Business Applications",[18,23856,23857,23858,23863,23864,23869],{},"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 ",[585,23859,23862],{"href":23860,"rel":23861},"https://twitter.com/bitboss",[589],"Michael Plöd’s"," talk,\nwho ",[585,23865,23868],{"href":23866,"rel":23867},"http://de.slideshare.net/mploed/caching-fur-business-anwendungen-deutsch",[589],"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:",[577,23871,23872,23875,23878,23881,23884,23887,23890,23893,23896,23899,23902,23905,23908],{},[580,23873,23874],{},"Identify appropriate layers for caching",[580,23876,23877],{},"Stay local as long as possible",[580,23879,23880],{},"Prefer invalidation over replication",[580,23882,23883],{},"Avoid large heap sizes only for caching",[580,23885,23886],{},"Consider a distributed cache for very large amounts of data",[580,23888,23889],{},"The Op-Guy is your best friend!",[580,23891,23892],{},"Only cache data that is suited for caching (read-mostly, expensive to get)",[580,23894,23895],{},"Use proved and tested cache implementations and NEVER EVER write your own implementation",[580,23897,23898],{},"Introduce caches in three steps (optimize the application itself → introduce local cache → introduce distributed\ncache)",[580,23900,23901],{},"Optimize serialization of objects",[580,23903,23904],{},"Abstract your cache provider",[580,23906,23907],{},"Store often used data as near to your application as possible",[580,23909,23910],{},"Use off-heap storage for cache instances of 4GB or more",[18,23912,23913],{},[1773,23914],{"alt":23915,"src":23916},"\"caching\"","https://media.synyx.de/uploads//2014/04/caching.jpg",[649,23918,23920],{"id":23919},"_4-n-many-many-more","4.-n. Many, many more",[18,23922,23923,23924,23929,23930,23935,23936,23941,23942,17019,23947,23952,23953,99,23958,23963,23964,23969,23970,23974,23975,986],{},"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 ",[585,23925,23928],{"href":23926,"rel":23927},"https://twitter.com/fhopf",[589],"Florian Hopf"," who gave a\nnice ",[585,23931,23934],{"href":23932,"rel":23933},"https://speakerdeck.com/exensio/search-driven-applications",[589],"overview on the state of search driven applications","\ntogether with his colleague ",[585,23937,23940],{"href":23938,"rel":23939},"https://twitter.com/tokraft",[589],"Tobias Kraft",". They demonstrated that it is worthier than ever\ntaking a look on Solr and ElasticSearch to implement your queries. Also definitely worth mentioning\nare ",[585,23943,23946],{"href":23944,"rel":23945},"https://twitter.com/stilkov",[589],"Stefan Tilkov",[585,23948,23951],{"href":23949,"rel":23950},"https://twitter.com/Eigenbrodtm",[589],"Martin Eigenbrodt"," with their\ndemonstration and evaluation\nof ",[585,23954,23957],{"href":23955,"rel":23956},"https://speakerdeck.com/stilkov/restful-http-on-the-jvm",[589],"REST HTTP implementations in different JVM languages",[585,23959,23962],{"href":23960,"rel":23961},"https://twitter.com/StefanZoerner",[589],"Stefan Zörner","\nwith a useful categorization\nof ",[585,23965,23968],{"href":23966,"rel":23967},"http://www.embarc.de/vortrag-berlin-expert-days-2014-verunfallte-softwarearchitektur/",[589],"software architecture and different ways to evaluate your architecture",",\nand ",[585,23971,19312],{"href":23972,"rel":23973},"https://twitter.com/timmo_gierke",[589]," with a\ninsightful ",[585,23976,23979],{"href":23977,"rel":23978},"https://speakerdeck.com/timmo/micro-services-die-verheissungen-konnten-eintreten",[589],"practical overview on micro services",[14,23981,23983],{"id":23982},"the-trend","The Trend",[18,23985,23986,23987,23992],{},"The clear winner in the category “trending buzzword” was ",[27,23988,23989],{},[573,23990,23991],{},"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.",[18,23994,23995],{},"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!",[14,23997,23999],{"id":23998},"time-off","Time Off",[18,24001,24002,24003,24008],{},"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 “",[585,24004,24007],{"href":24005,"rel":24006},"http://en.wikipedia.org/wiki/Shandy#Diesel",[589],"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”.",[18,24010,24011],{},"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.",[18,24013,24014],{},"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":48,"searchDepth":86,"depth":86,"links":24016},[24017,24018,24019,24020,24021],{"id":23775,"depth":126,"text":23776},{"id":23808,"depth":126,"text":23813},{"id":23816,"depth":126,"text":23817},{"id":23853,"depth":126,"text":23854},{"id":23919,"depth":126,"text":23920},[613],"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":23753,"description":24029},"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",[24032,9500,8320,24033,3491,24034,24035],"application-servers","caching","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…","N0aO9hcqDcPBr8hnNgL5-vK3OcSKPXQY-v36liC2VVQ",{"id":24039,"title":24040,"author":24041,"body":24042,"category":24124,"date":24125,"description":24126,"extension":617,"link":24127,"meta":24128,"navigation":499,"path":24129,"seo":24130,"slug":24131,"stem":24132,"tags":24133,"teaser":24136,"__hash__":24137},"blog/blog/devcamp_karlsruhe.md","DevCamp Karlsruhe: viele nette Leute, interessante Vorträge und jede Menge Spaß",[8052,11619],{"type":11,"value":24043,"toc":24122},[24044,24047,24050,24056,24066,24069,24072,24078,24081,24089,24094,24101,24104,24109,24112,24115],[14,24045,24040],{"id":24046},"devcamp-karlsruhe-viele-nette-leute-interessante-vorträge-und-jede-menge-spaß",[18,24048,24049],{},"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.",[18,24051,24052],{},[1773,24053],{"alt":24054,"src":24055},"\"sessionplanung\"","https://media.synyx.de/uploads//2014/03/sessionplanung.jpg",[18,24057,24058,24059,24065],{},"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 ",[585,24060,24064],{"href":24061,"rel":24062,"title":24063},"https://www.devoxx4kids.de/",[589],"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.",[18,24067,24068],{},"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.",[18,24070,24071],{},"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.",[18,24073,24074],{},[1773,24075],{"alt":24076,"src":24077},"\"devcamp\"","https://media.synyx.de/uploads//2014/03/devcamp.jpg",[18,24079,24080],{},"Wie schon erwähnt, war mein Kollege Christian Mennerich auch dabei. Er hat ebenfalls eine kleine Zusammenfassung zum\nDevCamp geschrieben. Hier folgt sie:",[18,24082,24083,24084,24088],{},"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: ",[585,24085,24086],{"href":24086,"rel":24087},"http://lanyrd.com/2014/dcka/schedule/",[589],". 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.",[18,24090,24091],{},[27,24092,24093],{},"Der Samstag",[18,24095,24096,24097,986],{},"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: ",[585,24098,24099],{"href":24099,"rel":24100},"https://speakerdeck.com/elasticsearch/using-elasticsearch-logstash-and-kibana-to-create-realtime-dashboards",[589],[18,24102,24103],{},"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.",[18,24105,24106],{},[27,24107,24108],{},"Der Sonntag",[18,24110,24111],{},"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.",[18,24113,24114],{},"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.",[18,24116,24117,24118,986],{},"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: ",[585,24119,24120],{"href":24120,"rel":24121},"https://github.com/crowddining/einkaufszettel",[589],{"title":48,"searchDepth":86,"depth":86,"links":24123},[],[614],"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":24040,"description":24049},"devcamp_karlsruhe","blog/devcamp_karlsruhe",[24134,24135],"devcamp","karlsruhe","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…","u_Fm6yEJ3gJgMv-J5-wcioHVYu0pEkGP1k0z0MHcoes",{"id":24139,"title":24140,"author":24141,"body":24142,"category":24213,"date":24214,"description":24215,"extension":617,"link":24216,"meta":24217,"navigation":499,"path":24218,"seo":24219,"slug":24221,"stem":24222,"tags":24223,"teaser":24227,"__hash__":24228},"blog/blog/fairantwortung-ubernehmen-ist-nicht-nur-ein-lippenbekenntnis.md","FAIRantwortung übernehmen ist nicht nur ein Lippenbekenntnis",[11619],{"type":11,"value":24143,"toc":24211},[24144,24147,24157,24160,24163,24166,24169,24172,24189,24198,24208],[14,24145,24140],{"id":24146},"fairantwortung-übernehmen-ist-nicht-nur-ein-lippenbekenntnis",[18,24148,24149,24150,24156],{},"Am 22.01.14 fand im IHK Bildungszentrum das\nerste ",[585,24151,24155],{"href":24152,"rel":24153,"title":24154},"http://www.fairantwortung.net/",[589],"Fairantwortung","Karlsruher Unternehmerforum"," statt. Thema war: Verantwortung\nübernehmen, Flagge zeigen, Zukunft sichern.",[18,24158,24159],{},"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.",[18,24161,24162],{},"Ich möchte hier gar nicht lange über die Vorträge oder Impulse eingehen, das würde den Rahmen sprengen.",[18,24164,24165],{},"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.",[18,24167,24168],{},"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.",[18,24170,24171],{},"Fazit: Es war eine gelungene Veranstaltung, welche mit vielen Impulsen anregen konnte.",[18,24173,24174,24175,24181,24182,24188],{},"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 ",[585,24176,24180],{"href":24177,"rel":24178,"title":24179},"http://www.cafe-libertad.de",[589],"Cafe Libertad","Café Libertad Kollektiv e.G","., oder unser Wasser\nvon ",[585,24183,24187],{"href":24184,"rel":24185,"title":24186},"http://www.vivaconagua.org/",[589],"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.",[18,24190,24191,24192,24197],{},"Ergänzend stehen die Mitarbeiter bei synyx ebenfalls im Mittelpunkt. Seit 2008 sind wir Mitglied\nbei ",[585,24193,24196],{"href":24194,"rel":24195,"title":24196},"https://web.archive.org/web/20140924063622/http://faircompany.karriere.de/",[589],"Fair Company",". Unseren\nMitarbeitern stehen jährlich 2.000 EUR Fortbildungsbudget zur Verfügung. Auch flexible Arbeitszeiten und die\nindividuelle Arbeitsplatzgestaltung gehören dazu.",[18,24199,24200,24201,24207],{},"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 ",[585,24202,24206],{"href":24203,"rel":24204,"title":24205},"http://www.synyx.de/unternehmen/verantwortung_csr/",[589],"Verantwortung","Hompage"," kann man unsere Maßnahmen\nnachlesen.",[18,24209,24210],{},"Wir sind von der Initiative FAIRantwortung begeistert. Deshalb sind wir als FAIRpeople Förderer der Initiative.",{"title":48,"searchDepth":86,"depth":86,"links":24212},[],[614],"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":24140,"description":24220},"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",[24224,24225,24226],"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":24230,"title":24231,"author":24232,"body":24233,"category":25334,"date":25335,"description":48,"extension":617,"link":25336,"meta":25337,"navigation":499,"path":25338,"seo":25339,"slug":24237,"stem":25340,"tags":25341,"teaser":25348,"__hash__":25349},"blog/blog/code-gluse.md","Code gluse",[16566],{"type":11,"value":24234,"toc":25328},[24235,24238,24241,24251,24254,24257,24260,24266,24276,24283,24287,24290,24299,24370,24376,24379,24407,24414,24466,24469,24662,24665,24668,24672,24675,24692,24698,24701,24711,24714,24717,24724,24731,24760,24763,24766,24798,24801,24804,24858,24865,24871,24874,24877,24973,24975,24979,24988,24993,25005,25008,25017,25023,25025,25048,25054,25082,25192,25195,25198,25201,25207,25210,25213,25216,25219,25325],[14,24236,24231],{"id":24237},"code-gluse",[649,24239,24231],{"id":24240},"code-gluse-1",[18,24242,24243,24244,24247,24248,24250],{},"Today’s post targets an API, which has been released on Dec. 11, 2006; the ",[50,24245,24246],{},"javax.scripting"," package ",[53,24249,7598],{}," and a lot of\ngood articles that have been written around it.",[18,24252,24253],{},"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",[18,24255,24256],{},"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",[18,24258,24259],{},"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.",[18,24261,24262,24263,24265],{},"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 ",[53,24264,7607],{}," btw.)",[18,24267,24268,24269,24272,24273,24275],{},"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. ",[27,24270,24271],{},"All\nexamples"," can be downloaded from ",[53,24274,7615],{},". 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.",[18,24277,24278,24279,24282],{},"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",[50,24280,24281],{},"bind"," function.",[649,24284,24286],{"id":24285},"proxy","Proxy",[18,24288,24289],{},"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.",[18,24291,24292,24293,24296,24297,986],{},"In Java you would probably end up using the ",[50,24294,24295],{},"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 ",[53,24298,7633],{},[43,24300,24302],{"className":6989,"code":24301,"language":6991,"meta":48,"style":48},"\"first 000 second\".replace(/[a-zA-Z]+/g, function (match) {\n return \"[\" + match.toUpperCase() + \"]\";\n});\n",[50,24303,24304,24340,24366],{"__ignoreMap":48},[53,24305,24306,24309,24311,24314,24316,24318,24321,24324,24326,24329,24331,24333,24335,24338],{"class":55,"line":56},[53,24307,24308],{"class":63},"\"first 000 second\"",[53,24310,986],{"class":82},[53,24312,24313],{"class":59},"replace",[53,24315,1067],{"class":82},[53,24317,2628],{"class":63},[53,24319,24320],{"class":89},"[a-zA-Z]",[53,24322,24323],{"class":389},"+",[53,24325,2628],{"class":63},[53,24327,24328],{"class":389},"g",[53,24330,99],{"class":82},[53,24332,5789],{"class":389},[53,24334,7040],{"class":82},[53,24336,24337],{"class":5805},"match",[53,24339,13628],{"class":82},[53,24341,24342,24344,24347,24350,24353,24356,24359,24361,24364],{"class":55,"line":86},[53,24343,13791],{"class":389},[53,24345,24346],{"class":63}," \"[\"",[53,24348,24349],{"class":389}," +",[53,24351,24352],{"class":82}," match.",[53,24354,24355],{"class":59},"toUpperCase",[53,24357,24358],{"class":82},"() ",[53,24360,24323],{"class":389},[53,24362,24363],{"class":63}," \"]\"",[53,24365,1727],{"class":82},[53,24367,24368],{"class":55,"line":126},[53,24369,7148],{"class":82},[18,24371,24372,24373,24375],{},"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 ",[53,24374,7615],{},", but for the sake of completeness:",[18,24377,24378],{},"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).",[43,24380,24382],{"className":288,"code":24381,"language":290,"meta":48,"style":48},"\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",[50,24383,24384,24388,24393,24398,24403],{"__ignoreMap":48},[53,24385,24386],{"class":55,"line":56},[53,24387,500],{"emptyLinePlaceholder":499},[53,24389,24390],{"class":55,"line":86},[53,24391,24392],{}," public interface Replacement {\n",[53,24394,24395],{"class":55,"line":126},[53,24396,24397],{}," public abstract CharSequence any (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[53,24399,24400],{"class":55,"line":163},[53,24401,24402],{}," public abstract CharSequence all (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[53,24404,24405],{"class":55,"line":186},[53,24406,860],{},[18,24408,24409,24410,24413],{},"The final Java code would look like the following (Java 8 users will flavour the new lambda syntax:\n",[50,24411,24412],{},"(match) -> { return \"[\" + match + \"]\"; }","):",[43,24415,24417],{"className":288,"code":24416,"language":290,"meta":48,"style":48},"\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",[50,24418,24419,24423,24428,24433,24438,24443,24448,24453,24457,24461],{"__ignoreMap":48},[53,24420,24421],{"class":55,"line":56},[53,24422,500],{"emptyLinePlaceholder":499},[53,24424,24425],{"class":55,"line":86},[53,24426,24427],{}," Replacement replacement;\n",[53,24429,24430],{"class":55,"line":126},[53,24431,24432],{}," replacement = replacement ();\n",[53,24434,24435],{"class":55,"line":163},[53,24436,24437],{}," CharSequence enclosed = replacement.all (Pattern.compile (\"\\\\d+\"), \"could you please enclose 1234, 789, 345 with brackets?\", new Function\u003CCharSequence, CharSequence> () {\n",[53,24439,24440],{"class":55,"line":186},[53,24441,24442],{}," @Override\n",[53,24444,24445],{"class":55,"line":221},[53,24446,24447],{}," public CharSequence apply (CharSequence sequence) {\n",[53,24449,24450],{"class":55,"line":242},[53,24451,24452],{}," return \"[\" + sequence + \"]\";\n",[53,24454,24455],{"class":55,"line":273},[53,24456,12712],{},[53,24458,24459],{"class":55,"line":279},[53,24460,7104],{},[53,24462,24463],{"class":55,"line":496},[53,24464,24465],{}," /* 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",[18,24467,24468],{},"The Javascript implementation would look like:",[43,24470,24472],{"className":6989,"code":24471,"language":6991,"meta":48,"style":48},"\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",[50,24473,24474,24478,24486,24510,24514,24518,24533,24566,24584,24589,24620,24641,24645,24657],{"__ignoreMap":48},[53,24475,24476],{"class":55,"line":56},[53,24477,500],{"emptyLinePlaceholder":499},[53,24479,24480,24482,24484],{"class":55,"line":86},[53,24481,1067],{"class":82},[53,24483,5789],{"class":389},[53,24485,13502],{"class":82},[53,24487,24488,24491,24494,24496,24499,24501,24503,24505,24508],{"class":55,"line":126},[53,24489,24490],{"class":389}," function",[53,24492,24493],{"class":59}," replace",[53,24495,7040],{"class":82},[53,24497,24498],{"class":5805},"regex",[53,24500,99],{"class":82},[53,24502,17554],{"class":5805},[53,24504,99],{"class":82},[53,24506,24507],{"class":5805},"callback",[53,24509,13628],{"class":82},[53,24511,24512],{"class":55,"line":163},[53,24513,15109],{"class":389},[53,24515,24516],{"class":55,"line":186},[53,24517,860],{"class":82},[53,24519,24520,24523,24526,24528,24530],{"class":55,"line":221},[53,24521,24522],{"class":389}," var",[53,24524,24525],{"class":59}," Replacement",[53,24527,1245],{"class":389},[53,24529,13726],{"class":389},[53,24531,24532],{"class":82}," () {};\n",[53,24534,24535,24538,24540,24543,24545,24548,24550,24552,24554,24556,24558,24560,24562,24564],{"class":55,"line":242},[53,24536,24537],{"class":89}," Replacement",[53,24539,986],{"class":82},[53,24541,24542],{"class":89},"prototype",[53,24544,986],{"class":82},[53,24546,24547],{"class":59},"any",[53,24549,1245],{"class":389},[53,24551,13726],{"class":389},[53,24553,7040],{"class":82},[53,24555,24498],{"class":5805},[53,24557,99],{"class":82},[53,24559,17554],{"class":5805},[53,24561,99],{"class":82},[53,24563,24507],{"class":5805},[53,24565,13628],{"class":82},[53,24567,24568,24571,24573,24575,24578,24581],{"class":55,"line":273},[53,24569,24570],{"class":389}," return",[53,24572,24493],{"class":59},[53,24574,7040],{"class":82},[53,24576,24577],{"class":389},"new",[53,24579,24580],{"class":59}," RegExp",[53,24582,24583],{"class":82}," (regex), content, callback);\n",[53,24585,24586],{"class":55,"line":279},[53,24587,24588],{"class":82}," };\n",[53,24590,24591,24593,24595,24597,24599,24602,24604,24606,24608,24610,24612,24614,24616,24618],{"class":55,"line":496},[53,24592,24537],{"class":89},[53,24594,986],{"class":82},[53,24596,24542],{"class":89},[53,24598,986],{"class":82},[53,24600,24601],{"class":59},"all",[53,24603,1245],{"class":389},[53,24605,13726],{"class":389},[53,24607,7040],{"class":82},[53,24609,24498],{"class":5805},[53,24611,99],{"class":82},[53,24613,17554],{"class":5805},[53,24615,99],{"class":82},[53,24617,24507],{"class":5805},[53,24619,13628],{"class":82},[53,24621,24622,24624,24626,24628,24630,24632,24635,24638],{"class":55,"line":503},[53,24623,24570],{"class":389},[53,24625,24493],{"class":59},[53,24627,7040],{"class":82},[53,24629,24577],{"class":389},[53,24631,24580],{"class":59},[53,24633,24634],{"class":82}," (regex, ",[53,24636,24637],{"class":63},"'g'",[53,24639,24640],{"class":82},"), content, callback);\n",[53,24642,24643],{"class":55,"line":509},[53,24644,24588],{"class":82},[53,24646,24647,24649,24652,24654],{"class":55,"line":515},[53,24648,13561],{"class":389},[53,24650,24651],{"class":389}," new",[53,24653,24525],{"class":59},[53,24655,24656],{"class":82}," ();\n",[53,24658,24659],{"class":55,"line":521},[53,24660,24661],{"class":82},"}) ();\n",[18,24663,24664],{},"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 🙂",[18,24666,24667],{},"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.",[649,24669,24671],{"id":24670},"modularity","Modularity",[18,24673,24674],{},"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.",[43,24676,24678],{"className":6989,"code":24677,"language":6991,"meta":48,"style":48},"require(\"org.geonames.reverse\");\n",[50,24679,24680],{"__ignoreMap":48},[53,24681,24682,24685,24687,24690],{"class":55,"line":56},[53,24683,24684],{"class":59},"require",[53,24686,1067],{"class":82},[53,24688,24689],{"class":63},"\"org.geonames.reverse\"",[53,24691,1079],{"class":82},[18,24693,24694,24695,24697],{},"The similarity to requirejs ",[53,24696,21156],{}," is intentional and you may want to extend the signature to be fully compliant, but this\nwill be left for your curiosity 🙂",[18,24699,24700],{},"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.",[18,24702,24703,24704,24706,24707,24710],{},"You could achieve this by verifying the signature of the reviewed files using a PKI ",[53,24705,21173],{}," infrastructure – ",[50,24708,24709],{},"javax.crypto","\nis your friend here and fortunately you can implement this in Java and/or use a security library to accomplish this\ntask.",[18,24712,24713],{},"Simply spoken: Always check the integrity if you provide a way for modifications.",[18,24715,24716],{},"If you are already familiar with the scripting engine API, you might have noticed that require is a function and not an\n‘object’.",[18,24718,24719,24720,24723],{},"Even when functions in Javascript are objects, there is no semantic way to say that ",[573,24721,24722],{},"“this object is a function and can\nbe invoked”"," if you share it between the environment.",[18,24725,24726,24727,24730],{},"There might be support for some engines, but not for the others and ",[50,24728,24729],{},"javax.script"," API is designed for general purpose –\ndepending on engine internal interfaces is not desired.",[43,24732,24734],{"className":6989,"code":24733,"language":6991,"meta":48,"style":48},"obj.require(\n \"org.geonames.reverse\",\n); /* nah, okay but requires additional knowledge obj. */\n",[50,24735,24736,24745,24752],{"__ignoreMap":48},[53,24737,24738,24741,24743],{"class":55,"line":56},[53,24739,24740],{"class":82},"obj.",[53,24742,24684],{"class":59},[53,24744,1139],{"class":82},[53,24746,24747,24750],{"class":55,"line":86},[53,24748,24749],{"class":63}," \"org.geonames.reverse\"",[53,24751,7143],{"class":82},[53,24753,24754,24757],{"class":55,"line":126},[53,24755,24756],{"class":82},"); ",[53,24758,24759],{"class":2530},"/* nah, okay but requires additional knowledge obj. */\n",[18,24761,24762],{},"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.",[18,24764,24765],{},"Pseudo Algorithm:",[3525,24767,24768,24786,24789,24792,24795],{},[580,24769,24770,24771,99,24774,99,24777,99,24780,24782,24783],{},"create a java function object which can load your resources from the ",[573,24772,24773],{},"classpath",[573,24775,24776],{},"internet",[573,24778,24779],{},"local filesystem",[573,24781,15622],{},"\nwith a method signature you know. (a function/SAM object) like: ",[50,24784,24785],{},"void apply (String)",[580,24787,24788],{},"create a script context and attach the object from 1. to it with a variable called ‘whatever’ (really whatever name\nyou like)",[580,24790,24791],{},"evaluate an inline require function before you evaluate your business code, which puts your require function into the\nscope of the context from 2.",[580,24793,24794],{},"evaluate your business codes which relies on the require function with the same scope from 2.",[580,24796,24797],{},"have fun",[18,24799,24800],{},"var require = function (library) { whatever.apply (library); }",[18,24802,24803],{},"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.",[43,24805,24807],{"className":6989,"code":24806,"language":6991,"meta":48,"style":48},"var require = function (library) {\n this.apply(library);\n}.bind(whatever);\ndelete whatever;\n",[50,24808,24809,24827,24840,24850],{"__ignoreMap":48},[53,24810,24811,24814,24816,24818,24820,24822,24825],{"class":55,"line":56},[53,24812,24813],{"class":389},"var",[53,24815,14710],{"class":59},[53,24817,1245],{"class":389},[53,24819,13726],{"class":389},[53,24821,7040],{"class":82},[53,24823,24824],{"class":5805},"library",[53,24826,13628],{"class":82},[53,24828,24829,24832,24834,24837],{"class":55,"line":86},[53,24830,24831],{"class":89}," this",[53,24833,986],{"class":82},[53,24835,24836],{"class":59},"apply",[53,24838,24839],{"class":82},"(library);\n",[53,24841,24842,24845,24847],{"class":55,"line":126},[53,24843,24844],{"class":82},"}.",[53,24846,24281],{"class":59},[53,24848,24849],{"class":82},"(whatever);\n",[53,24851,24852,24855],{"class":55,"line":163},[53,24853,24854],{"class":389},"delete",[53,24856,24857],{"class":82}," whatever;\n",[5221,24859,24860],{},[18,24861,24862,24863],{},"“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.” ",[53,24864,21191],{},[18,24866,24867,24868,24870],{},"If the bind function is not available by your Rhino environment, you may want to look at the implementation from\nPrototype ",[53,24869,21208],{}," et al. and add it manually with the same procedure.",[18,24872,24873],{},"We delete the ‘whatever’ object from the script context afterwards.",[18,24875,24876],{},"You need the following Java code as glue:",[43,24878,24880],{"className":288,"code":24879,"language":290,"meta":48,"style":48},"\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",[50,24881,24882,24886,24891,24896,24901,24906,24910,24915,24920,24925,24930,24935,24940,24944,24948,24953,24958,24963,24968],{"__ignoreMap":48},[53,24883,24884],{"class":55,"line":56},[53,24885,500],{"emptyLinePlaceholder":499},[53,24887,24888],{"class":55,"line":86},[53,24889,24890],{},"// internal context for variables\n",[53,24892,24893],{"class":55,"line":126},[53,24894,24895],{},"final Bindings bindings = scripting.newBindings ();\n",[53,24897,24898],{"class":55,"line":163},[53,24899,24900],{}," bindings.put (Importer, new Function\u003CString, Void> () {\n",[53,24902,24903],{"class":55,"line":186},[53,24904,24905],{}," // load a library 'argument' from the 'lib' directory\n",[53,24907,24908],{"class":55,"line":221},[53,24909,13033],{},[53,24911,24912],{"class":55,"line":242},[53,24913,24914],{}," public Void apply (String argument) {\n",[53,24916,24917],{"class":55,"line":273},[53,24918,24919],{}," // trusting returns either a valid stream object or throws an 'untrusted code' exception\n",[53,24921,24922],{"class":55,"line":279},[53,24923,24924],{}," String resource;\n",[53,24926,24927],{"class":55,"line":496},[53,24928,24929],{}," resource = String.format (\"/lib/%s.js\", argument);\n",[53,24931,24932],{"class":55,"line":503},[53,24933,24934],{}," scripting.evaluate (trusting (resource), context);\n",[53,24936,24937],{"class":55,"line":509},[53,24938,24939],{}," return null;\n",[53,24941,24942],{"class":55,"line":515},[53,24943,860],{},[53,24945,24946],{"class":55,"line":521},[53,24947,7148],{},[53,24949,24950],{"class":55,"line":527},[53,24951,24952],{},"context.setBindings (bindings, ScriptContext.ENGINE_SCOPE);\n",[53,24954,24955],{"class":55,"line":533},[53,24956,24957],{},"// add require function to the scope before the application script is loaded\n",[53,24959,24960],{"class":55,"line":539},[53,24961,24962],{},"scripting.evaluate (requirejs (Importer), context);\n",[53,24964,24965],{"class":55,"line":545},[53,24966,24967],{},"// execute the script ultimately\n",[53,24969,24970],{"class":55,"line":2070},[53,24971,24972],{},"scripting.evaluate (applicationjs (), context);\n",[18,24974,16930],{},[649,24976,24978],{"id":24977},"reporting","Reporting",[18,24980,24981,24982,24984,24985,24987],{},"In the last example I want to explain how to integrate a script engine, which is not shipped by default – JRuby ",[53,24983,21314],{},".\nThe idea behind is to embed code of ruby gems into your application, especially PDFKit ",[53,24986,21326],{}," for this example. PDFKit\ndescribes itself with",[5221,24989,24990],{},[18,24991,24992],{},"“Create PDFs using plain old HTML+CSS. Uses wkhtmltopdf on the back-end which renders HTML using Webkit.”",[18,24994,24995,24996,24999,25000,25002,25003,986],{},"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: ",[50,24997,24998],{},"Model -> HTML -> PDF",", which can be achieved using e.g. the nice Jade\n",[53,25001,23471],{}," language for the rendering process, especially Jade4j ",[53,25004,23480],{},[18,25006,25007],{},"Instead of writing the integration code for wkhtmltopdf, we will base on the work of PDFKit and write some JRuby glue.",[18,25009,25010,25011,25013,25014,25016],{},"If you need some information about ‘gem bundling’ I would recommend the articles/examples from Sieger ",[53,25012,23486],{}," and Harada\n",[53,25015,23492],{}," as a starting point.",[18,25018,25019,25020,25022],{},"You will find a local file-based repository in the project, as I wanted Maven ",[53,25021,23498],{}," to handle all the dependencies, but\nany other repository might work fine. It simply depends on your infrastructure and what suits you best.",[18,25024,24765],{},[3525,25026,25027,25030,25033,25039,25042,25045],{},[580,25028,25029],{},"put jruby-complete on your classpath, as the library ships the jsr223 implementation",[580,25031,25032],{},"put the converted pdfkit bundle on your classpath",[580,25034,25035,25036],{},"put any other needed library on your classpath ",[53,25037,25038],{},"jade4j, guava, …",[580,25040,25041],{},"write some jruby code to instantiate a configured pdfkit object",[580,25043,25044],{},"proxy the returned jruby object from 4. with a java interface",[580,25046,25047],{},"convert a jade (or differently) generated html stream to pdf using the proxy from 5.",[18,25049,25050,25051,25053],{},"I’ll show you the glue for the proxy only. Please download the project under ",[53,25052,7615],{}," if you want to see the remaining\nparts.",[43,25055,25057],{"className":288,"code":25056,"language":290,"meta":48,"style":48},"\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",[50,25058,25059,25063,25068,25073,25078],{"__ignoreMap":48},[53,25060,25061],{"class":55,"line":56},[53,25062,500],{"emptyLinePlaceholder":499},[53,25064,25065],{"class":55,"line":86},[53,25066,25067],{},"public interface Pdfy {\n",[53,25069,25070],{"class":55,"line":126},[53,25071,25072],{}," public boolean convert (InputStream streamin, OutputStream streamout);\n",[53,25074,25075],{"class":55,"line":163},[53,25076,25077],{}," public boolean convert (InputStream streamin, OutputStream streamout, Map\u003CString, String> options);\n",[53,25079,25080],{"class":55,"line":186},[53,25081,282],{},[43,25083,25087],{"className":25084,"code":25085,"language":25086,"meta":48,"style":48},"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",[50,25088,25089,25093,25098,25103,25108,25113,25118,25123,25128,25133,25138,25143,25148,25153,25158,25163,25168,25173,25178,25183,25187],{"__ignoreMap":48},[53,25090,25091],{"class":55,"line":56},[53,25092,500],{"emptyLinePlaceholder":499},[53,25094,25095],{"class":55,"line":86},[53,25096,25097],{},"class Pdfy\n",[53,25099,25100],{"class":55,"line":126},[53,25101,25102],{}," def initialize(stylesheet)\n",[53,25104,25105],{"class":55,"line":163},[53,25106,25107],{}," @stylesheet = stylesheet\n",[53,25109,25110],{"class":55,"line":186},[53,25111,25112],{}," end\n",[53,25114,25115],{"class":55,"line":221},[53,25116,25117],{}," def convert(streamin, streamout, options = {})\n",[53,25119,25120],{"class":55,"line":242},[53,25121,25122],{}," begin\n",[53,25124,25125],{"class":55,"line":273},[53,25126,25127],{}," html = streamin.to_io.read\n",[53,25129,25130],{"class":55,"line":279},[53,25131,25132],{}," kit = PDFKit.new(html, options)\n",[53,25134,25135],{"class":55,"line":496},[53,25136,25137],{}," if @stylesheet\n",[53,25139,25140],{"class":55,"line":503},[53,25141,25142],{}," kit.stylesheets \u003C\u003C @stylesheet\n",[53,25144,25145],{"class":55,"line":509},[53,25146,25147],{}," end\n",[53,25149,25150],{"class":55,"line":515},[53,25151,25152],{}," out = streamout.to_io\n",[53,25154,25155],{"class":55,"line":521},[53,25156,25157],{}," out.binmode \u003C\u003C kit.to_pdf\n",[53,25159,25160],{"class":55,"line":527},[53,25161,25162],{}," out.flush\n",[53,25164,25165],{"class":55,"line":533},[53,25166,25167],{}," rescue\n",[53,25169,25170],{"class":55,"line":539},[53,25171,25172],{}," return false\n",[53,25174,25175],{"class":55,"line":545},[53,25176,25177],{}," end\n",[53,25179,25180],{"class":55,"line":2070},[53,25181,25182],{}," true\n",[53,25184,25185],{"class":55,"line":2075},[53,25186,25112],{},[53,25188,25189],{"class":55,"line":2081},[53,25190,25191],{},"end\n",[18,25193,25194],{},"Maven will produce an assembly as zip file, which can be extracted elsewhere with a shell script for windows and *nix\nbased systems.",[18,25196,25197],{},"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.",[18,25199,25200],{},"I did not implement any special CLI handling for this prototype.",[18,25202,25203,25204,25206],{},"You need to install wkhtmltopdf ",[53,25205,23508],{}," 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).",[18,25208,25209],{},"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.",[18,25211,25212],{},"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.",[18,25214,25215],{},"The effort could be less in comparison to a full rewrite. Stick to a separation of interfaces and implementations and\nlet the environment decide.",[18,25217,25218],{},"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.",[18,25220,25221,25224,25228,25230,25234,25237,25241,25244,25248,25251,25255,25258,25262,25265,25269,25272,25276,25279,25283,25285,25289,25291,25295,25297,25301,25303,25307,25309,25313,25315,25319,25321],{},[53,25222,25223],{}," 1",[585,25225,25226],{"href":25226,"rel":25227},"http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform",[589],[53,25229,6185],{},[585,25231,25232],{"href":25232,"rel":25233},"http://stackoverflow.com/questions/11838369/where-can-i-find-a-list-of-available-jsr-223-scripting-languages",[589],[53,25235,25236],{}," 3",[585,25238,25239],{"href":25239,"rel":25240},"https://media.synyx.de/uploads//2014/01/synyx.sample.zip",[589],[53,25242,25243],{}," 4",[585,25245,25246],{"href":25246,"rel":25247},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",[589],[53,25249,25250],{}," 5",[585,25252,25253],{"href":25253,"rel":25254},"http://requirejs.org/",[589],[53,25256,25257],{}," 6",[585,25259,25260],{"href":25260,"rel":25261},"http://de.wikipedia.org/wiki/Public-Key-Infrastruktur",[589],[53,25263,25264],{}," 7",[585,25266,25267],{"href":25267,"rel":25268},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind",[589],[53,25270,25271],{}," 8",[585,25273,25274],{"href":25274,"rel":25275},"http://prototypejs.org/doc/latest/language/Function/prototype/bind/",[589],[53,25277,25278],{}," 9",[585,25280,25281],{"href":25281,"rel":25282},"http://www.jruby.org/",[589],[53,25284,21326],{},[585,25286,25287],{"href":25287,"rel":25288},"https://github.com/pdfkit/pdfkit",[589],[53,25290,23471],{},[585,25292,25293],{"href":25293,"rel":25294},"http://jade-lang.com/",[589],[53,25296,23480],{},[585,25298,25299],{"href":25299,"rel":25300},"https://github.com/neuland/jade4j",[589],[53,25302,23486],{},[585,25304,25305],{"href":25305,"rel":25306},"http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar/",[589],[53,25308,23492],{},[585,25310,25311],{"href":25311,"rel":25312},"http://yokolet.blogspot.de/2010/10/gems-in-jar-with-redbridge.html",[589],[53,25314,23498],{},[585,25316,25317],{"href":25317,"rel":25318},"http://maven.apache.org/",[589],[53,25320,23508],{},[585,25322,25323],{"href":25323,"rel":25324},"https://code.google.com/p/wkhtmltopdf/",[589],[607,25326,25327],{},"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":48,"searchDepth":86,"depth":86,"links":25329},[25330,25331,25332,25333],{"id":24240,"depth":126,"text":24231},{"id":24285,"depth":126,"text":24286},{"id":24670,"depth":126,"text":24671},{"id":24977,"depth":126,"text":24978},[613],"2014-01-22T10:47:14","https://synyx.de/blog/code-gluse/",{},"/blog/code-gluse",{"title":24231,"description":48},"blog/code-gluse",[290,6991,25342,25343,25344,25345,25346,25347],"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…","M4C3LMe5gjOAJ7fTRdyMF5pjkdkf_FbgJp5K0AK4qnA",{"id":25351,"title":25352,"author":25353,"body":25354,"category":25644,"date":25645,"description":25646,"extension":617,"link":25647,"meta":25648,"navigation":499,"path":25649,"seo":25650,"slug":25358,"stem":25652,"tags":25653,"teaser":25656,"__hash__":25657},"blog/blog/talking-tech-to-the-business-guy.md","Talking tech to the business guy",[9507],{"type":11,"value":25355,"toc":25634},[25356,25359,25368,25433,25436,25440,25443,25454,25457,25461,25464,25486,25495,25499,25502,25544,25547,25551,25554,25558,25561,25565,25568,25576,25579,25583,25588,25590,25593,25614,25617,25620,25627],[14,25357,25352],{"id":25358},"talking-tech-to-the-business-guy",[18,25360,25361,25362,25367],{},"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 ",[585,25363,25366],{"href":25364,"rel":25365,"title":25366},"http://programmers.stackexchange.com/questions/157928/how-to-justify-code-refactoring-time",[589],"stackexchange.com post","\nand that seems awkwardly familiar to every developer:",[5221,25369,25370,25376,25382,25387,25392,25397,25401,25406,25411,25416,25421,25425,25430],{},[18,25371,25372,25375],{},[573,25373,25374],{},"Developer",": We need time to do this technical redesign.",[18,25377,25378,25381],{},[573,25379,25380],{},"PHB",": Why?",[18,25383,25384,25386],{},[573,25385,25374],{},": It’ll make things easier to fix",[18,25388,25389,25391],{},[573,25390,25380],{},": So what?",[18,25393,25394,25396],{},[573,25395,25374],{},": It’ll increase throughput – we’ll get new builds out the door quicker?",[18,25398,25399,25391],{},[573,25400,25380],{},[18,25402,25403,25405],{},[573,25404,25374],{},": Err…Happier customers?",[18,25407,25408,25410],{},[573,25409,25380],{},": WTF?",[18,25412,25413,25415],{},[573,25414,25374],{},": I mean increased recommendations, greater satisfaction, more profit sooner due to low turnaround.",[18,25417,25418,25420],{},[573,25419,25380],{},": Oh! Sounds nice. But it only “sounds” nice can I see it?",[18,25422,25423,25410],{},[573,25424,25374],{},[18,25426,25427,25429],{},[573,25428,25380],{},": Show me some numbers!",[18,25431,25432],{},"…. and so on…",[18,25434,25435],{},"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.",[2352,25437,25439],{"id":25438},"_1-trust","1. Trust",[18,25441,25442],{},"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:",[5221,25444,25445,25449],{},[18,25446,25447,25375],{},[573,25448,25374],{},[18,25450,25451,25453],{},[573,25452,25380],{},": Well, it is not particularly cheap but you are the expert and your advice has always been valuable, let’s do\nit!",[18,25455,25456],{},"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.",[2352,25458,25460],{"id":25459},"_2-technical-affinity-of-the-manager","2. Technical affinity of the manager",[18,25462,25463],{},"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:",[5221,25465,25466,25471,25476,25481],{},[18,25467,25468,25470],{},[573,25469,25374],{},": Before we implement any new functionality we need to write tests for this legacy code first.",[18,25472,25473,25475],{},[573,25474,25380],{},": Why is that? We never had any tests back in the nineties when developing software while listening to MC Hammer?",[18,25477,25478,25480],{},[573,25479,25374],{},": 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.",[18,25482,25483,25485],{},[573,25484,25380],{},": Oh yeah, that always bothered me. And unit tests sound like a good way to fix that problem. Let’s write some\ntests first!",[18,25487,25488,25489,25494],{},"Points like “only tested code is easy to change without side effects” or “refactoring and redesign\nare ",[585,25490,25493],{"href":25491,"rel":25492,"title":25493},"http://xprogramming.com/blog/why-is-refactoring-a-must/",[589],"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.",[2352,25496,25498],{"id":25497},"_3-find-a-leverage-that-the-manager-understands","3. Find a leverage that the manager understands",[18,25500,25501],{},"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.",[5221,25503,25504,25508,25513,25518,25524,25529,25535,25538],{},[18,25505,25506,25375],{},[573,25507,25374],{},[18,25509,25510,25381],{},[573,25511,25512],{},"Time-PHB",[18,25514,25515,25517],{},[573,25516,25374],{},": Because after that we can introduce new features into the software way faster!",[18,25519,25520,25523],{},[573,25521,25522],{},"Budget-PHB",": I don’t see the real benefit.",[18,25525,25526,25528],{},[573,25527,25374],{},": 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.",[18,25530,25531,25534],{},[573,25532,25533],{},"Quality-PHB",": Meh, I still don’t see it!",[18,25536,25537],{},"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.",[18,25539,25540,25543],{},[573,25541,25542],{},"Time-PHB, Budget-PHB, Quality-PHB unisonous",": THAT IS THE BEST THING EVER, WHY HAVEN’T WE ALREADY DONE THIS?",[18,25545,25546],{},"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.",[2352,25548,25550],{"id":25549},"_4-business-case","4. Business case",[18,25552,25553],{},"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.",[2352,25555,25557],{"id":25556},"_5-play-the-expert-card","5. Play the expert card",[18,25559,25560],{},"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.",[2352,25562,25564],{"id":25563},"_6-pdd-pain-driven-development","6. PDD – Pain driven development",[18,25566,25567],{},"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.",[5221,25569,25570,25573],{},[18,25571,25572],{},"“If we had any test coverage, half of the reported bugs would not have occured”.",[18,25574,25575],{},"“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”.",[18,25577,25578],{},"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.",[2352,25580,25582],{"id":25581},"_7-understand-the-other-side","7. Understand the other side",[18,25584,25585,25586,986],{},"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 ",[573,25587,9663],{},[2352,25589,21319],{"id":21318},[18,25591,25592],{},"That’s it, I hope this list will help you communicating your desires to your manager.",[18,25594,25595,25596,25599,25600,25605,25606,25609,25610,25613],{},"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. ",[573,25597,25598],{},"You"," are the\nsoftware ",[585,25601,25604],{"href":25602,"rel":25603,"title":25604},"http://manifesto.softwarecraftsmanship.org/",[589],"craftsman"," and often ",[573,25607,25608],{},"you"," 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 ",[573,25611,25612],{},"have"," to do them to deliver a good product, you don’t have to ask for permission\nto do them.",[18,25615,25616],{},"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.",[18,25618,25619],{},"Some Links:",[18,25621,25622],{},[585,25623,25626],{"href":25624,"rel":25625},"http://stackoverflow.com/questions/3138823/what-is-the-best-way-to-explain-refactoring-to-non-technical-people",[589],"stackoverflow.com thread on explaining refactoring to non-tech guys",[18,25628,25629],{},[585,25630,25633],{"href":25631,"rel":25632},"http://www.infoq.com/news/2010/07/explaining-refactoring",[589],"explaining refactoring to management",{"title":48,"searchDepth":86,"depth":86,"links":25635},[25636,25637,25638,25639,25640,25641,25642,25643],{"id":25438,"depth":86,"text":25439},{"id":25459,"depth":86,"text":25460},{"id":25497,"depth":86,"text":25498},{"id":25549,"depth":86,"text":25550},{"id":25556,"depth":86,"text":25557},{"id":25563,"depth":86,"text":25564},{"id":25581,"depth":86,"text":25582},{"id":21318,"depth":86,"text":21319},[613],"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":25352,"description":25651},"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",[4221,25654,19860,25655],"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,…","ez7W0LZkIxZszGVbDbxm6yhrWEZ66IYuD1ffECT1xCY",{"id":25659,"title":25660,"author":25661,"body":25662,"category":25905,"date":25906,"description":25907,"extension":617,"link":25908,"meta":25909,"navigation":499,"path":25910,"seo":25911,"slug":25666,"stem":25913,"tags":25914,"teaser":25920,"__hash__":25921},"blog/blog/my-first-time-on-a-conference-with-synyx.md","My first time on a conference with synyx",[11620],{"type":11,"value":25663,"toc":25903},[25664,25667,25674,25680,25683,25686,25701,25715,25730,25761,25802,25817,25820,25835,25854,25857,25860,25877,25880],[14,25665,25660],{"id":25666},"my-first-time-on-a-conference-with-synyx",[18,25668,25669,25670,25673],{},"I am a fresh employee at synyx. I started early in 2013 to work for the company and the",[573,25671,25672],{},"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!”.",[18,25675,25676,25677,25679],{},"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 ",[573,25678,4221],{},", 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!",[18,25681,25682],{},"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.",[18,25684,25685],{},"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.",[18,25687,25688,25689,17540,25694,17019,25697,25700],{},"One of the first talks I listened to was ",[585,25690,25693],{"href":25691,"rel":25692},"http://fhornain.wordpress.com/2013/11/12/devoxx13-java-ee-7-whats-new-in-the-java-ee-platform/http://",[589],"**Java EE 7: What’s New in the Java EE Platform\n**",[573,25695,25696],{},"Argun\nGupta",[573,25698,25699],{},"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.",[18,25702,25703,25706,25707,25714],{},[573,25704,25705],{},"David Gageot"," showed us in a quick 30 minutes live coding session called ",[585,25708,25711],{"href":25709,"rel":25710},"http://www.devoxx.be/dv13-david-gageot.html?presId=3108",[589],[27,25712,25713],{},"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.",[18,25716,25717,25718,25721,25722,25729],{},"At the second day I was excited about the talk from ",[573,25719,25720],{},"José Paumard"," called ",[585,25723,25726],{"href":25724,"rel":25725},"http://www.devoxx.be/dv13-jos-paumard.html?presId=3540",[589],[27,25727,25728],{},"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.",[18,25731,25732,25733,4070,25740,25743,25744,99,25749,25754,25755,25760],{},"The next talk I watched was ",[585,25734,25737],{"href":25735,"rel":25736},"http://www.devoxx.be/dv13-matt-raible.html?presId=3648",[589],[27,25738,25739],{},"The Modern Java Web Developer",[573,25741,25742],{},"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 ",[1773,25745],{"alt":25746,"src":25747,"title":25748},"\"20131112_133433\"","http://angularjs.org/","Angular JS",[585,25750,25753],{"href":25751,"rel":25752},"http://jquery.com/",[589],"jQuery",",\nCSS3, ",[585,25756,25759],{"href":25757,"rel":25758},"http://www.html5rocks.com/en/",[589],"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.",[18,25762,25763,25764,25767,25768,25775,25776,25779,25780,25783,25784,3566,25787,17019,25792,25797,25798,25801],{},"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 ",[573,25765,25766],{},"Martijn Verburg",". The problem was not that they were bad, but maybe not so forceful. On the other hand I\nwatched ",[585,25769,25772],{"href":25770,"rel":25771},"http://www.devoxx.be/dv13-chet-haase.html?presId=3193",[589],[27,25773,25774],{},"Patterns Shmatterns"," by ",[573,25777,25778],{},"Chet Haase",". And maybe that\nmakes me forget all the other talks. It was a ",[573,25781,25782],{},"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 ",[573,25785,25786],{},"Martijn Verburg:",[585,25788,25791],{"href":25789,"rel":25790},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3214",[589],"*\n*The Habits of Highly Effective Technical Teams**",[585,25793,25796],{"href":25794,"rel":25795},"http://www.devoxx.be/dv13-martijn-verburg.html?presId=3223",[589],"*\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 ",[573,25799,25800],{},"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.",[18,25803,25804,25808,25809,25812,25813,25816],{},[1773,25805],{"alt":25806,"src":25807},"\"20131111_231326\"","https://media.synyx.de/uploads//2013/12/20131111_231326.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",[573,25810,25811],{},"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 ",[573,25814,25815],{},"almost"," did it. Great but different birthday than the years before. Thanks synyx!",[18,25818,25819],{},"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.",[18,25821,25822,25823,25826,25827,25834],{},"First of all another highlight of my Devoxx week was the talk from ",[573,25824,25825],{},"Joey Cieplinski"," with the title ",[585,25828,25831],{"href":25829,"rel":25830},"http://www.devoxx.be/dv13-joe-cieplinski.html?presId=3330",[589],[27,25832,25833],{},"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.",[18,25836,25837,25844,25845,25848,25849,25853],{},[585,25838,25841],{"href":25839,"rel":25840},"http://www.devoxx.be/dv13-alain-regnier.html?presId=3733",[589],[27,25842,25843],{},"Introduction to Google Glass"," from ",[573,25846,25847],{},"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. ",[1773,25850],{"alt":25851,"src":25852},"\"20131115_105017\"","https://media.synyx.de/uploads//2013/12/20131115_105017.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.",[18,25855,25856],{},"And at the end a small summary.",[18,25858,25859],{},"Antwerp:",[577,25861,25862,25865,25868,25871,25874],{},[580,25863,25864],{},"The subway smells like mushrooms.",[580,25866,25867],{},"The subway is small and carries a lot of people.",[580,25869,25870],{},"Awesome and horrible beer at once.",[580,25872,25873],{},"Beautiful main train station.",[580,25875,25876],{},"They love mayonnaise.",[18,25878,25879],{},"Devoxx:",[577,25881,25882,25885,25888,25891,25894,25897,25900],{},[580,25883,25884],{},"Great speakers with very interesting talks.",[580,25886,25887],{},"Nice People.",[580,25889,25890],{},"Learned a lot of new stuff.",[580,25892,25893],{},"Raspberry Pis will rule the world.",[580,25895,25896],{},"Crab sandwiches are delicious. Yeah! Ate a lot of them.",[580,25898,25899],{},"Tuna salad with only tuna are not that fun.",[580,25901,25902],{},"Belgian fries are awesome.",{"title":48,"searchDepth":86,"depth":86,"links":25904},[],[614],"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":25660,"description":25912},"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",[3491,19738,25915,25916,25917,4231,25918,25919],"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.…","o_aFlRO4Z2neys_7nDR-63csn2MLWAYQgg8F4ktsHls",{"id":25923,"title":25924,"author":25925,"body":25926,"category":26034,"date":26035,"description":26036,"extension":617,"link":26037,"meta":26038,"navigation":499,"path":26039,"seo":26040,"slug":25930,"stem":26042,"tags":26043,"teaser":26046,"__hash__":26047},"blog/blog/synyx-bei-den-xp-days-2013.md","synyx bei den XP Days 2013",[13202],{"type":11,"value":25927,"toc":26032},[25928,25931,25940,25943,25952,25961,25973,25982,25991,25999,26002,26011,26020,26029],[14,25929,25924],{"id":25930},"synyx-bei-den-xp-days-2013",[18,25932,25933,25934,25939],{},"Vom 14. bis 16. November fanden in Karlsruhe die ",[585,25935,25938],{"href":25936,"rel":25937,"title":25938},"http://xpdays.de/2013/",[589],"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.",[18,25941,25942],{},"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.",[18,25944,25945,25946,25951],{},"Die Konferenz began für mich mit dem\nVortrag ",[585,25947,25950],{"href":25948,"rel":25949},"http://www.xpdays.de/2013/sessions/079-kreative-retrospektiven.html",[589],"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.",[18,25953,25954,25955,25960],{},"Weiter ging es\nmit ",[585,25956,25959],{"href":25957,"rel":25958},"http://www.xpdays.de/2013/sessions/049-pair-programming-mythbusters.html",[589],"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.",[18,25962,25963,25968,25972],{},[585,25964,25967],{"href":25965,"rel":25966},"http://www.xpdays.de/2013/sessions/019-ich-will-kein-agiler-coach-mehr-sein-und-du.html",[589],"„",[585,25969,25971],{"href":25965,"rel":25970},[589],"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ß.",[18,25974,25975,25976,25981],{},"Am Nachmittag überzeugte mich Martin Klose mit seinem\nVortrag ",[585,25977,25980],{"href":25978,"rel":25979},"http://www.xpdays.de/2013/sessions/100-code-retreat-behind-the-scenes.html",[589],"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.",[18,25983,25984,25985,25990],{},"Als letzte Veranstaltung des Tages hörte ich mir noch\ndie ",[585,25986,25989],{"href":25987,"rel":25988},"http://www.xpdays.de/2013/sessions/052-kanban-nicht-yet-another-development-process.html",[589],"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.",[18,25992,5442,25993,25998],{},[585,25994,25997],{"href":25995,"rel":25996},"http://www.xpdays.de/2013/sessions/keynote-dan-north-why-agile-doesnt-scale-and-what-you-can-do-about-it.html",[589],"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.",[18,26000,26001],{},"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.",[18,26003,26004,26005,26010],{},"Der erste Workshop war für mich gleichzeitig der, welcher mir am meisten gebracht\nhat: ",[585,26006,26009],{"href":26007,"rel":26008},"http://www.xpdays.de/2013/sessions/060b-object-calisthenics-objekt-gymnastik-praxis.html",[589],"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.",[18,26012,26013,26014,26019],{},"Der zweite Workshop den ich besuchen wollte,\nwar ",[585,26015,26018],{"href":26016,"rel":26017},"http://www.xpdays.de/2013/sessions/054-hands-on-test-refactoring.html",[589],"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.",[18,26021,26022,26023,26028],{},"Der letzte Workshop und für mich auch die letzte Veranstaltung der Konferenz\nwar ",[585,26024,26027],{"href":26025,"rel":26026},"http://www.xpdays.de/2013/sessions/093-taking-baby-steps-reloaded.html",[589],"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.",[18,26030,26031],{},"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":48,"searchDepth":86,"depth":86,"links":26033},[],[613],"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":25924,"description":26041},"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",[4221,3491,26044,26045],"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":26049,"title":26050,"author":26051,"body":26053,"category":26500,"date":26501,"description":26502,"extension":617,"link":26503,"meta":26504,"navigation":499,"path":26505,"seo":26506,"slug":26057,"stem":26507,"tags":26508,"teaser":26513,"__hash__":26514},"blog/blog/yammer-metrics-made-easy-part-i.md","yammer – Metrics made easy – Part I",[26052],"arrasz",{"type":11,"value":26054,"toc":26498},[26055,26058,26061,26064,26098,26101,26115,26118,26137,26140,26143,26146,26149,26166,26169,26176,26179,26182,26206,26209,26212,26215,26218,26221,26224,26268,26271,26481,26484,26490,26493,26496],[14,26056,26050],{"id":26057},"yammer-metrics-made-easy-part-i",[18,26059,26060],{},"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.",[18,26062,26063],{},"First we add needed dependencies into our pom:",[43,26065,26067],{"className":1980,"code":26066,"language":1982,"meta":48,"style":48},"\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",[50,26068,26069,26073,26078,26083,26088,26093],{"__ignoreMap":48},[53,26070,26071],{"class":55,"line":56},[53,26072,500],{"emptyLinePlaceholder":499},[53,26074,26075],{"class":55,"line":86},[53,26076,26077],{}," \u003Cdependency>\n",[53,26079,26080],{"class":55,"line":126},[53,26081,26082],{}," \u003Cgroupid>com.yammer.metrics\u003C/groupid>\n",[53,26084,26085],{"class":55,"line":163},[53,26086,26087],{}," \u003Cartifactid>metrics-core\u003C/artifactid>\n",[53,26089,26090],{"class":55,"line":186},[53,26091,26092],{}," \u003Cversion>3.0.0-BETA1\u003C/version>\n",[53,26094,26095],{"class":55,"line":221},[53,26096,26097],{}," \u003C/dependency>\n",[18,26099,26100],{},"After providing this, we are able to do something like that in our code:",[43,26102,26104],{"className":288,"code":26103,"language":290,"meta":48,"style":48},"\nstatic final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n\n",[50,26105,26106,26110],{"__ignoreMap":48},[53,26107,26108],{"class":55,"line":56},[53,26109,500],{"emptyLinePlaceholder":499},[53,26111,26112],{"class":55,"line":86},[53,26113,26114],{},"static final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n",[18,26116,26117],{},"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:",[43,26119,26121],{"className":288,"code":26120,"language":290,"meta":48,"style":48},"\nstatic final MetricRegistry crunchMetrics = new MetricRegistry(\"CrunchMeasurement\");\nstatic final MetricRegistry requestMetrics = new MetricRegistry(\"RequestMeasurement\");\n\n",[50,26122,26123,26127,26132],{"__ignoreMap":48},[53,26124,26125],{"class":55,"line":56},[53,26126,500],{"emptyLinePlaceholder":499},[53,26128,26129],{"class":55,"line":86},[53,26130,26131],{},"static final MetricRegistry crunchMetrics = new MetricRegistry(\"CrunchMeasurement\");\n",[53,26133,26134],{"class":55,"line":126},[53,26135,26136],{},"static final MetricRegistry requestMetrics = new MetricRegistry(\"RequestMeasurement\");\n",[18,26138,26139],{},"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).",[18,26141,26142],{},"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?",[18,26144,26145],{},"Which possibilities do we have with metrics?",[18,26147,26148],{},"Well, there are 5 types of measurements included",[577,26150,26151,26154,26157,26160,26163],{},[580,26152,26153],{},"Gauge (instantaneous measurement of one value)",[580,26155,26156],{},"Histogram (measurement of value variants)",[580,26158,26159],{},"Timer (measurement of timings)",[580,26161,26162],{},"Counter (measurement of atomic longs)",[580,26164,26165],{},"Meter (measurement of ticks in a time range)",[18,26167,26168],{},"as well as some typically needed ones for special purposes like the",[18,26170,26171],{},[27,26172,26173],{},[573,26174,26175],{},"com.yammer.metrics.servlet.DefaultWebappMetricsFilter (we will discuss this later in the blog)",[18,26177,26178],{},"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.",[18,26180,26181],{},"Next step is to expose the measured values to a format you can use it. Metrics provides a lot of default exposements\nlike:",[577,26183,26184,26186,26189,26192,26195,26198,26201,26204],{},[580,26185,11915],{},[580,26187,26188],{},"JSON",[580,26190,26191],{},"CSV",[580,26193,26194],{},"log4j / slf4j",[580,26196,26197],{},"logback",[580,26199,26200],{},"ganglia",[580,26202,26203],{},"graphite",[580,26205,17489],{},[18,26207,26208],{},"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.",[18,26210,26211],{},"So, at a glance we need to do the following steps to enable a Java-Application with Metrics:",[18,26213,26214],{},"1.) Create and instantiate a MetricRegistry (i highly encourage you to inject them into your productive code!)",[18,26216,26217],{},"2.) Create Measurements to your needs (in our example the mentioned 2 timers)",[18,26219,26220],{},"3.) Create and instantiate a Reporter to your needs ( i highly encourage you to inject them into your productive code\n🙂",[18,26222,26223],{},"Let’s show this with a very straightforward coded application",[43,26225,26227],{"className":288,"code":26226,"language":290,"meta":48,"style":48},"\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",[50,26228,26229,26233,26238,26243,26248,26253,26258,26263],{"__ignoreMap":48},[53,26230,26231],{"class":55,"line":56},[53,26232,500],{"emptyLinePlaceholder":499},[53,26234,26235],{"class":55,"line":86},[53,26236,26237],{}," final MetricRegistry metrics = new MetricRegistry(\"Demonstration\");\n",[53,26239,26240],{"class":55,"line":126},[53,26241,26242],{}," evictions = metrics.counter(MetricRegistry.name(HealthCheckDemo.class, \"cache-evictions\"));\n",[53,26244,26245],{"class":55,"line":163},[53,26246,26247],{}," request = metrics.timer(MetricRegistry.name(ArithmeticDemoOperation.class, \"calculation-duration\"));\n",[53,26249,26250],{"class":55,"line":186},[53,26251,26252],{}," reporter = ConsoleReporter.forRegistry(metrics).build();\n",[53,26254,26255],{"class":55,"line":221},[53,26256,26257],{}," jmxReporter = JmxReporter.forRegistry(metrics).build();\n",[53,26259,26260],{"class":55,"line":242},[53,26261,26262],{}," reporter.start(1, TimeUnit.MINUTES); // should expose values every minute\n",[53,26264,26265],{"class":55,"line":273},[53,26266,26267],{}," jmxReporter.start();\n",[18,26269,26270],{},"After running this application you should see a console output like this:",[43,26272,26274],{"className":13667,"code":26273,"language":13669,"meta":48,"style":48},"\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",[50,26275,26276,26280,26285,26290,26295,26300,26305,26310,26314,26319,26324,26329,26334,26339,26344,26349,26354,26359,26364,26369,26374,26379,26384,26389,26393,26397,26401,26405,26409,26413,26418,26423,26428,26433,26437,26441,26446,26451,26456,26461,26466,26471,26476],{"__ignoreMap":48},[53,26277,26278],{"class":55,"line":56},[53,26279,500],{"emptyLinePlaceholder":499},[53,26281,26282],{"class":55,"line":86},[53,26283,26284],{},"05.05.13 08:22:03 ==============================================================\n",[53,26286,26287],{"class":55,"line":126},[53,26288,26289],{},"-- Counters --------------------------------------------------------------------\n",[53,26291,26292],{"class":55,"line":163},[53,26293,26294],{},"org.synyx.demos.HealthCheckDemo.cache-evictions\n",[53,26296,26297],{"class":55,"line":186},[53,26298,26299],{},"count = 1\n",[53,26301,26302],{"class":55,"line":221},[53,26303,26304],{},"-- Timers ----------------------------------------------------------------------\n",[53,26306,26307],{"class":55,"line":242},[53,26308,26309],{},"org.synyx.demos.ArithmeticDemoOperation.calculation-duration\n",[53,26311,26312],{"class":55,"line":273},[53,26313,26299],{},[53,26315,26316],{"class":55,"line":279},[53,26317,26318],{},"mean rate = 0,02 calls/second\n",[53,26320,26321],{"class":55,"line":496},[53,26322,26323],{},"1-minute rate = 0,09 calls/second\n",[53,26325,26326],{"class":55,"line":503},[53,26327,26328],{},"5-minute rate = 0,17 calls/second\n",[53,26330,26331],{"class":55,"line":509},[53,26332,26333],{},"15-minute rate = 0,19 calls/second\n",[53,26335,26336],{"class":55,"line":515},[53,26337,26338],{},"min = 1250,28 milliseconds\n",[53,26340,26341],{"class":55,"line":521},[53,26342,26343],{},"max = 1250,28 milliseconds\n",[53,26345,26346],{"class":55,"line":527},[53,26347,26348],{},"mean = 1250,28 milliseconds\n",[53,26350,26351],{"class":55,"line":533},[53,26352,26353],{},"stddev = 0,00 milliseconds\n",[53,26355,26356],{"class":55,"line":539},[53,26357,26358],{},"median = 1250,28 milliseconds\n",[53,26360,26361],{"class":55,"line":545},[53,26362,26363],{},"75% \u003C= 1250,28 milliseconds\n",[53,26365,26366],{"class":55,"line":2070},[53,26367,26368],{},"95% \u003C= 1250,28 milliseconds\n",[53,26370,26371],{"class":55,"line":2075},[53,26372,26373],{},"98% \u003C= 1250,28 milliseconds\n",[53,26375,26376],{"class":55,"line":2081},[53,26377,26378],{},"99% \u003C= 1250,28 milliseconds\n",[53,26380,26381],{"class":55,"line":2087},[53,26382,26383],{},"99.9% \u003C= 1250,28 milliseconds\n",[53,26385,26386],{"class":55,"line":2092},[53,26387,26388],{},"05.05.13 08:23:03 ==============================================================\n",[53,26390,26391],{"class":55,"line":2097},[53,26392,26289],{},[53,26394,26395],{"class":55,"line":2103},[53,26396,26294],{},[53,26398,26399],{"class":55,"line":2109},[53,26400,26299],{},[53,26402,26403],{"class":55,"line":2115},[53,26404,26304],{},[53,26406,26407],{"class":55,"line":2120},[53,26408,26309],{},[53,26410,26411],{"class":55,"line":2946},[53,26412,26299],{},[53,26414,26415],{"class":55,"line":2952},[53,26416,26417],{},"mean rate = 0,01 calls/second\n",[53,26419,26420],{"class":55,"line":2964},[53,26421,26422],{},"1-minute rate = 0,03 calls/second\n",[53,26424,26425],{"class":55,"line":2973},[53,26426,26427],{},"5-minute rate = 0,14 calls/second\n",[53,26429,26430],{"class":55,"line":2979},[53,26431,26432],{},"15-minute rate = 0,18 calls/second\n",[53,26434,26435],{"class":55,"line":2990},[53,26436,26338],{},[53,26438,26439],{"class":55,"line":10443},[53,26440,26343],{},[53,26442,26444],{"class":55,"line":26443},36,[53,26445,26348],{},[53,26447,26449],{"class":55,"line":26448},37,[53,26450,26353],{},[53,26452,26454],{"class":55,"line":26453},38,[53,26455,26358],{},[53,26457,26459],{"class":55,"line":26458},39,[53,26460,26363],{},[53,26462,26464],{"class":55,"line":26463},40,[53,26465,26368],{},[53,26467,26469],{"class":55,"line":26468},41,[53,26470,26373],{},[53,26472,26474],{"class":55,"line":26473},42,[53,26475,26378],{},[53,26477,26479],{"class":55,"line":26478},43,[53,26480,26383],{},[18,26482,26483],{},"Furthermore, if you debug the demo application you are able to inspect our exposements via a jmx-client like jVisualVM\nof jConsole after connecting.",[18,26485,26486],{},[1773,26487],{"alt":26488,"src":26489},"\"Bildschirmfoto 2013-09-02 um 12.08.02\"","https://media.synyx.de/uploads//2013/06/Bildschirmfoto-2013-09-02-um-12.08.02.png",[18,26491,26492],{},"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!",[18,26494,26495],{},"Next time we will enable a java-webapplication with some measurements, so stay tuned!",[607,26497,989],{},{"title":48,"searchDepth":86,"depth":86,"links":26499},[],[613,996],"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":26050,"description":26060},"blog/yammer-metrics-made-easy-part-i",[290,26509,26510,26511,19537,18100,26512],"javaee","jetty","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…","3qXtXK-jIyC3OdUZ5imnvlDvTO3UVK8aJvjo9_nNGT8",{"id":26516,"title":26517,"author":26518,"body":26519,"category":26797,"date":26798,"description":26528,"extension":617,"link":8543,"meta":26799,"navigation":499,"path":26800,"seo":26801,"slug":26523,"stem":26802,"tags":26803,"teaser":26808,"__hash__":26809},"blog/blog/devoooops-azubitausch-bei-synyx.md","DevOooops – Azubitausch bei synyx",[8328],{"type":11,"value":26520,"toc":26795},[26521,26524,26529,26532,26542,26547,26557,26567,26572,26575,26580,26589,26594,26603,26608,26611,26616,26629,26632,26637,26647,26652,26655,26658,26661,26666,26669,26674,26701,26706,26716,26721,26724,26729,26732,26737,26740,26745,26754,26759,26766,26772,26775,26780,26783],[14,26522,26517],{"id":26523},"devoooops-azubitausch-bei-synyx",[18,26525,26526],{},[27,26527,26528],{},"03-04-2013",[18,26530,26531],{},"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.",[18,26533,26534,26535,26538,26539],{},"Dank ",[50,26536,26537],{},"$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. ",[53,26540,26541],{},"_Anmerkung der Redaktion: man beachte die Einträge vom\n29-04-2013 und 17-06-2013_",[18,26543,26544],{},[27,26545,26546],{},"10-04-2013",[18,26548,26549,26550,26556],{},"Mein erstes richtiges Shell-Skript ist fertig und der\ndazugehörige ",[585,26551,26555],{"href":26552,"rel":26553,"title":26554},"http://blog.synyx.de/2013/04/continuous-deployment-automatic-backup-script/",[589],"Automatisiertes Backup Skript","Blogpost","\nauch. Das bisher manuell ablaufende Backup vor Deployments ist automatisiert, yeah.",[18,26558,26559,26560,26566],{},"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 ",[585,26561,26565],{"href":26562,"rel":26563,"title":26564},"https://puppetlabs.com/",[589],"puppetlabs","Puppet",". Das\nsieht ja ganz nett aus. Mir gefällt der “declare how the world should be” Ansatz.",[18,26568,26569],{},[27,26570,26571],{},"15-04-2013",[18,26573,26574],{},"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….",[18,26576,26577],{},[27,26578,26579],{},"16-04-2013",[18,26581,26582,26583,26588],{},"Ich möchte, dass\ndie ",[585,26584,26587],{"href":26585,"rel":26586,"title":26587},"http://blog.synyx.de/2012/11/urlaubsverwaltung-was-hat-sich-getan/",[589],"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.",[18,26590,26591],{},[27,26592,26593],{},"17-04-2013",[18,26595,26596,26597,26602],{},"Ich versuche herumfliegende Kabel im ",[585,26598,26601],{"href":26599,"rel":26600,"title":26599},"http://partkeepr.org/",[589],"Partkeepr"," zu inventarisieren. Die\nBetonung liegt bei “versuchen”… (“Was ist das denn?! Hab ich ja noch nie gesehen…”)",[18,26604,26605],{},[27,26606,26607],{},"22-04-2013",[18,26609,26610],{},"Verschdl gibt seit Wochen wirre Dinge von sich. Irgendwas mit “AWS” und “Cloud”. Dann schaue ich mir das halt mal an…",[18,26612,26613],{},[27,26614,26615],{},"23-04-2013",[18,26617,26618,26619,19999,26624],{},"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. ",[585,26620,26623],{"href":26621,"rel":26622,"title":26621},"http://theforeman.org/",[589],"Foreman",[585,26625,26628],{"href":26626,"rel":26627,"title":26628},"https://github.com/rodjek/librarian-puppet",[589],"librarian-puppet",[18,26630,26631],{},"…und wir sind als Pioniere mit dabei.",[18,26633,26634],{},[27,26635,26636],{},"29-04-2013",[18,26638,26639,26640,14064,26643,26646],{},"Aus einer Laune heraus führe ich auf meinem Rechner ",[50,26641,26642],{},"mv sudoers sudoers.bak",[50,26644,26645],{},"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…",[18,26648,26649],{},[27,26650,26651],{},"30-04-2013",[18,26653,26654],{},"Heute darf ich mit htaccess spielen. Zivi und ich richten zum Einen eine LDAP-Authentifizierung, zum Anderen eine\nAuthentifizierung mittels htpasswd File ein.",[18,26656,26657],{},"Cool, ist ja gar nicht sooo kompliziert.",[18,26659,26660],{},"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 🙂",[18,26662,26663],{},[27,26664,26665],{},"08-05-2013",[18,26667,26668],{},"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.",[18,26670,26671],{},[27,26672,26673],{},"14-05-2013",[18,26675,26676,26677,26683,26684,26689,26690,26696,26697,26700],{},"Ich stelle fest: ",[585,26678,26682],{"href":26679,"rel":26680,"title":26681},"http://aws.amazon.com/de/",[589],"AWS","Amazon Web Services"," EC2 Instanzen\nper ",[585,26685,26688],{"href":26686,"rel":26687,"title":26688},"http://www.vagrantup.com/",[589],"Vagrant"," hochzuziehen ist trotz\nvorhandenem ",[585,26691,26695],{"href":26692,"rel":26693,"title":26694},"https://github.com/mitchellh/vagrant-aws",[589],"vagrant-aws","AWS-Plugin"," nicht so ganz ausgereift. AWS EC2\nInstanzen über ",[585,26698,26623],{"href":26621,"rel":26699,"title":26621},[589]," zu pflegen ist allerdings einfach nur\nriesengroßer Pain argh…",[18,26702,26703],{},[27,26704,26705],{},"22-05-2013",[18,26707,26708,26711,26712,26715],{},[50,26709,26710],{},"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 ",[50,26713,26714],{},"librarian-puppet update"," auszuführen. Naja, dann skripte ich halt selber was.",[18,26717,26718],{},[27,26719,26720],{},"17-06-2013",[18,26722,26723],{},"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.",[18,26725,26726],{},[27,26727,26728],{},"18-06-2013",[18,26730,26731],{},"“Aljona, das Problem gestern lag gar nicht an deinem Rechner. Unser Netzwerk war kaputt.” Argh….",[18,26733,26734],{},[27,26735,26736],{},"02-07-2013",[18,26738,26739],{},"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…",[18,26741,26742],{},[27,26743,26744],{},"03-07-2013",[18,26746,26747,26748,26753],{},"Ich erfreue mich mal wieder daran ",[585,26749,26752],{"href":26750,"rel":26751,"title":26752},"http://gitolite.com/gitolite/admin.html",[589],"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.",[18,26755,26756],{},[27,26757,26758],{},"09-07-2013",[18,26760,26761,26762,26765],{},"Kein “works on my machine” mehr für unser synyx Dashboard. Ich habe eine ",[585,26763,26688],{"href":26686,"rel":26764,"title":26688},[589],"\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.",[18,26767,26768,26769],{},"Nun reicht ein simples ",[50,26770,26771],{},"vagrant up",[18,26773,26774],{},"Automatisierung++ 🙂",[18,26776,26777],{},[27,26778,26779],{},"…und die Moral von der Geschicht’?",[18,26781,26782],{},"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.",[18,26784,26785,26786,26791,26792,26794],{},"Aber Vorsicht, es gibt auch Nebenwirkungen! Die inflationäre Benutzung von ",[585,26787,26790],{"href":26788,"rel":26789,"title":26790},"http://www.vim.org/",[589],"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 ",[50,26793,2628],{}," zu suchen….",{"title":48,"searchDepth":86,"depth":86,"links":26796},[],[5568],"2013-07-10T12:39:00",{},"/blog/devoooops-azubitausch-bei-synyx",{"title":26517,"description":26528},"blog/devoooops-azubitausch-bei-synyx",[26804,26805,26806,26807,6238,5743],"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.…","b5UDOKQz1H2dlc46Kdu72unYd-ZF96_a51uPwhU1yMg",{"id":26811,"title":26812,"author":26813,"body":26814,"category":26977,"date":26978,"description":26979,"extension":617,"link":26980,"meta":26981,"navigation":499,"path":26982,"seo":26983,"slug":26818,"stem":26984,"tags":26985,"teaser":26989,"__hash__":26990},"blog/blog/usefulness-ranking-of-code-metrics.md","Usefulness Ranking of Code Metrics",[9507],{"type":11,"value":26815,"toc":26966},[26816,26819,26822,26830,26833,26836,26850,26853,26857,26866,26870,26873,26877,26880,26883,26886,26890,26893,26897,26906,26910,26913,26920,26924,26933,26937,26944,26948,26957,26960,26963],[14,26817,26812],{"id":26818},"usefulness-ranking-of-code-metrics",[18,26820,26821],{},"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.",[18,26823,26824,26825,26829],{},"Since I mostly work in Java projects with ",[585,26826,26828],{"href":22463,"rel":26827,"title":26828},[589],"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.",[18,26831,26832],{},"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.",[18,26834,26835],{},"First things first: What makes a code metric useful?",[577,26837,26838,26841,26844,26847],{},[580,26839,26840],{},"It outright shows a violation of or deviation from the defined best practice.",[580,26842,26843],{},"It hints to a place in your source code that has design flaws.",[580,26845,26846],{},"It shows that a certain aspect in your project is highly neglected and needs to be worked on.",[580,26848,26849],{},"You can quantify it by telling a “normal” or “good” value and react on it when this norm is violated",[18,26851,26852],{},"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:",[2352,26854,26856],{"id":26855},"_1-cyclomatic-complexity","1. Cyclomatic Complexity",[18,26858,10933,26859,26865],{},[585,26860,26864],{"href":26861,"rel":26862,"title":26863},"http://en.wikipedia.org/wiki/Cyclomatic_complexity",[589],"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.",[2352,26867,26869],{"id":26868},"_2-duplications","2. Duplications",[18,26871,26872],{},"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.",[2352,26874,26876],{"id":26875},"_3-rules-compliance-sonar","3. Rules Compliance (Sonar)",[18,26878,26879],{},"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.",[18,26881,26882],{},"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.",[18,26884,26885],{},"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.",[2352,26887,26889],{"id":26888},"_4-package-tangling","4. Package Tangling",[18,26891,26892],{},"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.",[2352,26894,26896],{"id":26895},"_5-lcom4","5. LCOM4",[18,26898,10933,26899,26905],{},[585,26900,26904],{"href":26901,"rel":26902,"title":26903},"https://web.archive.org/web/20131129081756/http://docs.codehaus.org:80/display/SONAR/LCOM4+-+Lack+of+Cohesion+of+Methods",[589],"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.",[2352,26907,26909],{"id":26908},"_6-lines-of-code","6. Lines of Code",[18,26911,26912],{},"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?",[18,26914,26915,26916,26919],{},"Well, on the one hand this is true – on the other hand it isn’t ",[573,26917,26918],{},"that"," 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.",[2352,26921,26923],{"id":26922},"_7-sonar-quality-index","7. Sonar Quality Index",[18,26925,26926,26927,26932],{},"Sonar’s ",[585,26928,26931],{"href":26929,"rel":26930,"title":26931},"https://web.archive.org/web/20150118055327/http://docs.codehaus.org:80/display/SONAR/Quality+Index+Plugin",[589],"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.",[2352,26934,26936],{"id":26935},"_8-sonar-complexity-factor","8. Sonar Complexity Factor",[18,26938,26939,26940,26943],{},"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 ",[573,26941,26942],{},"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.",[2352,26945,26947],{"id":26946},"_9-lines-of-comments","9. Lines of Comments",[18,26949,26950,26951,26956],{},"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 ",[585,26952,26955],{"href":26953,"rel":26954,"title":26955},"http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882",[589],"Clean Code"," that\n“comments are not like Schindler’s list, they are not pure good”?",[18,26958,26959],{},"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.",[18,26961,26962],{},"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.",[18,26964,26965],{},"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":48,"searchDepth":86,"depth":86,"links":26967},[26968,26969,26970,26971,26972,26973,26974,26975,26976],{"id":26855,"depth":86,"text":26856},{"id":26868,"depth":86,"text":26869},{"id":26875,"depth":86,"text":26876},{"id":26888,"depth":86,"text":26889},{"id":26895,"depth":86,"text":26896},{"id":26908,"depth":86,"text":26909},{"id":26922,"depth":86,"text":26923},{"id":26935,"depth":86,"text":26936},{"id":26946,"depth":86,"text":26947},[613],"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":26812,"description":26821},"blog/usefulness-ranking-of-code-metrics",[26986,22825,26987,26988],"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":26992,"title":26993,"author":26994,"body":26995,"category":27655,"date":27656,"description":27002,"extension":617,"link":27657,"meta":27658,"navigation":499,"path":27659,"seo":27660,"slug":26999,"stem":27661,"tags":27662,"teaser":27664,"__hash__":27665},"blog/blog/awesome-css-3-layouting.md","Awesome CSS 3 Layouting",[5191],{"type":11,"value":26996,"toc":27650},[26997,27000,27003,27017,27032,27046,27049,27052,27058,27228,27254,27274,27277,27281,27295,27301,27599,27605,27616,27619,27622,27625,27636,27645,27648],[14,26998,26993],{"id":26999},"awesome-css-3-layouting",[18,27001,27002],{},"At first let me ask you a few questions about developing web applications:",[577,27004,27005,27008,27011,27014],{},[580,27006,27007],{},"How do you create multiple column layouts?",[580,27009,27010],{},"How do you make it flexible?",[580,27012,27013],{},"How do you solve the 100% height problem?",[580,27015,27016],{},"How do you make it responsiveness for Desktop vs Mobile?",[18,27018,27019,27020,27023,27024,27027,27028,27031],{},"If one of your answers contained ‘",[573,27021,27022],{},"absolute positioning","‘, ‘",[573,27025,27026],{},"floating","‘ or ‘",[573,27029,27030],{},"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.",[18,27033,27034],{},[27,27035,27036,27039,27040,3566,27043],{},[13401,27037,27038],{},"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 ",[50,27041,27042],{},"display: flex",[573,27044,27045],{},"yay",[18,27047,27048],{},"Feel free to add working examples in the comments below 🙂",[2352,27050,27042],{"id":27051},"display-flex",[18,27053,27054,27055,27057],{},"My most favourite feature is the new display property ",[50,27056,27042],{},". 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.",[43,27059,27061],{"className":13667,"code":27060,"language":13669,"meta":48,"style":48},".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",[50,27062,27063,27068,27073,27078,27082,27087,27092,27097,27102,27107,27111,27116,27121,27126,27130,27135,27140,27144,27149,27154,27158,27163,27168,27173,27178,27182,27187,27192,27196,27201,27205,27209,27214,27219,27224],{"__ignoreMap":48},[53,27064,27065],{"class":55,"line":56},[53,27066,27067],{},".container {\n",[53,27069,27070],{"class":55,"line":86},[53,27071,27072],{}," display: -webkit-flex;\n",[53,27074,27075],{"class":55,"line":126},[53,27076,27077],{}," display: flex;\n",[53,27079,27080],{"class":55,"line":163},[53,27081,282],{},[53,27083,27084],{"class":55,"line":186},[53,27085,27086],{},".menu {\n",[53,27088,27089],{"class":55,"line":221},[53,27090,27091],{}," overflow: hidden;\n",[53,27093,27094],{"class":55,"line":242},[53,27095,27096],{}," background-color: #D8E47F;\n",[53,27098,27099],{"class":55,"line":273},[53,27100,27101],{}," -webkit-flex: 1;\n",[53,27103,27104],{"class":55,"line":279},[53,27105,27106],{}," flex: 1;\n",[53,27108,27109],{"class":55,"line":496},[53,27110,282],{},[53,27112,27113],{"class":55,"line":503},[53,27114,27115],{},".content {\n",[53,27117,27118],{"class":55,"line":509},[53,27119,27120],{}," -webkit-flex: 3;\n",[53,27122,27123],{"class":55,"line":515},[53,27124,27125],{}," flex: 3;\n",[53,27127,27128],{"class":55,"line":521},[53,27129,282],{},[53,27131,27132],{"class":55,"line":527},[53,27133,27134],{},".sidenote {\n",[53,27136,27137],{"class":55,"line":533},[53,27138,27139],{}," padding: 1em;\n",[53,27141,27142],{"class":55,"line":539},[53,27143,27096],{},[53,27145,27146],{"class":55,"line":545},[53,27147,27148],{}," -webkit-flex: 2;\n",[53,27150,27151],{"class":55,"line":2070},[53,27152,27153],{}," flex: 2;\n",[53,27155,27156],{"class":55,"line":2075},[53,27157,282],{},[53,27159,27160],{"class":55,"line":2081},[53,27161,27162],{},".menu > ul {\n",[53,27164,27165],{"class":55,"line":2087},[53,27166,27167],{}," margin: 1em;\n",[53,27169,27170],{"class":55,"line":2092},[53,27171,27172],{}," list-style-type: none;\n",[53,27174,27175],{"class":55,"line":2097},[53,27176,27177],{}," white-space: nowrap;\n",[53,27179,27180],{"class":55,"line":2103},[53,27181,282],{},[53,27183,27184],{"class":55,"line":2109},[53,27185,27186],{},".menu a {\n",[53,27188,27189],{"class":55,"line":2115},[53,27190,27191],{}," color: black;\n",[53,27193,27194],{"class":55,"line":2120},[53,27195,282],{},[53,27197,27198],{"class":55,"line":2946},[53,27199,27200],{},".content article {\n",[53,27202,27203],{"class":55,"line":2952},[53,27204,27167],{},[53,27206,27207],{"class":55,"line":2964},[53,27208,282],{},[53,27210,27211],{"class":55,"line":2973},[53,27212,27213],{},"* {\n",[53,27215,27216],{"class":55,"line":2979},[53,27217,27218],{}," padding: 0;\n",[53,27220,27221],{"class":55,"line":2990},[53,27222,27223],{}," margin: 0;\n",[53,27225,27226],{"class":55,"line":10443},[53,27227,282],{},[18,27229,27230,27231,27234,27235,27238,27239,27242,27243,27246,27247,27249,27250,27253],{},"The value of the ",[50,27232,27233],{},"flex"," property tells the browser how much space the section should fill of the available place. In our\nexample the ",[573,27236,27237],{},".main-nav"," is the smallest, followed by ",[573,27240,27241],{},".side-note"," which is twice as big and by ",[573,27244,27245],{},".content"," which is\nthree times as big as the ",[573,27248,27237],{},". The children of ",[573,27251,27252],{},".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.",[18,27255,27256,27257,27260,27261,27264,27265,27267,27268,986],{},"Now what if you want a static width for the ",[573,27258,27259],{},".main-menu",". Well, just remove ",[50,27262,27263],{},"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",[50,27266,27233],{}," value. If you’re interested to dive deaper into the amazing flexbox layout I recommend the article\non ",[585,27269,27273],{"href":27270,"rel":27271,"title":27272},"http://css-tricks.com/snippets/css/a-guide-to-flexbox/",[589],"css-tricks.com | flexbox","css-tricks.com",[18,27275,27276],{},"Impressed so far? We’re just getting started with CSS 3!",[2352,27278,27280],{"id":27279},"media","@media",[18,27282,27283,27284,27287,27288,27294],{},"Nowadays we developers ",[13401,27285,27286],{},"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 ",[585,27289,27293],{"href":27290,"rel":27291,"title":27292},"https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries",[589],"MDN | Media Queries","CSS 3 Media Queries","\neven now.",[18,27296,27297,27298,27300],{},"In the codepen below the ",[50,27299,27280],{}," 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.",[43,27302,27304],{"className":13667,"code":27303,"language":13669,"meta":48,"style":48},".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",[50,27305,27306,27310,27314,27318,27322,27326,27330,27334,27338,27342,27347,27352,27356,27360,27364,27368,27372,27376,27380,27384,27388,27392,27396,27400,27404,27408,27412,27416,27420,27424,27428,27432,27436,27440,27445,27450,27455,27459,27463,27468,27473,27478,27483,27488,27494,27499,27505,27511,27517,27523,27528,27534,27540,27546,27552,27557,27563,27569,27574,27579,27584,27589,27594],{"__ignoreMap":48},[53,27307,27308],{"class":55,"line":56},[53,27309,27067],{},[53,27311,27312],{"class":55,"line":86},[53,27313,27072],{},[53,27315,27316],{"class":55,"line":126},[53,27317,27077],{},[53,27319,27320],{"class":55,"line":163},[53,27321,282],{},[53,27323,27324],{"class":55,"line":186},[53,27325,27086],{},[53,27327,27328],{"class":55,"line":221},[53,27329,27091],{},[53,27331,27332],{"class":55,"line":242},[53,27333,27096],{},[53,27335,27336],{"class":55,"line":273},[53,27337,27101],{},[53,27339,27340],{"class":55,"line":279},[53,27341,27106],{},[53,27343,27344],{"class":55,"line":496},[53,27345,27346],{}," -webkit-transition: -webkit-flex 1s;\n",[53,27348,27349],{"class":55,"line":503},[53,27350,27351],{}," transition: flex 1s;\n",[53,27353,27354],{"class":55,"line":509},[53,27355,282],{},[53,27357,27358],{"class":55,"line":515},[53,27359,27115],{},[53,27361,27362],{"class":55,"line":521},[53,27363,27120],{},[53,27365,27366],{"class":55,"line":527},[53,27367,27125],{},[53,27369,27370],{"class":55,"line":533},[53,27371,282],{},[53,27373,27374],{"class":55,"line":539},[53,27375,27134],{},[53,27377,27378],{"class":55,"line":545},[53,27379,27139],{},[53,27381,27382],{"class":55,"line":2070},[53,27383,27096],{},[53,27385,27386],{"class":55,"line":2075},[53,27387,27148],{},[53,27389,27390],{"class":55,"line":2081},[53,27391,27153],{},[53,27393,27394],{"class":55,"line":2087},[53,27395,282],{},[53,27397,27398],{"class":55,"line":2092},[53,27399,27162],{},[53,27401,27402],{"class":55,"line":2097},[53,27403,27167],{},[53,27405,27406],{"class":55,"line":2103},[53,27407,27172],{},[53,27409,27410],{"class":55,"line":2109},[53,27411,27177],{},[53,27413,27414],{"class":55,"line":2115},[53,27415,282],{},[53,27417,27418],{"class":55,"line":2120},[53,27419,27186],{},[53,27421,27422],{"class":55,"line":2946},[53,27423,27191],{},[53,27425,27426],{"class":55,"line":2952},[53,27427,282],{},[53,27429,27430],{"class":55,"line":2964},[53,27431,27200],{},[53,27433,27434],{"class":55,"line":2973},[53,27435,27167],{},[53,27437,27438],{"class":55,"line":2979},[53,27439,282],{},[53,27441,27442],{"class":55,"line":2990},[53,27443,27444],{},"@media (max-width: 800px) {\n",[53,27446,27447],{"class":55,"line":10443},[53,27448,27449],{}," .sidenote {\n",[53,27451,27452],{"class":55,"line":26443},[53,27453,27454],{}," display: none;\n",[53,27456,27457],{"class":55,"line":26448},[53,27458,7109],{},[53,27460,27461],{"class":55,"line":26453},[53,27462,282],{},[53,27464,27465],{"class":55,"line":26458},[53,27466,27467],{},"@media (max-width: 400px) {\n",[53,27469,27470],{"class":55,"line":26463},[53,27471,27472],{}," .menu {\n",[53,27474,27475],{"class":55,"line":26468},[53,27476,27477],{}," position: fixed;\n",[53,27479,27480],{"class":55,"line":26473},[53,27481,27482],{}," top: 0;\n",[53,27484,27485],{"class":55,"line":26478},[53,27486,27487],{}," left: 0;\n",[53,27489,27491],{"class":55,"line":27490},44,[53,27492,27493],{}," right: 0;\n",[53,27495,27497],{"class":55,"line":27496},45,[53,27498,7109],{},[53,27500,27502],{"class":55,"line":27501},46,[53,27503,27504],{}," .menu > ul {\n",[53,27506,27508],{"class":55,"line":27507},47,[53,27509,27510],{}," display: -webkit-flex;\n",[53,27512,27514],{"class":55,"line":27513},48,[53,27515,27516],{}," display: -moz-box;\n",[53,27518,27520],{"class":55,"line":27519},49,[53,27521,27522],{}," display: flex;\n",[53,27524,27526],{"class":55,"line":27525},50,[53,27527,7109],{},[53,27529,27531],{"class":55,"line":27530},51,[53,27532,27533],{}," .menu > ul > li {\n",[53,27535,27537],{"class":55,"line":27536},52,[53,27538,27539],{}," padding: 0 1em;\n",[53,27541,27543],{"class":55,"line":27542},53,[53,27544,27545],{}," -webkit-flex: 1;\n",[53,27547,27549],{"class":55,"line":27548},54,[53,27550,27551],{}," flex: 1;\n",[53,27553,27555],{"class":55,"line":27554},55,[53,27556,7109],{},[53,27558,27560],{"class":55,"line":27559},56,[53,27561,27562],{}," .content {\n",[53,27564,27566],{"class":55,"line":27565},57,[53,27567,27568],{}," padding-top: 3em\n",[53,27570,27572],{"class":55,"line":27571},58,[53,27573,7109],{},[53,27575,27577],{"class":55,"line":27576},59,[53,27578,282],{},[53,27580,27582],{"class":55,"line":27581},60,[53,27583,27213],{},[53,27585,27587],{"class":55,"line":27586},61,[53,27588,27218],{},[53,27590,27592],{"class":55,"line":27591},62,[53,27593,27223],{},[53,27595,27597],{"class":55,"line":27596},63,[53,27598,282],{},[18,27600,27601,27602,27604],{},"A more complex use case could be the alignment of the navigation dependent of the screen size. With ",[50,27603,27280],{}," 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.",[18,27606,27607,27608,27610,27611,986],{},"There are a lot more ",[50,27609,27280],{}," properties and possibilities that can be best read\non ",[585,27612,27273],{"href":27613,"rel":27614,"title":27615},"http://css-tricks.com/css-media-queries/",[589],"css-tricks | Media Queries",[2352,27617,27618],{"id":27618},"transitions",[18,27620,27621],{},"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.",[18,27623,27624],{},"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!",[18,27626,27627,27628,27631,27632,27635],{},"Due to simple adding or removing the class ",[573,27629,27630],{},".hidden"," we can change the width of the menu container. The ",[50,27633,27634],{},"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.",[18,27637,27638,27639,27644],{},"As well as the above mentioned CSS 3 features, transitions are much more powerful\nand ",[585,27640,27273],{"href":27641,"rel":27642,"title":27643},"http://css-tricks.com/search-results/?q=transition",[589],"css-tricks.com | transition"," is a nice source\nto learn more and to play with advanced examples.",[18,27646,27647],{},"Thanks for reading! And hopefully we can enjoy the full power of CSS 3 as soon as possible throughout all modern\nbrowsers (even IE…).",[607,27649,989],{},{"title":48,"searchDepth":86,"depth":86,"links":27651},[27652,27653,27654],{"id":27051,"depth":86,"text":27042},{"id":27279,"depth":86,"text":27280},{"id":27618,"depth":86,"text":27618},[613],"2013-06-26T11:51:48","https://synyx.de/blog/awesome-css-3-layouting/",{},"/blog/awesome-css-3-layouting",{"title":26993,"description":27002},"blog/awesome-css-3-layouting",[27663],"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…","Y2DiBD_KnuEqdChUKF_H_3Qaxz2XX93qcAd5uc647dY",{"id":27667,"title":27668,"author":27669,"body":27671,"category":28627,"date":28628,"description":28629,"extension":617,"link":28630,"meta":28631,"navigation":499,"path":28632,"seo":28633,"slug":28635,"stem":28636,"tags":28637,"teaser":28639,"__hash__":28640},"blog/blog/asynchronous-concurrency-with-vert-x-part-2.md","Asynchronous concurrency with vert.x – Part 2",[27670],"allmendinger",{"type":11,"value":27672,"toc":28625},[27673,27676,27691,27705,27708,27731,27734,27758,27761,27824,27827,27835,27838,27853,27860,28126,28129,28132,28361,28364,28393,28400,28468,28471,28550,28561,28600,28603,28621,28623],[14,27674,27668],{"id":27675},"asynchronous-concurrency-with-vertx-part-2",[18,27677,27678,27679,27684,27685,27690],{},"CoffeeScript\nVert.x supports JavaScript through the ",[585,27680,27683],{"href":27681,"rel":27682},"https://developer.mozilla.org/en/docs/Rhino",[589],"Rhino JavaScript engine",". Although\nJavaScript is a decent language once you get to know it, I prefer ",[585,27686,27689],{"href":27687,"rel":27688},"http://www.coffeescript.org",[589],"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:",[43,27692,27694],{"className":13667,"code":27693,"language":13669,"meta":48,"style":48},"\nfoo = (a, b) -> a + b\n\n",[50,27695,27696,27700],{"__ignoreMap":48},[53,27697,27698],{"class":55,"line":56},[53,27699,500],{"emptyLinePlaceholder":499},[53,27701,27702],{"class":55,"line":86},[53,27703,27704],{},"foo = (a, b) -> a + b\n",[18,27706,27707],{},"Translates to the JavaScript code",[43,27709,27711],{"className":13667,"code":27710,"language":13669,"meta":48,"style":48},"\nvar foo = function (a, b) {\n return a + b; // (the last statement is returned)\n}\n\n",[50,27712,27713,27717,27722,27727],{"__ignoreMap":48},[53,27714,27715],{"class":55,"line":56},[53,27716,500],{"emptyLinePlaceholder":499},[53,27718,27719],{"class":55,"line":86},[53,27720,27721],{},"var foo = function (a, b) {\n",[53,27723,27724],{"class":55,"line":126},[53,27725,27726],{}," return a + b; // (the last statement is returned)\n",[53,27728,27729],{"class":55,"line":163},[53,27730,282],{},[18,27732,27733],{},"Also parentheses around function arguments are optional",[43,27735,27737],{"className":13667,"code":27736,"language":13669,"meta":48,"style":48},"\n foo a, b, c\n # same as\n foo(a, b, c)\n\n",[50,27738,27739,27743,27748,27753],{"__ignoreMap":48},[53,27740,27741],{"class":55,"line":56},[53,27742,500],{"emptyLinePlaceholder":499},[53,27744,27745],{"class":55,"line":86},[53,27746,27747],{}," foo a, b, c\n",[53,27749,27750],{"class":55,"line":126},[53,27751,27752],{}," # same as\n",[53,27754,27755],{"class":55,"line":163},[53,27756,27757],{}," foo(a, b, c)\n",[18,27759,27760],{},"The translated source code from the example described in the last post is",[43,27762,27764],{"className":13667,"code":27763,"language":13669,"meta":48,"style":48},"\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",[50,27765,27766,27770,27775,27780,27785,27790,27795,27800,27805,27810,27815,27820],{"__ignoreMap":48},[53,27767,27768],{"class":55,"line":56},[53,27769,500],{"emptyLinePlaceholder":499},[53,27771,27772],{"class":55,"line":86},[53,27773,27774],{},"vertx = require 'vertx'\n",[53,27776,27777],{"class":55,"line":126},[53,27778,27779],{},"address = 'example.address'\n",[53,27781,27782],{"class":55,"line":163},[53,27783,27784],{},"handler = (message, replier) ->\n",[53,27786,27787],{"class":55,"line":186},[53,27788,27789],{}," stdout.println \"sender sent \" + message\n",[53,27791,27792],{"class":55,"line":221},[53,27793,27794],{}," replier \"pong 1\", (message, replier) ->\n",[53,27796,27797],{"class":55,"line":242},[53,27798,27799],{}," # and so on\n",[53,27801,27802],{"class":55,"line":273},[53,27803,27804],{},"vertx.eventBus.registerHandler address, handler\n",[53,27806,27807],{"class":55,"line":279},[53,27808,27809],{},"vertx.eventBus.send address, \"ping 1\", (message, replier) ->\n",[53,27811,27812],{"class":55,"line":496},[53,27813,27814],{}," stdout.println \"handler sent \" + message\n",[53,27816,27817],{"class":55,"line":503},[53,27818,27819],{}," replier \"ping 2\", (message, replier) ->\n",[53,27821,27822],{"class":55,"line":509},[53,27823,27799],{},[18,27825,27826],{},"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.",[18,27828,27829,27830,2304],{},"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 ",[585,27831,27834],{"href":27832,"rel":27833},"http://en.wikipedia.org/wiki/Sleeping_barber_problem",[589],"Sleeping Barber Problem",[18,27836,27837],{},"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.",[18,27839,27840,27841,27846,27847,27852],{},"I’ve ",[585,27842,27845],{"href":27843,"rel":27844},"https://github.com/OttoAllmendinger/term-paper-stm",[589],"previously solved this problem","\nusing ",[585,27848,27851],{"href":27849,"rel":27850},"http://en.wikipedia.org/wiki/Software_transactional_memory",[589],"Software Transactional Memory"," and was interested how\nthe message-passing style of vert.x compares.",[18,27854,27855,27856,27859],{},"Barber.coffee\nThe barber shop problem nicely separates into two systems: a ",[50,27857,27858],{},"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.",[43,27861,27863],{"className":13667,"code":27862,"language":13669,"meta":48,"style":48},"\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",[50,27864,27865,27869,27873,27878,27883,27888,27893,27898,27903,27908,27913,27918,27923,27928,27933,27938,27943,27948,27953,27958,27963,27967,27972,27977,27982,27987,27992,27997,28002,28007,28012,28017,28022,28027,28032,28037,28042,28047,28052,28057,28062,28067,28072,28077,28082,28087,28092,28097,28102,28107,28111,28116,28121],{"__ignoreMap":48},[53,27866,27867],{"class":55,"line":56},[53,27868,500],{"emptyLinePlaceholder":499},[53,27870,27871],{"class":55,"line":86},[53,27872,27774],{},[53,27874,27875],{"class":55,"line":126},[53,27876,27877],{},"addr = 'barber'\n",[53,27879,27880],{"class":55,"line":163},[53,27881,27882],{},"waitTime = -> Math.random() * 100\n",[53,27884,27885],{"class":55,"line":186},[53,27886,27887],{},"barber = ->\n",[53,27889,27890],{"class":55,"line":221},[53,27891,27892],{}," # the state of the message handler lives\n",[53,27894,27895],{"class":55,"line":242},[53,27896,27897],{}," # in this closure\n",[53,27899,27900],{"class":55,"line":273},[53,27901,27902],{}," busy = false\n",[53,27904,27905],{"class":55,"line":279},[53,27906,27907],{}," queue = []\n",[53,27909,27910],{"class":55,"line":496},[53,27911,27912],{}," freeSeats = 20\n",[53,27914,27915],{"class":55,"line":503},[53,27916,27917],{}," # make the system a little indeterministic\n",[53,27919,27920],{"class":55,"line":509},[53,27921,27922],{}," log = (message) ->\n",[53,27924,27925],{"class":55,"line":515},[53,27926,27927],{}," stdout.println \"barber: #{message}\"\n",[53,27929,27930],{"class":55,"line":521},[53,27931,27932],{}," # the following methods define the core behavior\n",[53,27934,27935],{"class":55,"line":527},[53,27936,27937],{}," checkQueue = ->\n",[53,27939,27940],{"class":55,"line":533},[53,27941,27942],{}," if queue.length > 0\n",[53,27944,27945],{"class":55,"line":539},[53,27946,27947],{}," serveCustomer queue.shift()\n",[53,27949,27950],{"class":55,"line":545},[53,27951,27952],{}," freeSeats += 1\n",[53,27954,27955],{"class":55,"line":2070},[53,27956,27957],{}," return true\n",[53,27959,27960],{"class":55,"line":2075},[53,27961,27962],{}," else\n",[53,27964,27965],{"class":55,"line":2081},[53,27966,25172],{},[53,27968,27969],{"class":55,"line":2087},[53,27970,27971],{}," serveCustomer = ({customer, replier}) ->\n",[53,27973,27974],{"class":55,"line":2092},[53,27975,27976],{}," log \"serving #{customer}\"\n",[53,27978,27979],{"class":55,"line":2097},[53,27980,27981],{}," busy = true\n",[53,27983,27984],{"class":55,"line":2103},[53,27985,27986],{}," replier 'serve', (message, replier) ->\n",[53,27988,27989],{"class":55,"line":2109},[53,27990,27991],{}," vertx.setTimer waitTime(), ->\n",[53,27993,27994],{"class":55,"line":2115},[53,27995,27996],{}," log \"done serving #{customer}\"\n",[53,27998,27999],{"class":55,"line":2120},[53,28000,28001],{}," busy = checkQueue()\n",[53,28003,28004],{"class":55,"line":2946},[53,28005,28006],{}," replier 'done'\n",[53,28008,28009],{"class":55,"line":2952},[53,28010,28011],{}," # this is the handler's callback method that\n",[53,28013,28014],{"class":55,"line":2964},[53,28015,28016],{}," # is being returned by the barber function\n",[53,28018,28019],{"class":55,"line":2973},[53,28020,28021],{}," (message, replier) ->\n",[53,28023,28024],{"class":55,"line":2979},[53,28025,28026],{}," customer = message\n",[53,28028,28029],{"class":55,"line":2990},[53,28030,28031],{}," if busy\n",[53,28033,28034],{"class":55,"line":10443},[53,28035,28036],{}," # there is an intermediate state where we know that we\n",[53,28038,28039],{"class":55,"line":26443},[53,28040,28041],{}," # have to queue the customer because there aren't any\n",[53,28043,28044],{"class":55,"line":26448},[53,28045,28046],{}," # free seats, but the customer must first acknowledge\n",[53,28048,28049],{"class":55,"line":26453},[53,28050,28051],{}," # the waiting state before we can actually put him in\n",[53,28053,28054],{"class":55,"line":26458},[53,28055,28056],{}," # the queue.\n",[53,28058,28059],{"class":55,"line":26463},[53,28060,28061],{}," if freeSeats > 0\n",[53,28063,28064],{"class":55,"line":26468},[53,28065,28066],{}," freeSeats -= 1\n",[53,28068,28069],{"class":55,"line":26473},[53,28070,28071],{}," log \"sending #{customer} to queue\"\n",[53,28073,28074],{"class":55,"line":26478},[53,28075,28076],{}," replier 'busy', (message, replier) ->\n",[53,28078,28079],{"class":55,"line":27490},[53,28080,28081],{}," # customer waiting ack\n",[53,28083,28084],{"class":55,"line":27496},[53,28085,28086],{}," queue.push {customer, replier}\n",[53,28088,28089],{"class":55,"line":27501},[53,28090,28091],{}," log \"queued #{customer} - \" +\n",[53,28093,28094],{"class":55,"line":27507},[53,28095,28096],{}," \"length: #{queue.length} - free seats: #{freeSeats}\"\n",[53,28098,28099],{"class":55,"line":27513},[53,28100,28101],{}," else\n",[53,28103,28104],{"class":55,"line":27519},[53,28105,28106],{}," replier 'full'\n",[53,28108,28109],{"class":55,"line":27525},[53,28110,27962],{},[53,28112,28113],{"class":55,"line":27530},[53,28114,28115],{}," serveCustomer {customer, replier}\n",[53,28117,28118],{"class":55,"line":27536},[53,28119,28120],{},"exports.start = ->\n",[53,28122,28123],{"class":55,"line":27542},[53,28124,28125],{}," vertx.eventBus.registerHandler addr, barber()\n",[18,28127,28128],{},"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.",[18,28130,28131],{},"Customer.coffee\nLet’s define the behavior of the customer in a separate file",[43,28133,28135],{"className":13667,"code":28134,"language":13669,"meta":48,"style":48},"\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",[50,28136,28137,28141,28145,28149,28153,28158,28163,28168,28173,28178,28183,28188,28192,28197,28202,28207,28212,28217,28222,28227,28232,28237,28242,28247,28252,28257,28262,28267,28272,28277,28282,28287,28292,28297,28302,28307,28312,28317,28322,28327,28332,28337,28342,28347,28352,28356],{"__ignoreMap":48},[53,28138,28139],{"class":55,"line":56},[53,28140,500],{"emptyLinePlaceholder":499},[53,28142,28143],{"class":55,"line":86},[53,28144,27774],{},[53,28146,28147],{"class":55,"line":126},[53,28148,27877],{},[53,28150,28151],{"class":55,"line":163},[53,28152,27882],{},[53,28154,28155],{"class":55,"line":186},[53,28156,28157],{},"sendCustomer = (i) ->\n",[53,28159,28160],{"class":55,"line":221},[53,28161,28162],{}," # As with the barber, the customer's state is\n",[53,28164,28165],{"class":55,"line":242},[53,28166,28167],{}," # defined in this closure. The variables will\n",[53,28169,28170],{"class":55,"line":273},[53,28171,28172],{}," # be modified by the callback methods that are\n",[53,28174,28175],{"class":55,"line":279},[53,28176,28177],{}," # triggered by the message handler's replies.\n",[53,28179,28180],{"class":55,"line":496},[53,28181,28182],{}," waiting = false\n",[53,28184,28185],{"class":55,"line":503},[53,28186,28187],{}," beingServed = false\n",[53,28189,28190],{"class":55,"line":509},[53,28191,27922],{},[53,28193,28194],{"class":55,"line":515},[53,28195,28196],{}," stdout.println \"customer #{i}: #{message}\"\n",[53,28198,28199],{"class":55,"line":521},[53,28200,28201],{}," # just a shorthand\n",[53,28203,28204],{"class":55,"line":527},[53,28205,28206],{}," send = (message, callback) ->\n",[53,28208,28209],{"class":55,"line":533},[53,28210,28211],{}," vertx.eventBus.send addr, message, callback\n",[53,28213,28214],{"class":55,"line":539},[53,28215,28216],{}," # factor out the exit method:\n",[53,28218,28219],{"class":55,"line":545},[53,28220,28221],{}," # a customer can exit after having been served\n",[53,28223,28224],{"class":55,"line":2070},[53,28225,28226],{}," # or when the queue is full\n",[53,28228,28229],{"class":55,"line":2075},[53,28230,28231],{}," exit = (message) ->\n",[53,28233,28234],{"class":55,"line":2081},[53,28235,28236],{}," log message + \" - exiting\"\n",[53,28238,28239],{"class":55,"line":2087},[53,28240,28241],{}," # this method doesn't send a response\n",[53,28243,28244],{"class":55,"line":2092},[53,28245,28246],{}," # via the replier\n",[53,28248,28249],{"class":55,"line":2097},[53,28250,28251],{}," getHaircut = (message, replier) ->\n",[53,28253,28254],{"class":55,"line":2103},[53,28255,28256],{}," waiting = false\n",[53,28258,28259],{"class":55,"line":2109},[53,28260,28261],{}," beingServed = true\n",[53,28263,28264],{"class":55,"line":2115},[53,28265,28266],{}," log \"being served\"\n",[53,28268,28269],{"class":55,"line":2120},[53,28270,28271],{}," replier 'being-served', exit\n",[53,28273,28274],{"class":55,"line":2946},[53,28275,28276],{}," log \"enters\"\n",[53,28278,28279],{"class":55,"line":2952},[53,28280,28281],{}," send \"customer #{i}\", (message, replier) ->\n",[53,28283,28284],{"class":55,"line":2964},[53,28285,28286],{}," switch message\n",[53,28288,28289],{"class":55,"line":2973},[53,28290,28291],{}," when 'busy'\n",[53,28293,28294],{"class":55,"line":2979},[53,28295,28296],{}," waiting = true\n",[53,28298,28299],{"class":55,"line":2990},[53,28300,28301],{}," log 'waiting'\n",[53,28303,28304],{"class":55,"line":10443},[53,28305,28306],{}," replier 'waiting', getHaircut\n",[53,28308,28309],{"class":55,"line":26443},[53,28310,28311],{}," when 'serve'\n",[53,28313,28314],{"class":55,"line":26448},[53,28315,28316],{}," getHaircut message, replier\n",[53,28318,28319],{"class":55,"line":26453},[53,28320,28321],{}," when 'full'\n",[53,28323,28324],{"class":55,"line":26458},[53,28325,28326],{}," exit message\n",[53,28328,28329],{"class":55,"line":26463},[53,28330,28331],{},"# a loop that continuously sends customers\n",[53,28333,28334],{"class":55,"line":26468},[53,28335,28336],{},"# to the barber\n",[53,28338,28339],{"class":55,"line":26473},[53,28340,28341],{},"sendCustomerLoop = (i) ->\n",[53,28343,28344],{"class":55,"line":26478},[53,28345,28346],{}," sendCustomer i\n",[53,28348,28349],{"class":55,"line":27490},[53,28350,28351],{}," vertx.setTimer waitTime(), -> sendCustomerLoop i + 1\n",[53,28353,28354],{"class":55,"line":27496},[53,28355,28120],{},[53,28357,28358],{"class":55,"line":27501},[53,28359,28360],{}," sendCustomerLoop 1\n",[18,28362,28363],{},"barbershop.coffee\nThis time, we want to run both handler and sender in the same process, for easier testing.",[43,28365,28367],{"className":13667,"code":28366,"language":13669,"meta":48,"style":48},"\nbarber = require 'barber'\ncustomer = require 'customer'\nbarber.start()\ncustomer.start()\n\n",[50,28368,28369,28373,28378,28383,28388],{"__ignoreMap":48},[53,28370,28371],{"class":55,"line":56},[53,28372,500],{"emptyLinePlaceholder":499},[53,28374,28375],{"class":55,"line":86},[53,28376,28377],{},"barber = require 'barber'\n",[53,28379,28380],{"class":55,"line":126},[53,28381,28382],{},"customer = require 'customer'\n",[53,28384,28385],{"class":55,"line":163},[53,28386,28387],{},"barber.start()\n",[53,28389,28390],{"class":55,"line":186},[53,28391,28392],{},"customer.start()\n",[18,28394,28395,28396,28399],{},"Running the shop\nWhen we start the ",[50,28397,28398],{},"barbershop.coffee"," script, we can see in the log that the shop is running as it is supposed to:",[43,28401,28403],{"className":13667,"code":28402,"language":13669,"meta":48,"style":48},"\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",[50,28404,28405,28409,28414,28419,28424,28429,28434,28439,28444,28449,28454,28459,28464],{"__ignoreMap":48},[53,28406,28407],{"class":55,"line":56},[53,28408,500],{"emptyLinePlaceholder":499},[53,28410,28411],{"class":55,"line":86},[53,28412,28413],{},"customer 1: enters\n",[53,28415,28416],{"class":55,"line":126},[53,28417,28418],{},"barber: serving customer 1\n",[53,28420,28421],{"class":55,"line":163},[53,28422,28423],{},"customer 1: being served\n",[53,28425,28426],{"class":55,"line":186},[53,28427,28428],{},"barber: done serving customer 1\n",[53,28430,28431],{"class":55,"line":221},[53,28432,28433],{},"customer 1: done - exiting\n",[53,28435,28436],{"class":55,"line":242},[53,28437,28438],{},"customer 2: enters\n",[53,28440,28441],{"class":55,"line":273},[53,28442,28443],{},"barber: serving customer 2\n",[53,28445,28446],{"class":55,"line":279},[53,28447,28448],{},"customer 2: being served\n",[53,28450,28451],{"class":55,"line":496},[53,28452,28453],{},"barber: done serving customer 2\n",[53,28455,28456],{"class":55,"line":503},[53,28457,28458],{},"customer 2: done - exiting\n",[53,28460,28461],{"class":55,"line":509},[53,28462,28463],{},"customer 3: enters\n",[53,28465,28466],{"class":55,"line":515},[53,28467,3778],{},[18,28469,28470],{},"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:",[43,28472,28474],{"className":13667,"code":28473,"language":13669,"meta":48,"style":48},"\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",[50,28475,28476,28480,28485,28490,28495,28500,28505,28510,28515,28520,28525,28530,28535,28540,28545],{"__ignoreMap":48},[53,28477,28478],{"class":55,"line":56},[53,28479,500],{"emptyLinePlaceholder":499},[53,28481,28482],{"class":55,"line":86},[53,28483,28484],{},"barber: serving customer 3\n",[53,28486,28487],{"class":55,"line":126},[53,28488,28489],{},"customer 3: being served\n",[53,28491,28492],{"class":55,"line":163},[53,28493,28494],{},"customer 4: enters\n",[53,28496,28497],{"class":55,"line":186},[53,28498,28499],{},"barber: sending customer 4 to queue\n",[53,28501,28502],{"class":55,"line":221},[53,28503,28504],{},"customer 4: waiting\n",[53,28506,28507],{"class":55,"line":242},[53,28508,28509],{},"barber: queued customer 4 - length: 1 - free seats: 19\n",[53,28511,28512],{"class":55,"line":273},[53,28513,28514],{},"customer 5: enters\n",[53,28516,28517],{"class":55,"line":279},[53,28518,28519],{},"barber: sending customer 5 to queue\n",[53,28521,28522],{"class":55,"line":496},[53,28523,28524],{},"customer 5: waiting\n",[53,28526,28527],{"class":55,"line":503},[53,28528,28529],{},"barber: queued customer 5 - length: 2 - free seats: 18\n",[53,28531,28532],{"class":55,"line":509},[53,28533,28534],{},"barber: done serving customer 3\n",[53,28536,28537],{"class":55,"line":515},[53,28538,28539],{},"barber: serving customer 4\n",[53,28541,28542],{"class":55,"line":521},[53,28543,28544],{},"customer 3: done - exiting\n",[53,28546,28547],{"class":55,"line":527},[53,28548,28549],{},"customer 4: being served\n",[18,28551,28552,28553,28556,28557,28560],{},"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 ",[50,28554,28555],{},"waitTime = -> Math.random() * 80"," in ",[50,28558,28559],{},"customer.coffee"," so that there are a few more customers\nentering than leaving.",[43,28562,28564],{"className":13667,"code":28563,"language":13669,"meta":48,"style":48},"\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",[50,28565,28566,28570,28575,28580,28585,28590,28595],{"__ignoreMap":48},[53,28567,28568],{"class":55,"line":56},[53,28569,500],{"emptyLinePlaceholder":499},[53,28571,28572],{"class":55,"line":86},[53,28573,28574],{},"customer 34: enters\n",[53,28576,28577],{"class":55,"line":126},[53,28578,28579],{},"barber: sending customer 34 to queue\n",[53,28581,28582],{"class":55,"line":163},[53,28583,28584],{},"customer 34: waiting\n",[53,28586,28587],{"class":55,"line":186},[53,28588,28589],{},"barber: queued customer 34 - length: 20 - free seats: 0\n",[53,28591,28592],{"class":55,"line":221},[53,28593,28594],{},"customer 35: enters\n",[53,28596,28597],{"class":55,"line":242},[53,28598,28599],{},"customer 35: full - exiting\n",[18,28601,28602],{},"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.",[18,28604,28605,28606,10757,28609,28612,28613,28616,28617,28620],{},"Conclusion\nThe central primitive is the construct ",[50,28607,28608],{},"replier(send_message, next_state)",[50,28610,28611],{},"replier"," triggers a state transition in\nthe remote system through ",[50,28614,28615],{},"send_message"," and defines the local ",[50,28618,28619],{},"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.",[16670,28622],{},[607,28624,989],{},{"title":48,"searchDepth":86,"depth":86,"links":28626},[],[613,996],"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":27668,"description":28634},"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",[28638,16644,6991,24035],"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":28642,"title":28643,"author":28644,"body":28645,"category":28823,"date":28824,"description":28825,"extension":617,"link":28826,"meta":28827,"navigation":499,"path":28828,"seo":28829,"slug":28655,"stem":28830,"tags":28831,"teaser":28836,"__hash__":28837},"blog/blog/what-is-an-acceptance-test.md","Acceptance testing at synyx – Part 5",[11420],{"type":11,"value":28646,"toc":28820},[28647,28650,28653,28657,28686,28689,28692,28763,28770,28777,28788,28812,28815,28818],[14,28648,28643],{"id":28649},"acceptance-testing-at-synyx-part-5",[18,28651,28652],{},"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.",[2352,28654,28656],{"id":28655},"what-is-an-acceptance-test","What is an Acceptance Test?",[18,28658,28659,28660,28665,28666,28671,28672,28675,28676,28679,28680,28685],{},"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 ",[585,28661,28664],{"href":28662,"rel":28663},"http://en.wikipedia.org/wiki/User_story",[589],"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 ",[585,28667,28670],{"href":28668,"rel":28669},"http://scrummethodology.com/scrum-acceptance-criteria/",[589],"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 ",[573,28673,28674],{},"“A shop-item can be added to the shopping cart”"," you probably can define acceptance criterias\npretty easy. One of them could be ",[573,28677,28678],{},"“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 ",[585,28681,28684],{"href":28682,"rel":28683},"https://web.archive.org/web/20170331070234if_/http://cdn.memegenerator.net/instances/400x/24216149.jpg",[589],"QA",". But\nit’s preferable if these criteria are tested automatically somewhere in the build pipeline.",[18,28687,28688],{},"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.",[18,28690,28691],{},"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”:",[43,28693,28695],{"className":288,"code":28694,"language":290,"meta":48,"style":48},"\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",[50,28696,28697,28701,28706,28711,28716,28721,28726,28731,28735,28740,28745,28749,28754,28759],{"__ignoreMap":48},[53,28698,28699],{"class":55,"line":56},[53,28700,500],{"emptyLinePlaceholder":499},[53,28702,28703],{"class":55,"line":86},[53,28704,28705],{},"@Test\n",[53,28707,28708],{"class":55,"line":126},[53,28709,28710],{},"public void addsAvailableItemsToCart() {\n",[53,28712,28713],{"class":55,"line":163},[53,28714,28715],{}," Item item = new Item(\"synyx coffee mug\");\n",[53,28717,28718],{"class":55,"line":186},[53,28719,28720],{}," item.setAvailable(true);\n",[53,28722,28723],{"class":55,"line":221},[53,28724,28725],{}," cart.add(item);\n",[53,28727,28728],{"class":55,"line":242},[53,28729,28730],{}," Assert.assertThat(cart.getItems(), containsItem(item));\n",[53,28732,28733],{"class":55,"line":273},[53,28734,282],{},[53,28736,28737],{"class":55,"line":279},[53,28738,28739],{},"@Test(expected = ItemUnavailableException.class)\n",[53,28741,28742],{"class":55,"line":496},[53,28743,28744],{},"public void doesNotAddUnavailableItemsToCart() {\n",[53,28746,28747],{"class":55,"line":503},[53,28748,28715],{},[53,28750,28751],{"class":55,"line":509},[53,28752,28753],{}," item.setAvailable(false);\n",[53,28755,28756],{"class":55,"line":515},[53,28757,28758],{}," cart.add(item); // exception expected\n",[53,28760,28761],{"class":55,"line":521},[53,28762,282],{},[18,28764,28765,28766,28769],{},"As you can see the two test methods above could be a way to verify the criteria of the story ",[573,28767,28768],{},"“A shop-item can be added\nto the shopping cart”"," I described above.",[18,28771,28772,28773,28776],{},"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 ",[50,28774,28775],{},"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.",[18,28778,28779,28780,28783,28784,28787],{},"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 ",[27,28781,28782],{},"what"," the\nuser wants to do, how this breaks down to single ",[27,28785,28786],{},"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:",[43,28789,28791],{"className":13667,"code":28790,"language":13669,"meta":48,"style":48},"\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",[50,28792,28793,28797,28802,28807],{"__ignoreMap":48},[53,28794,28795],{"class":55,"line":56},[53,28796,500],{"emptyLinePlaceholder":499},[53,28798,28799],{"class":55,"line":86},[53,28800,28801],{},"Given I look at the details of the item \"synyx coffee mug\"\n",[53,28803,28804],{"class":55,"line":126},[53,28805,28806],{},"When I add the item to the shopping cart\n",[53,28808,28809],{"class":55,"line":163},[53,28810,28811],{},"Then there is a \"synyx coffee mug\" in my Cart.\n",[18,28813,28814],{},"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.",[18,28816,28817],{},"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 🙂",[607,28819,989],{},{"title":48,"searchDepth":86,"depth":86,"links":28821},[28822],{"id":28655,"depth":86,"text":28656},[613],"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":28643,"description":28652},"blog/what-is-an-acceptance-test",[28832,28833,28834,22400,28835],"acceptance-test","attd","bdd","testing","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":28839,"title":28840,"author":28841,"body":28842,"category":29143,"date":29144,"description":29145,"extension":617,"link":29146,"meta":29147,"navigation":499,"path":29148,"seo":29149,"slug":29151,"stem":29152,"tags":29153,"teaser":29154,"__hash__":29155},"blog/blog/asynchronous-concurrency-with-vert-x-part-1.md","Asynchronous concurrency with vert.x – Part 1",[27670],{"type":11,"value":28843,"toc":29139},[28844,28847,28878,28881,28893,28955,28958,29018,29021,29040,29049,29128,29137],[14,28845,28840],{"id":28846},"asynchronous-concurrency-with-vertx-part-1",[18,28848,28849,28850,28855,28856,28859,28860,28863,28864,28867,28868,28871,28872,28877],{},"Event-Driven Concurrency\nAt synyx, we are looking at ",[585,28851,28854],{"href":28852,"rel":28853},"http://www.vertx.io",[589],"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 ",[573,28857,28858],{},"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 ",[50,28861,28862],{},"$.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 ",[50,28865,28866],{},"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 ",[50,28869,28870],{},"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 ",[585,28873,28876],{"href":28874,"rel":28875},"https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers",[589],"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.",[18,28879,28880],{},"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.",[18,28882,28883,28884,28889,28890,2304],{},"Event bus\nA short example in JavaScript that uses the event bus from\nthe ",[585,28885,28888],{"href":28886,"rel":28887},"https://github.com/vert-x/vert.x/blob/master/vertx-examples/src/main/javascript/eventbus",[589],"vert.x github repository"," –\ndefine a message handler that simply displays messages sent to the event bus address ",[50,28891,28892],{},"example.address",[43,28894,28896],{"className":13667,"code":28895,"language":13669,"meta":48,"style":48},"\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",[50,28897,28898,28902,28907,28912,28917,28922,28927,28932,28936,28941,28946,28951],{"__ignoreMap":48},[53,28899,28900],{"class":55,"line":56},[53,28901,500],{"emptyLinePlaceholder":499},[53,28903,28904],{"class":55,"line":86},[53,28905,28906],{},"// file handler.js\n",[53,28908,28909],{"class":55,"line":126},[53,28910,28911],{},"load('vertx.js')\n",[53,28913,28914],{"class":55,"line":163},[53,28915,28916],{},"var eb = vertx.eventBus;\n",[53,28918,28919],{"class":55,"line":186},[53,28920,28921],{},"var address = 'example.address'\n",[53,28923,28924],{"class":55,"line":221},[53,28925,28926],{},"var handler = function(message) {\n",[53,28928,28929],{"class":55,"line":242},[53,28930,28931],{}," stdout.println('Received message ' + message)\n",[53,28933,28934],{"class":55,"line":273},[53,28935,282],{},[53,28937,28938],{"class":55,"line":279},[53,28939,28940],{},"eb.registerHandler(address, handler);\n",[53,28942,28943],{"class":55,"line":496},[53,28944,28945],{},"function vertxStop() {\n",[53,28947,28948],{"class":55,"line":503},[53,28949,28950],{}," eb.unregisterHandler(address, handler);\n",[53,28952,28953],{"class":55,"line":509},[53,28954,282],{},[18,28956,28957],{},"We put the program that sends messages in a different file to achieve process isolation:",[43,28959,28961],{"className":13667,"code":28960,"language":13669,"meta":48,"style":48},"\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",[50,28962,28963,28967,28972,28976,28980,28984,28989,28994,28999,29004,29009,29014],{"__ignoreMap":48},[53,28964,28965],{"class":55,"line":56},[53,28966,500],{"emptyLinePlaceholder":499},[53,28968,28969],{"class":55,"line":86},[53,28970,28971],{},"// file sender.js\n",[53,28973,28974],{"class":55,"line":126},[53,28975,28911],{},[53,28977,28978],{"class":55,"line":163},[53,28979,28916],{},[53,28981,28982],{"class":55,"line":186},[53,28983,28921],{},[53,28985,28986],{"class":55,"line":221},[53,28987,28988],{},"vertx.setPeriodic(2000, sendMessage)\n",[53,28990,28991],{"class":55,"line":242},[53,28992,28993],{},"var count = 0\n",[53,28995,28996],{"class":55,"line":273},[53,28997,28998],{},"function sendMessage() {\n",[53,29000,29001],{"class":55,"line":279},[53,29002,29003],{}," var msg = \"some-message-\" + count++;\n",[53,29005,29006],{"class":55,"line":496},[53,29007,29008],{}," eb.send(address, msg);\n",[53,29010,29011],{"class":55,"line":503},[53,29012,29013],{}," stdout.println(\"sent message \" + msg)\n",[53,29015,29016],{"class":55,"line":509},[53,29017,282],{},[18,29019,29020],{},"Both programs are then started separately using the vertx runtime. They can then communicate on the event bus via the\nnetwork:",[43,29022,29024],{"className":13667,"code":29023,"language":13669,"meta":48,"style":48},"\n# vertx run handler.js -cluster -cluster-port 10001 &\n# vertx run sender.js -cluster -cluster-port 10002\n\n",[50,29025,29026,29030,29035],{"__ignoreMap":48},[53,29027,29028],{"class":55,"line":56},[53,29029,500],{"emptyLinePlaceholder":499},[53,29031,29032],{"class":55,"line":86},[53,29033,29034],{},"# vertx run handler.js -cluster -cluster-port 10001 &\n",[53,29036,29037],{"class":55,"line":126},[53,29038,29039],{},"# vertx run sender.js -cluster -cluster-port 10002\n",[18,29041,29042,29043,29048],{},"Repliers\nThis described system can be extended by the use\nof ",[585,29044,29047],{"href":29045,"rel":29046},"http://vertx.io/core_manual_js.html#replying-to-messages",[589],"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:",[43,29050,29052],{"className":13667,"code":29051,"language":13669,"meta":48,"style":48},"\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",[50,29053,29054,29058,29063,29068,29073,29078,29083,29088,29092,29096,29101,29106,29111,29116,29120,29124],{"__ignoreMap":48},[53,29055,29056],{"class":55,"line":56},[53,29057,500],{"emptyLinePlaceholder":499},[53,29059,29060],{"class":55,"line":86},[53,29061,29062],{},"var vertx = require('vertx'); // alternative import\n",[53,29064,29065],{"class":55,"line":126},[53,29066,29067],{},"var address = \"example.address\";\n",[53,29069,29070],{"class":55,"line":163},[53,29071,29072],{},"var handler = function (message, replier) {\n",[53,29074,29075],{"class":55,"line":186},[53,29076,29077],{}," stdout.println(\"sender sent \" + message);\n",[53,29079,29080],{"class":55,"line":221},[53,29081,29082],{}," replier(\"pong 1\", function (message, replier) {\n",[53,29084,29085],{"class":55,"line":242},[53,29086,29087],{}," // and so on\n",[53,29089,29090],{"class":55,"line":273},[53,29091,7104],{},[53,29093,29094],{"class":55,"line":279},[53,29095,282],{},[53,29097,29098],{"class":55,"line":496},[53,29099,29100],{},"vertx.eventBus.registerHandler(address, handler);\n",[53,29102,29103],{"class":55,"line":503},[53,29104,29105],{},"vertx.eventBus.send(address, \"ping 1\", function (message, replier) {\n",[53,29107,29108],{"class":55,"line":509},[53,29109,29110],{}," stdout.println(\"handler sent \" + message);\n",[53,29112,29113],{"class":55,"line":515},[53,29114,29115],{}," replier(\"ping 2\", function(message, replier) {\n",[53,29117,29118],{"class":55,"line":521},[53,29119,29087],{},[53,29121,29122],{"class":55,"line":527},[53,29123,7104],{},[53,29125,29126],{"class":55,"line":533},[53,29127,7148],{},[2352,29129,29131,29132,29136],{"id":29130},"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 ",[585,29133,29135],{"href":27832,"rel":29134},[589],"Sleeping barber problem"," – stay tuned!",[607,29138,989],{},{"title":48,"searchDepth":86,"depth":86,"links":29140},[29141],{"id":29130,"depth":86,"text":29142},"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!",[613,996],"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":28840,"description":29150},"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",[16644,6991,24035],"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":29157,"title":29158,"author":29159,"body":29160,"category":29793,"date":29794,"description":29795,"extension":617,"link":29796,"meta":29797,"navigation":499,"path":29798,"seo":29799,"slug":29164,"stem":29801,"tags":29802,"teaser":29807,"__hash__":29808},"blog/blog/liquibase-our-setup-in-a-larger-scale-project.md","Liquibase: Our setup in a larger scale project",[12861],{"type":11,"value":29161,"toc":29783},[29162,29165,29174,29178,29181,29204,29207,29218,29222,29226,29229,29232,29415,29418,29421,29425,29428,29528,29532,29535,29593,29602,29605,29615,29619,29622,29625,29699,29702,29705,29750,29753,29756,29760,29763,29772,29775,29778,29781],[14,29163,29158],{"id":29164},"liquibase-our-setup-in-a-larger-scale-project",[18,29166,29167,29168,29173],{},"In this post, we want to show you our ",[585,29169,29172],{"href":29170,"rel":29171},"http://www.liquibase.org/",[589],"Liquibase"," setup in a larger scale project that we’ve\nbeen developing for some time now.",[2352,29175,29177],{"id":29176},"gather-requirements","Gather Requirements",[18,29179,29180],{},"First off, a bit more information about the project and the whole project environment:",[577,29182,29183,29186,29189,29192,29195,29198,29201],{},[580,29184,29185],{},"The software developed in this project consists of different applications",[580,29187,29188],{},"Some applications use the same database, some use different ones",[580,29190,29191],{},"The software runs in multiple branch offices of a company",[580,29193,29194],{},"Not every application runs in every branch office",[580,29196,29197],{},"Some of the data in the databases of the branch offices is the same as in the others, some isn’t",[580,29199,29200],{},"We use maven with profiles to build for the different branch offices, because they need different config",[580,29202,29203],{},"As we took over the project from the company, the applications and also the database already existed for some years",[18,29205,29206],{},"This results in some requirements for our liquibase setup:",[577,29208,29209,29212,29215],{},[580,29210,29211],{},"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",[580,29213,29214],{},"We need different configurations for each branch office",[580,29216,29217],{},"We need different liquibase scripts for each branch office for some data, while we need the same scripts for other\ndata",[2352,29219,29221],{"id":29220},"liquibase-setup","Liquibase setup",[649,29223,29225],{"id":29224},"the-pom-file","The pom file",[18,29227,29228],{},"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.",[18,29230,29231],{},"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!",[43,29233,29235],{"className":1980,"code":29234,"language":1982,"meta":48,"style":48},"\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",[50,29236,29237,29242,29247,29252,29257,29262,29267,29272,29277,29282,29287,29292,29297,29302,29306,29311,29316,29321,29325,29330,29335,29340,29345,29350,29355,29360,29365,29370,29375,29380,29385,29390,29395,29400,29405,29410],{"__ignoreMap":48},[53,29238,29239],{"class":55,"line":56},[53,29240,29241],{},"\u003Cbuild>\n",[53,29243,29244],{"class":55,"line":86},[53,29245,29246],{}," \u003Cplugins>\n",[53,29248,29249],{"class":55,"line":126},[53,29250,29251],{}," \u003Cplugin>\n",[53,29253,29254],{"class":55,"line":163},[53,29255,29256],{}," \u003CgroupId>org.liquibase\u003C/groupId>\n",[53,29258,29259],{"class":55,"line":186},[53,29260,29261],{}," \u003CartifactId>liquibase-maven-plugin\u003C/artifactId>\n",[53,29263,29264],{"class":55,"line":221},[53,29265,29266],{}," \u003Cversion>2.0.3\u003C/version>\n",[53,29268,29269],{"class":55,"line":242},[53,29270,29271],{}," \u003Cconfiguration>\n",[53,29273,29274],{"class":55,"line":273},[53,29275,29276],{}," \u003CmigrationSqlOutputFile>\n",[53,29278,29279],{"class":55,"line":279},[53,29280,29281],{}," ${project.build.directory}/liquibase/migrate-${projectname.dbName}-${projectname.environment}.sql\n",[53,29283,29284],{"class":55,"line":496},[53,29285,29286],{}," \u003C/migrationSqlOutputFile>\n",[53,29288,29289],{"class":55,"line":503},[53,29290,29291],{}," \u003CpropertyFile>target/classes/liquibase-${projectname.environment}.properties\u003C/propertyFile>\n",[53,29293,29294],{"class":55,"line":509},[53,29295,29296],{}," \u003C/configuration>\n",[53,29298,29299],{"class":55,"line":515},[53,29300,29301],{}," \u003Cdependencies>\n",[53,29303,29304],{"class":55,"line":521},[53,29305,26077],{},[53,29307,29308],{"class":55,"line":527},[53,29309,29310],{}," \u003CgroupId>org.liquibase.ext\u003C/groupId>\n",[53,29312,29313],{"class":55,"line":533},[53,29314,29315],{}," \u003CartifactId>liquibase-oracle\u003C/artifactId>\n",[53,29317,29318],{"class":55,"line":539},[53,29319,29320],{}," \u003Cversion>1.2.0\u003C/version>\n",[53,29322,29323],{"class":55,"line":545},[53,29324,26097],{},[53,29326,29327],{"class":55,"line":2070},[53,29328,29329],{}," \u003C/dependencies>\n",[53,29331,29332],{"class":55,"line":2075},[53,29333,29334],{}," \u003Cexecutions>\n",[53,29336,29337],{"class":55,"line":2081},[53,29338,29339],{}," \u003Cexecution>\n",[53,29341,29342],{"class":55,"line":2087},[53,29343,29344],{}," \u003Cphase>install\u003C/phase>\n",[53,29346,29347],{"class":55,"line":2092},[53,29348,29349],{}," \u003Cgoals>\n",[53,29351,29352],{"class":55,"line":2097},[53,29353,29354],{}," \u003Cgoal>update\u003C/goal>\n",[53,29356,29357],{"class":55,"line":2103},[53,29358,29359],{}," \u003C/goals>\n",[53,29361,29362],{"class":55,"line":2109},[53,29363,29364],{}," \u003C/execution>\n",[53,29366,29367],{"class":55,"line":2115},[53,29368,29369],{}," \u003C/executions>\n",[53,29371,29372],{"class":55,"line":2120},[53,29373,29374],{}," \u003C/plugin>\n",[53,29376,29377],{"class":55,"line":2946},[53,29378,29379],{}," \u003C/plugins>\n",[53,29381,29382],{"class":55,"line":2952},[53,29383,29384],{}," \u003Cresources>\n",[53,29386,29387],{"class":55,"line":2964},[53,29388,29389],{}," \u003Cresource>\n",[53,29391,29392],{"class":55,"line":2973},[53,29393,29394],{}," \u003Cdirectory>src/main/resources\u003C/directory>\n",[53,29396,29397],{"class":55,"line":2979},[53,29398,29399],{}," \u003Cfiltering>true\u003C/filtering>\n",[53,29401,29402],{"class":55,"line":2990},[53,29403,29404],{}," \u003C/resource>\n",[53,29406,29407],{"class":55,"line":10443},[53,29408,29409],{}," \u003C/resources>\n",[53,29411,29412],{"class":55,"line":26443},[53,29413,29414],{},"\u003C/build>\n",[18,29416,29417],{},"The resource filtering is needed, because we’ll use placeholders in the liquibase files.",[18,29419,29420],{},"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).",[649,29422,29424],{"id":29423},"the-folder-structure","The folder structure",[18,29426,29427],{},"As for the liquibase folder structure, we set it up as followed:",[43,29429,29431],{"className":13667,"code":29430,"language":13669,"meta":48,"style":48},"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",[50,29432,29433,29438,29443,29448,29453,29458,29463,29468,29473,29478,29483,29488,29493,29498,29503,29508,29513,29518,29523],{"__ignoreMap":48},[53,29434,29435],{"class":55,"line":56},[53,29436,29437],{},"src/main/resources/application1/\n",[53,29439,29440],{"class":55,"line":86},[53,29441,29442],{},"├── changes \u003C-- folder for changes\n",[53,29444,29445],{"class":55,"line":126},[53,29446,29447],{},"├── data \u003C-- folder for the initial data imports\n",[53,29449,29450],{"class":55,"line":163},[53,29451,29452],{},"│ ├── all \u003C-- Liqiubase scripts , that are executed\n",[53,29454,29455],{"class":55,"line":186},[53,29456,29457],{},"│ │ │ for every branch\n",[53,29459,29460],{"class":55,"line":221},[53,29461,29462],{},"│ │ ├── csv \u003C-- CSV files for data imports\n",[53,29464,29465],{"class":55,"line":242},[53,29466,29467],{},"│ │ ├── data-xyz-001.xml \u003C-- liquibase scripts\n",[53,29469,29470],{"class":55,"line":273},[53,29471,29472],{},"│ │ └── data-xyz-002.xml\n",[53,29474,29475],{"class":55,"line":279},[53,29476,29477],{},"│ ├── branchX \u003C-- Branch specific folder. contains scripts and\n",[53,29479,29480],{"class":55,"line":496},[53,29481,29482],{},"│ └── branchY ... csv files, that are specific for this branch\n",[53,29484,29485],{"class":55,"line":503},[53,29486,29487],{},"├── init \u003C-- folder for database init (executed as SYSDBA)\n",[53,29489,29490],{"class":55,"line":509},[53,29491,29492],{},"├── install \u003C-- folder for some more database initialisation.\n",[53,29494,29495],{"class":55,"line":515},[53,29496,29497],{},"│ that can be executed as the actual user,\n",[53,29499,29500],{"class":55,"line":521},[53,29501,29502],{},"│ table creation and stuff\n",[53,29504,29505],{"class":55,"line":527},[53,29506,29507],{},"├── db.changelog.xml \u003C-- Liquibase Changelog that contains references\n",[53,29509,29510],{"class":55,"line":533},[53,29511,29512],{},"│ to the single liquibase scripts\n",[53,29514,29515],{"class":55,"line":539},[53,29516,29517],{},"└── db.init.xml \u003C-- Initial Liquibase Changelog that\n",[53,29519,29520],{"class":55,"line":545},[53,29521,29522],{}," has to be executed as @SYSDBA@\n",[53,29524,29525],{"class":55,"line":2070},[53,29526,29527],{}," and sets up the schemas and users (init folder)\n",[649,29529,29531],{"id":29530},"the-liquibase-changelog","The liquibase changelog",[18,29533,29534],{},"Here’s an example for the changelog file:",[43,29536,29538],{"className":1980,"code":29537,"language":1982,"meta":48,"style":48},"\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",[50,29539,29540,29545,29550,29555,29560,29565,29570,29574,29579,29584,29588],{"__ignoreMap":48},[53,29541,29542],{"class":55,"line":56},[53,29543,29544],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",[53,29546,29547],{"class":55,"line":86},[53,29548,29549],{},"\u003CdatabaseChangeLog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n",[53,29551,29552],{"class":55,"line":126},[53,29553,29554],{}," xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[53,29556,29557],{"class":55,"line":163},[53,29558,29559],{}," xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n",[53,29561,29562],{"class":55,"line":186},[53,29563,29564],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/tables.xml\"/>\n",[53,29566,29567],{"class":55,"line":221},[53,29568,29569],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/procedures.xml\"/>\n",[53,29571,29572],{"class":55,"line":242},[53,29573,322],{},[53,29575,29576],{"class":55,"line":273},[53,29577,29578],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/all/table_add_column_xyz.xml\"/>\n",[53,29580,29581],{"class":55,"line":279},[53,29582,29583],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/${projectname.branch}/adjust_procedure_asd.xml\"/>\n",[53,29585,29586],{"class":55,"line":496},[53,29587,322],{},[53,29589,29590],{"class":55,"line":503},[53,29591,29592],{},"\u003C/databaseChangeLog>\n",[18,29594,29595,29596,29601],{},"As you can see, we have used the ",[27,29597,29598],{},[573,29599,29600],{},"${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.",[18,29603,29604],{},"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.",[18,29606,29607,29608,29611,29612,29614],{},"You can also use the placeholder in other places, like the ",[573,29609,29610],{},"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 ‘",[573,29613,24601],{},"‘ folder and the .csv files in\neach branch folder. Furthermore, we are",[649,29616,29618],{"id":29617},"maven-profiles","maven profiles",[18,29620,29621],{},"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.",[18,29623,29624],{},"example profile:",[43,29626,29628],{"className":1980,"code":29627,"language":1982,"meta":48,"style":48},"\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",[50,29629,29630,29634,29639,29644,29649,29654,29659,29664,29669,29674,29679,29684,29689,29694],{"__ignoreMap":48},[53,29631,29632],{"class":55,"line":56},[53,29633,500],{"emptyLinePlaceholder":499},[53,29635,29636],{"class":55,"line":86},[53,29637,29638],{}," \u003Cprofile>\n",[53,29640,29641],{"class":55,"line":126},[53,29642,29643],{}," \u003Cid>xyz-test\u003C/id>\n",[53,29645,29646],{"class":55,"line":163},[53,29647,29648],{}," \u003Cproperties>\n",[53,29650,29651],{"class":55,"line":186},[53,29652,29653],{}," \u003Cprojectname.branch>xyz\u003C/projectname.branch>\n",[53,29655,29656],{"class":55,"line":221},[53,29657,29658],{}," \u003Cprojectname.environment>test\u003C/projectname.environment>\n",[53,29660,29661],{"class":55,"line":242},[53,29662,29663],{}," \u003Cprojectname.dbName>dbname\u003C/projectname.dbName>\n",[53,29665,29666],{"class":55,"line":273},[53,29667,29668],{}," \u003Cprojectname.liquibase.url>jdbc:oracle:thin:@192.168.224.234:1521:DBID\u003C/projectname.liquibase.url>\n",[53,29670,29671],{"class":55,"line":279},[53,29672,29673],{}," \u003Cprojectname.liquibase.username>username\u003C/projectname.liquibase.username>\n",[53,29675,29676],{"class":55,"line":496},[53,29677,29678],{}," \u003Cprojectname.liquibase.password>password\u003C/projectname.liquibase.password>\n",[53,29680,29681],{"class":55,"line":503},[53,29682,29683],{}," \u003Cprojectname.liquibase.schemaName>schema\u003C/projectname.liquibase.schemaName>\n",[53,29685,29686],{"class":55,"line":509},[53,29687,29688],{}," \u003Cprojectname.liquibase.changeLogFile>target/classes/path/to/changelog/db.changelog.xml\u003C/projectname.liquibase.changeLogFile>\n",[53,29690,29691],{"class":55,"line":515},[53,29692,29693],{}," \u003C/properties>\n",[53,29695,29696],{"class":55,"line":521},[53,29697,29698],{}," \u003C/profile>\n",[18,29700,29701],{},"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).",[18,29703,29704],{},"liquibase-test.properties:",[43,29706,29708],{"className":13667,"code":29707,"language":13669,"meta":48,"style":48},"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",[50,29709,29710,29715,29720,29725,29730,29735,29740,29745],{"__ignoreMap":48},[53,29711,29712],{"class":55,"line":56},[53,29713,29714],{},"changeLogFile=${projectname.liquibase.changeLogFile}\n",[53,29716,29717],{"class":55,"line":86},[53,29718,29719],{},"driver=oracle.jdbc.OracleDriver\n",[53,29721,29722],{"class":55,"line":126},[53,29723,29724],{},"url=${projectname.liquibase.url}\n",[53,29726,29727],{"class":55,"line":163},[53,29728,29729],{},"username=${projectname.liquibase.username}\n",[53,29731,29732],{"class":55,"line":186},[53,29733,29734],{},"password=${projectname.liquibase.password}\n",[53,29736,29737],{"class":55,"line":221},[53,29738,29739],{},"defaultSchemaName=${projectname.liquibase.schemaName}\n",[53,29741,29742],{"class":55,"line":242},[53,29743,29744],{},"verbose=true\n",[53,29746,29747],{"class":55,"line":273},[53,29748,29749],{},"dropFirst=false\n",[18,29751,29752],{},"Here we map our properties from the profiles to the actual liquibase property names and also set a few other liquibase\nconfigs.",[18,29754,29755],{},"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).",[2352,29757,29759],{"id":29758},"execution-conclusion","Execution & Conclusion",[18,29761,29762],{},"Because of the use of maven, we can execute all of the changes from our local machines very easy:",[43,29764,29766],{"className":13667,"code":29765,"language":13669,"meta":48,"style":48},"mvn clean install -Pxyz-test\n",[50,29767,29768],{"__ignoreMap":48},[53,29769,29770],{"class":55,"line":56},[53,29771,29765],{},[18,29773,29774],{},"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.",[18,29776,29777],{},"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.",[18,29779,29780],{},"Please let us know what you think of our approach and if you know an even better one!",[607,29782,989],{},{"title":48,"searchDepth":86,"depth":86,"links":29784},[29785,29786,29792],{"id":29176,"depth":86,"text":29177},{"id":29220,"depth":86,"text":29221,"children":29787},[29788,29789,29790,29791],{"id":29224,"depth":126,"text":29225},{"id":29423,"depth":126,"text":29424},{"id":29530,"depth":126,"text":29531},{"id":29617,"depth":126,"text":29618},{"id":29758,"depth":86,"text":29759},[613],"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":29158,"description":29800},"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",[29803,29804,29805,29806],"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":29810,"title":29811,"author":29812,"body":29813,"category":31931,"date":31932,"description":48,"extension":617,"link":31933,"meta":31934,"navigation":499,"path":31935,"seo":31936,"slug":29817,"stem":31937,"tags":31938,"teaser":31942,"__hash__":31943},"blog/blog/continuous-deployment-automatic-backup-script.md","Continuous Deployment – Automatic Backup Script",[8328],{"type":11,"value":29814,"toc":31913},[29815,29818,29822,29837,29841,29850,29859,29862,29865,29876,29883,29887,29908,29911,29946,29949,29958,30011,30025,30031,30045,30049,30052,30072,30270,30274,30277,30281,30284,30298,30305,30416,30420,30423,30740,30744,30747,30956,30960,30966,30975,30978,31012,31015,31024,31027,31181,31185,31188,31199,31209,31451,31455,31461,31477,31494,31508,31681,31685,31688,31707,31861,31872,31876,31885,31889,31892,31896,31903,31910],[14,29816,29811],{"id":29817},"continuous-deployment-automatic-backup-script",[2352,29819,29821],{"id":29820},"a-few-words-about-continuous-deployment","A few words about Continuous Deployment",[5221,29823,29824,29827,29830],{},[18,29825,29826],{},"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).",[18,29828,29829],{},"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.",[18,29831,29832],{},[585,29833,29836],{"href":29834,"rel":29835,"title":13828},"http://blog.assembla.com/assemblablog/tabid/12618/bid/92411/Continuous-Delivery-vs-Continuous-Deployment-vs-Continuous-Integration-Wait-huh.aspx",[589],"Read full blog post",[2352,29838,29840],{"id":29839},"were-on-the-way","…we’re on the way",[18,29842,29843,29844,29849],{},"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 ",[585,29845,29848],{"href":29846,"rel":29847,"title":29848},"http://tomcat.apache.org/",[589],"Tomcat"," applications.",[18,29851,29852,29853,29858],{},"For some applications we even use a continuous deployment script triggered by crontab (e.g. every hour) to check\nif ",[585,29854,29857],{"href":29855,"rel":29856,"title":29857},"http://www.sonatype.org/nexus/",[589],"Nexus"," has a new version of the application and if so to fetch and deploy it\nautomatically.",[18,29860,29861],{},"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.",[18,29863,29864],{},"The typical backup steps before a deployment are:",[577,29866,29867,29870,29873],{},[580,29868,29869],{},"saving war file resp. information about the current deployed version",[580,29871,29872],{},"saving database data in a dump",[580,29874,29875],{},"saving directories and/or files, e.g. generated error reports",[18,29877,29878,29879,986],{},"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 ",[585,29880,13838],{"href":29881,"rel":29882,"title":13838},"https://github.com/murygina/automated-backup",[589],[2352,29884,29886],{"id":29885},"configuration-for-mysqldump","Configuration for mysqldump",[18,29888,10933,29889,29892,29893,29896,29897,29900,29901,29904,29905,29907],{},[50,29890,29891],{},"[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 ",[50,29894,29895],{},"mysqldump"," in an automatic process with nobody around to\nenter the password, you have to provide the access data in a file. Create a ",[50,29898,29899],{},".ini","-style file named ",[50,29902,29903],{},".my.cnf"," providing\nuser and password information. Later in the script, ",[50,29906,29895],{}," will read this configuration and will automatically use\nthe supplied user name and password.",[18,29909,29910],{},"If you have only one database to be dumped, following lines are enough:",[43,29912,29916],{"className":29913,"code":29914,"language":29915,"meta":48,"style":48},"language-shell shiki shiki-themes github-light github-dark","\n[mysqldump]\n user = myUser\n password = xxx\n\n","shell",[50,29917,29918,29922,29927,29936],{"__ignoreMap":48},[53,29919,29920],{"class":55,"line":56},[53,29921,500],{"emptyLinePlaceholder":499},[53,29923,29924],{"class":55,"line":86},[53,29925,29926],{"class":82},"[mysqldump]\n",[53,29928,29929,29931,29933],{"class":55,"line":126},[53,29930,180],{"class":59},[53,29932,1245],{"class":63},[53,29934,29935],{"class":63}," myUser\n",[53,29937,29938,29941,29943],{"class":55,"line":163},[53,29939,29940],{"class":59}," password",[53,29942,1245],{"class":63},[53,29944,29945],{"class":63}," xxx\n",[18,29947,29948],{},"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.",[18,29950,29951,29952,17019,29955,986],{},"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: ",[50,29953,29954],{},"[mysqldumpfoo]",[50,29956,29957],{},"[mysqldumpbar]",[43,29959,29961],{"className":29913,"code":29960,"language":29915,"meta":48,"style":48},"\n[mysqldumpfoo]\n user = fooUser\n password = xxx\n[mysqldumpbar]\n user = barUser\n password = xxx\n\n",[50,29962,29963,29967,29972,29981,29989,29994,30003],{"__ignoreMap":48},[53,29964,29965],{"class":55,"line":56},[53,29966,500],{"emptyLinePlaceholder":499},[53,29968,29969],{"class":55,"line":86},[53,29970,29971],{"class":82},"[mysqldumpfoo]\n",[53,29973,29974,29976,29978],{"class":55,"line":126},[53,29975,180],{"class":59},[53,29977,1245],{"class":63},[53,29979,29980],{"class":63}," fooUser\n",[53,29982,29983,29985,29987],{"class":55,"line":163},[53,29984,29940],{"class":59},[53,29986,1245],{"class":63},[53,29988,29945],{"class":63},[53,29990,29991],{"class":55,"line":186},[53,29992,29993],{"class":82},"[mysqldumpbar]\n",[53,29995,29996,29998,30000],{"class":55,"line":221},[53,29997,180],{"class":59},[53,29999,1245],{"class":63},[53,30001,30002],{"class":63}," barUser\n",[53,30004,30005,30007,30009],{"class":55,"line":242},[53,30006,29940],{"class":59},[53,30008,1245],{"class":63},[53,30010,29945],{"class":63},[18,30012,30013,30016,30017,30020,30021,30024],{},[27,30014,30015],{},"Caution:"," In this case there ",[27,30018,30019],{},"must not"," be a ",[50,30022,30023],{},"[mysqldump]"," section since the more specific sections with database\nname as suffix would be ignored.",[18,30026,30027,30028,30030],{},"If you want to prevent other users from reading your ",[50,30029,29903],{},", change the file permission so that only the owner has\nfull read and write permissions.",[43,30032,30034],{"className":13667,"code":30033,"language":13669,"meta":48,"style":48},"\nchmod 600 ~/.my.cnf\n\n",[50,30035,30036,30040],{"__ignoreMap":48},[53,30037,30038],{"class":55,"line":56},[53,30039,500],{"emptyLinePlaceholder":499},[53,30041,30042],{"class":55,"line":86},[53,30043,30044],{},"chmod 600 ~/.my.cnf\n",[2352,30046,30048],{"id":30047},"configuration-for-backup-script","Configuration for Backup script",[18,30050,30051],{},"The backup script will depend on a configuration file containing details for the backup process:",[577,30053,30054,30057,30060,30063,30066,30069],{},[580,30055,30056],{},"the absolute path of the deployed webapp",[580,30058,30059],{},"the absolute path of the parent directory where the backups should be stored",[580,30061,30062],{},"the names of the databases to be dumped",[580,30064,30065],{},"the absolute path of the mysqldump config file with access data",[580,30067,30068],{},"if the deployed war file itself or if only the information about the current deployed version should be saved",[580,30070,30071],{},"if there are files and/or directories that should be moved or copied to the backup directory",[43,30073,30075],{"className":29913,"code":30074,"language":29915,"meta":48,"style":48},"\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",[50,30076,30077,30081,30086,30101,30106,30120,30125,30130,30140,30145,30150,30164,30169,30174,30179,30184,30189,30194,30198,30203,30208,30218,30223,30228,30232,30251],{"__ignoreMap":48},[53,30078,30079],{"class":55,"line":56},[53,30080,500],{"emptyLinePlaceholder":499},[53,30082,30083],{"class":55,"line":86},[53,30084,30085],{"class":2530},"# absolute path of the deployed webapp\n",[53,30087,30088,30091,30093,30095,30098],{"class":55,"line":126},[53,30089,30090],{"class":82},"WEBAPP_PATH",[53,30092,390],{"class":389},[53,30094,2704],{"class":63},[53,30096,30097],{"class":82},"$HOME",[53,30099,30100],{"class":63},"/tomcat/webapps/ROOT\"\n",[53,30102,30103],{"class":55,"line":163},[53,30104,30105],{"class":2530},"# absolute path where the backups should be stored\n",[53,30107,30108,30111,30113,30115,30117],{"class":55,"line":186},[53,30109,30110],{"class":82},"BACKUP_PATH",[53,30112,390],{"class":389},[53,30114,2704],{"class":63},[53,30116,30097],{"class":82},[53,30118,30119],{"class":63},"/backup\"\n",[53,30121,30122],{"class":55,"line":221},[53,30123,30124],{"class":2530},"# names of the databases to be dumped\n",[53,30126,30127],{"class":55,"line":242},[53,30128,30129],{"class":2530},"# separate with whitespace\n",[53,30131,30132,30135,30137],{"class":55,"line":273},[53,30133,30134],{"class":82},"DB_NAME",[53,30136,390],{"class":389},[53,30138,30139],{"class":63},"\"db1 db2 db3\"\n",[53,30141,30142],{"class":55,"line":279},[53,30143,30144],{"class":2530},"# absolute path of the mysql config file with user data\n",[53,30146,30147],{"class":55,"line":496},[53,30148,30149],{"class":2530},"# for the above specified databases\n",[53,30151,30152,30155,30157,30159,30161],{"class":55,"line":503},[53,30153,30154],{"class":82},"MYSQL_CONF",[53,30156,390],{"class":389},[53,30158,2704],{"class":63},[53,30160,30097],{"class":82},[53,30162,30163],{"class":63},"/.my.cnf\"\n",[53,30165,30166],{"class":55,"line":509},[53,30167,30168],{"class":2530},"# decide if current deployed war should be saved in backup dir\n",[53,30170,30171],{"class":55,"line":515},[53,30172,30173],{"class":2530},"# or if only the information about the version that is deployed\n",[53,30175,30176],{"class":55,"line":521},[53,30177,30178],{"class":2530},"# should be saved\n",[53,30180,30181],{"class":55,"line":527},[53,30182,30183],{"class":2530},"#\n",[53,30185,30186],{"class":55,"line":533},[53,30187,30188],{"class":2530},"# 0: save war only if the current deployed version is a snapshot\n",[53,30190,30191],{"class":55,"line":539},[53,30192,30193],{"class":2530},"# 1: always save war\n",[53,30195,30196],{"class":55,"line":545},[53,30197,30183],{"class":2530},[53,30199,30200],{"class":55,"line":2070},[53,30201,30202],{"class":2530},"# if you specify nothing or something that is not 0 or 1,\n",[53,30204,30205],{"class":55,"line":2075},[53,30206,30207],{"class":2530},"# current deployed war file won't be saved\n",[53,30209,30210,30213,30215],{"class":55,"line":2081},[53,30211,30212],{"class":82},"WAR",[53,30214,390],{"class":389},[53,30216,30217],{"class":63},"1\n",[53,30219,30220],{"class":55,"line":2087},[53,30221,30222],{"class":2530},"# specifiy folders and/or files (absolute path!)\n",[53,30224,30225],{"class":55,"line":2092},[53,30226,30227],{"class":2530},"# that should be copied resp. moved into backup dir\n",[53,30229,30230],{"class":55,"line":2097},[53,30231,30129],{"class":2530},[53,30233,30234,30237,30239,30241,30243,30246,30248],{"class":55,"line":2103},[53,30235,30236],{"class":82},"CONTENT_TO_BE_COPIED",[53,30238,390],{"class":389},[53,30240,2704],{"class":63},[53,30242,30097],{"class":82},[53,30244,30245],{"class":63},"/file ",[53,30247,30097],{"class":82},[53,30249,30250],{"class":63},"/folder\"\n",[53,30252,30253,30256,30258,30260,30262,30265,30267],{"class":55,"line":2109},[53,30254,30255],{"class":82},"CONTENT_TO_BE_MOVED",[53,30257,390],{"class":389},[53,30259,2704],{"class":63},[53,30261,30097],{"class":82},[53,30263,30264],{"class":63},"/folder/file ",[53,30266,30097],{"class":82},[53,30268,30269],{"class":63},"/folder/*\"\n",[2352,30271,30273],{"id":30272},"the-backup-script","The backup script",[18,30275,30276],{},"Now let’s have a look at the actual backup script and the most important steps of it.",[649,30278,30280],{"id":30279},"get-configuration-file","Get configuration file",[18,30282,30283],{},"You can call the script with the option -c to specify which configuration file should be used.",[43,30285,30287],{"className":13667,"code":30286,"language":13669,"meta":48,"style":48},"\n./backup.sh -c config/custom.conf\n\n",[50,30288,30289,30293],{"__ignoreMap":48},[53,30290,30291],{"class":55,"line":56},[53,30292,500],{"emptyLinePlaceholder":499},[53,30294,30295],{"class":55,"line":86},[53,30296,30297],{},"./backup.sh -c config/custom.conf\n",[18,30299,30300,30301,30304],{},"If you call the script without the -c option, the script tries to get a file named ",[50,30302,30303],{},"backup.conf"," in the directory the\nscript is located.",[43,30306,30308],{"className":29913,"code":30307,"language":29915,"meta":48,"style":48},"\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",[50,30309,30310,30314,30319,30340,30345,30350,30370,30385,30392,30401,30406,30411],{"__ignoreMap":48},[53,30311,30312],{"class":55,"line":56},[53,30313,500],{"emptyLinePlaceholder":499},[53,30315,30316],{"class":55,"line":86},[53,30317,30318],{"class":2530},"# default config file\n",[53,30320,30321,30324,30326,30329,30332,30334,30337],{"class":55,"line":126},[53,30322,30323],{"class":82},"CONF_FILE",[53,30325,390],{"class":389},[53,30327,30328],{"class":63},"\"$(",[53,30330,30331],{"class":59},"dirname",[53,30333,6072],{"class":63},[53,30335,30336],{"class":89},"$0",[53,30338,30339],{"class":63},"\")/backup.conf\"\n",[53,30341,30342],{"class":55,"line":163},[53,30343,30344],{"class":2530},"# a colon after the option means that the option\n",[53,30346,30347],{"class":55,"line":186},[53,30348,30349],{"class":2530},"# expects an argument\n",[53,30351,30352,30355,30358,30361,30364,30367],{"class":55,"line":221},[53,30353,30354],{"class":389},"while",[53,30356,30357],{"class":89}," getopts",[53,30359,30360],{"class":63}," \"hc:\"",[53,30362,30363],{"class":63}," OPTION",[53,30365,30366],{"class":82},"; ",[53,30368,30369],{"class":389},"do\n",[53,30371,30372,30375,30377,30380,30382],{"class":55,"line":242},[53,30373,30374],{"class":389},"case",[53,30376,6072],{"class":63},[53,30378,30379],{"class":82},"$OPTION",[53,30381,2704],{"class":63},[53,30383,30384],{"class":389}," in\n",[53,30386,30387,30390],{"class":55,"line":273},[53,30388,12568],{"class":30389},"sA_wV",[53,30391,685],{"class":389},[53,30393,30394,30396,30398],{"class":55,"line":279},[53,30395,30323],{"class":82},[53,30397,390],{"class":389},[53,30399,30400],{"class":82},"$OPTARG\n",[53,30402,30403],{"class":55,"line":496},[53,30404,30405],{"class":82},";;\n",[53,30407,30408],{"class":55,"line":503},[53,30409,30410],{"class":389},"esac\n",[53,30412,30413],{"class":55,"line":509},[53,30414,30415],{"class":389},"done\n",[649,30417,30419],{"id":30418},"read-and-validate-configuration-file","Read and validate configuration file",[18,30421,30422],{},"The script makes sure that the configuration file exists and contains all the required parameters. Otherwise it will\nfail with an appropriate error message.",[43,30424,30426],{"className":29913,"code":30425,"language":29915,"meta":48,"style":48},"\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",[50,30427,30428,30432,30437,30458,30466,30473,30477,30482,30503,30510,30516,30520,30525,30536,30541,30607,30626,30632,30636,30641,30661,30678,30684,30688,30693,30713,30730,30736],{"__ignoreMap":48},[53,30429,30430],{"class":55,"line":56},[53,30431,500],{"emptyLinePlaceholder":499},[53,30433,30434],{"class":55,"line":86},[53,30435,30436],{"class":2530},"# variable is empty\n",[53,30438,30439,30441,30443,30446,30448,30451,30453,30456],{"class":55,"line":126},[53,30440,2540],{"class":389},[53,30442,2307],{"class":82},[53,30444,30445],{"class":389},"-z",[53,30447,6072],{"class":63},[53,30449,30450],{"class":82},"$CONF_FILE",[53,30452,2704],{"class":63},[53,30454,30455],{"class":82}," ]; ",[53,30457,6087],{"class":389},[53,30459,30460,30463],{"class":55,"line":163},[53,30461,30462],{"class":89}," echo",[53,30464,30465],{"class":63}," \"CONF_FILE not set\"\n",[53,30467,30468,30471],{"class":55,"line":186},[53,30469,30470],{"class":89}," exit",[53,30472,2574],{"class":89},[53,30474,30475],{"class":55,"line":221},[53,30476,2579],{"class":389},[53,30478,30479],{"class":55,"line":242},[53,30480,30481],{"class":2530},"# no file found for the given variable\n",[53,30483,30484,30486,30488,30490,30493,30495,30497,30499,30501],{"class":55,"line":273},[53,30485,2540],{"class":389},[53,30487,2307],{"class":82},[53,30489,12136],{"class":389},[53,30491,30492],{"class":389}," -f",[53,30494,6072],{"class":63},[53,30496,30450],{"class":82},[53,30498,2704],{"class":63},[53,30500,30455],{"class":82},[53,30502,6087],{"class":389},[53,30504,30505,30507],{"class":55,"line":279},[53,30506,30462],{"class":89},[53,30508,30509],{"class":63}," \"No configuration file found\"\n",[53,30511,30512,30514],{"class":55,"line":496},[53,30513,30470],{"class":89},[53,30515,2574],{"class":89},[53,30517,30518],{"class":55,"line":503},[53,30519,2579],{"class":389},[53,30521,30522],{"class":55,"line":509},[53,30523,30524],{"class":2530},"# read in variables of conf file\n",[53,30526,30527,30529,30531,30533],{"class":55,"line":515},[53,30528,986],{"class":89},[53,30530,6072],{"class":63},[53,30532,30450],{"class":82},[53,30534,30535],{"class":63},"\"\n",[53,30537,30538],{"class":55,"line":521},[53,30539,30540],{"class":2530},"# make sure that conf file contains all required variables\n",[53,30542,30543,30545,30547,30549,30551,30554,30556,30559,30562,30564,30566,30568,30571,30573,30575,30577,30579,30581,30583,30586,30588,30590,30592,30594,30596,30598,30601,30603,30605],{"class":55,"line":527},[53,30544,2540],{"class":389},[53,30546,2307],{"class":82},[53,30548,30445],{"class":389},[53,30550,6072],{"class":63},[53,30552,30553],{"class":82},"$WEBAPP_PATH",[53,30555,2704],{"class":63},[53,30557,30558],{"class":82}," ] ",[53,30560,30561],{"class":389},"||",[53,30563,2307],{"class":82},[53,30565,30445],{"class":389},[53,30567,6072],{"class":63},[53,30569,30570],{"class":82},"$BACKUP_PATH",[53,30572,2704],{"class":63},[53,30574,30558],{"class":82},[53,30576,30561],{"class":389},[53,30578,2307],{"class":82},[53,30580,30445],{"class":389},[53,30582,6072],{"class":63},[53,30584,30585],{"class":82},"$DB_NAME",[53,30587,2704],{"class":63},[53,30589,30558],{"class":82},[53,30591,30561],{"class":389},[53,30593,2307],{"class":82},[53,30595,30445],{"class":389},[53,30597,6072],{"class":63},[53,30599,30600],{"class":82},"$MYSQL_CONF",[53,30602,2704],{"class":63},[53,30604,30455],{"class":82},[53,30606,6087],{"class":389},[53,30608,30609,30611,30614,30617,30619,30621,30623],{"class":55,"line":533},[53,30610,30462],{"class":89},[53,30612,30613],{"class":63}," \"Configuration file seems to be incorrect: required variables missing. Please check your config file: $(",[53,30615,30616],{"class":59},"readlink",[53,30618,30492],{"class":89},[53,30620,6072],{"class":63},[53,30622,30450],{"class":82},[53,30624,30625],{"class":63},"\")\"\n",[53,30627,30628,30630],{"class":55,"line":539},[53,30629,30470],{"class":89},[53,30631,2574],{"class":89},[53,30633,30634],{"class":55,"line":545},[53,30635,2579],{"class":389},[53,30637,30638],{"class":55,"line":2070},[53,30639,30640],{"class":2530},"# make sure that WEBAPP_PATH exists\n",[53,30642,30643,30645,30647,30649,30651,30653,30655,30657,30659],{"class":55,"line":2075},[53,30644,2540],{"class":389},[53,30646,2307],{"class":82},[53,30648,12136],{"class":389},[53,30650,2459],{"class":389},[53,30652,6072],{"class":63},[53,30654,30553],{"class":82},[53,30656,2704],{"class":63},[53,30658,30455],{"class":82},[53,30660,6087],{"class":389},[53,30662,30663,30665,30668,30670,30672,30674,30676],{"class":55,"line":2081},[53,30664,30462],{"class":89},[53,30666,30667],{"class":63}," \"The given webapp path doesn't exist. Please check your config file: $(",[53,30669,30616],{"class":59},[53,30671,30492],{"class":89},[53,30673,6072],{"class":63},[53,30675,30450],{"class":82},[53,30677,30625],{"class":63},[53,30679,30680,30682],{"class":55,"line":2087},[53,30681,30470],{"class":89},[53,30683,2574],{"class":89},[53,30685,30686],{"class":55,"line":2092},[53,30687,2579],{"class":389},[53,30689,30690],{"class":55,"line":2097},[53,30691,30692],{"class":2530},"# make sure that mysql config file exists\n",[53,30694,30695,30697,30699,30701,30703,30705,30707,30709,30711],{"class":55,"line":2103},[53,30696,2540],{"class":389},[53,30698,2307],{"class":82},[53,30700,12136],{"class":389},[53,30702,30492],{"class":389},[53,30704,6072],{"class":63},[53,30706,30600],{"class":82},[53,30708,2704],{"class":63},[53,30710,30455],{"class":82},[53,30712,6087],{"class":389},[53,30714,30715,30717,30720,30722,30724,30726,30728],{"class":55,"line":2109},[53,30716,30462],{"class":89},[53,30718,30719],{"class":63}," \"The given mysql config file doesn't exist. Please check your config file: $(",[53,30721,30616],{"class":59},[53,30723,30492],{"class":89},[53,30725,6072],{"class":63},[53,30727,30450],{"class":82},[53,30729,30625],{"class":63},[53,30731,30732,30734],{"class":55,"line":2115},[53,30733,30470],{"class":89},[53,30735,2574],{"class":89},[53,30737,30738],{"class":55,"line":2120},[53,30739,2579],{"class":389},[649,30741,30743],{"id":30742},"creating-the-backup-directories","Creating the backup directories",[18,30745,30746],{},"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.",[43,30748,30750],{"className":29913,"code":30749,"language":29915,"meta":48,"style":48},"\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",[50,30751,30752,30756,30770,30788,30793,30813,30824,30841,30845,30850,30871,30881,30898,30903,30921,30946,30952],{"__ignoreMap":48},[53,30753,30754],{"class":55,"line":56},[53,30755,500],{"emptyLinePlaceholder":499},[53,30757,30758,30761,30763,30765,30767],{"class":55,"line":86},[53,30759,30760],{"class":82},"CURRENT_DATE",[53,30762,390],{"class":389},[53,30764,30328],{"class":63},[53,30766,2596],{"class":59},[53,30768,30769],{"class":63}," +%Y-%m-%d_%H-%M)\"\n",[53,30771,30772,30775,30777,30779,30781,30783,30786],{"class":55,"line":126},[53,30773,30774],{"class":82},"FULL_BACKUP_PATH",[53,30776,390],{"class":389},[53,30778,2704],{"class":63},[53,30780,30570],{"class":82},[53,30782,2628],{"class":63},[53,30784,30785],{"class":82},"$CURRENT_DATE",[53,30787,30535],{"class":63},[53,30789,30790],{"class":55,"line":163},[53,30791,30792],{"class":2530},"# check if backup dir exists, if not create it\n",[53,30794,30795,30797,30799,30801,30803,30805,30807,30809,30811],{"class":55,"line":186},[53,30796,2540],{"class":389},[53,30798,2307],{"class":82},[53,30800,12136],{"class":389},[53,30802,2459],{"class":389},[53,30804,6072],{"class":63},[53,30806,30570],{"class":82},[53,30808,2704],{"class":63},[53,30810,30455],{"class":82},[53,30812,6087],{"class":389},[53,30814,30815,30818,30820,30822],{"class":55,"line":221},[53,30816,30817],{"class":59}," mkdir",[53,30819,6072],{"class":63},[53,30821,30570],{"class":82},[53,30823,30535],{"class":63},[53,30825,30826,30828,30831,30833,30835,30837,30839],{"class":55,"line":242},[53,30827,30462],{"class":89},[53,30829,30830],{"class":63}," \"Create parent backup directory $(",[53,30832,30616],{"class":59},[53,30834,30492],{"class":89},[53,30836,6072],{"class":63},[53,30838,30570],{"class":82},[53,30840,30625],{"class":63},[53,30842,30843],{"class":55,"line":273},[53,30844,2579],{"class":389},[53,30846,30847],{"class":55,"line":279},[53,30848,30849],{"class":2530},"# check if there is already a dir for current date, if not create it\n",[53,30851,30852,30854,30856,30858,30860,30862,30865,30867,30869],{"class":55,"line":496},[53,30853,2540],{"class":389},[53,30855,2307],{"class":82},[53,30857,12136],{"class":389},[53,30859,2459],{"class":389},[53,30861,6072],{"class":63},[53,30863,30864],{"class":82},"$FULL_BACKUP_PATH",[53,30866,2704],{"class":63},[53,30868,30455],{"class":82},[53,30870,6087],{"class":389},[53,30872,30873,30875,30877,30879],{"class":55,"line":503},[53,30874,30817],{"class":59},[53,30876,6072],{"class":63},[53,30878,30864],{"class":82},[53,30880,30535],{"class":63},[53,30882,30883,30885,30888,30890,30892,30894,30896],{"class":55,"line":509},[53,30884,30462],{"class":89},[53,30886,30887],{"class":63}," \"Create backup directory $(",[53,30889,30616],{"class":59},[53,30891,30492],{"class":89},[53,30893,6072],{"class":63},[53,30895,30864],{"class":82},[53,30897,30625],{"class":63},[53,30899,30900],{"class":55,"line":515},[53,30901,30902],{"class":389},"else\n",[53,30904,30905,30907,30910,30912,30914,30916,30918],{"class":55,"line":521},[53,30906,30462],{"class":89},[53,30908,30909],{"class":63}," \"$(",[53,30911,30616],{"class":59},[53,30913,30492],{"class":89},[53,30915,6072],{"class":63},[53,30917,30864],{"class":82},[53,30919,30920],{"class":63},"\") already exists\"\n",[53,30922,30923,30925,30928,30930,30932,30934,30936,30939,30941,30943],{"class":55,"line":527},[53,30924,30462],{"class":89},[53,30926,30927],{"class":63}," \"Wait until $(",[53,30929,30616],{"class":59},[53,30931,30492],{"class":89},[53,30933,6072],{"class":63},[53,30935,30570],{"class":82},[53,30937,30938],{"class":63},"/$(",[53,30940,2596],{"class":59},[53,30942,2459],{"class":89},[53,30944,30945],{"class":63}," '+1min' +%Y-%m-%d_%H-%M)\") can be created\"\n",[53,30947,30948,30950],{"class":55,"line":533},[53,30949,30470],{"class":89},[53,30951,2574],{"class":89},[53,30953,30954],{"class":55,"line":539},[53,30955,2579],{"class":389},[649,30957,30959],{"id":30958},"save-the-information-about-the-current-deployed-version","Save the information about the current deployed version",[18,30961,30962,30963,2304],{},"You find the information about the current deployed version in the ",[50,30964,30965],{},"pom.properties",[43,30967,30969],{"className":13667,"code":30968,"language":13669,"meta":48,"style":48}," ~/tomcat/webapps/ROOT/META-INF/maven/org.synyx/foo/pom.properties\n",[50,30970,30971],{"__ignoreMap":48},[53,30972,30973],{"class":55,"line":56},[53,30974,30968],{},[18,30976,30977],{},"It looks like this:",[43,30979,30981],{"className":13667,"code":30980,"language":13669,"meta":48,"style":48},"\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",[50,30982,30983,30987,30992,30997,31002,31007],{"__ignoreMap":48},[53,30984,30985],{"class":55,"line":56},[53,30986,500],{"emptyLinePlaceholder":499},[53,30988,30989],{"class":55,"line":86},[53,30990,30991],{},"#Generated by Maven\n",[53,30993,30994],{"class":55,"line":126},[53,30995,30996],{},"#Sat Mar 30 02:03:52 CET 2013\n",[53,30998,30999],{"class":55,"line":163},[53,31000,31001],{},"version=1.1.10-SNAPSHOT\n",[53,31003,31004],{"class":55,"line":186},[53,31005,31006],{},"groupId=org.synyx\n",[53,31008,31009],{"class":55,"line":221},[53,31010,31011],{},"artifactId=foo\n",[18,31013,31014],{},"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:",[43,31016,31018],{"className":13667,"code":31017,"language":13669,"meta":48,"style":48},"./nexusdeploy.sh -i org.synyx:foo:1.1.10-SNAPSHOT -w ROOT\n",[50,31019,31020],{"__ignoreMap":48},[53,31021,31022],{"class":55,"line":56},[53,31023,31017],{},[18,31025,31026],{},"So this properties file is copied to the backup directory.",[43,31028,31030],{"className":29913,"code":31029,"language":29915,"meta":48,"style":48},"\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",[50,31031,31032,31036,31046,31074,31079,31126,31148,31154,31158,31163],{"__ignoreMap":48},[53,31033,31034],{"class":55,"line":56},[53,31035,500],{"emptyLinePlaceholder":499},[53,31037,31038,31041,31043],{"class":55,"line":86},[53,31039,31040],{"class":82},"PROPS_NAME",[53,31042,390],{"class":389},[53,31044,31045],{"class":63},"\"pom.properties\"\n",[53,31047,31048,31051,31053,31055,31057,31059,31061,31064,31067,31069,31072],{"class":55,"line":126},[53,31049,31050],{"class":82},"APP_PROPS",[53,31052,390],{"class":389},[53,31054,30328],{"class":63},[53,31056,13809],{"class":59},[53,31058,6072],{"class":63},[53,31060,30553],{"class":82},[53,31062,31063],{"class":63},"\" ",[53,31065,31066],{"class":89},"-name",[53,31068,6072],{"class":63},[53,31070,31071],{"class":82},"$PROPS_NAME",[53,31073,30625],{"class":63},[53,31075,31076],{"class":55,"line":163},[53,31077,31078],{"class":2530},"# more than one file matching the criterias found\n",[53,31080,31081,31083,31085,31087,31090,31092,31094,31096,31098,31101,31103,31105,31107,31109,31112,31115,31117,31120,31122,31124],{"class":55,"line":186},[53,31082,2540],{"class":389},[53,31084,2307],{"class":82},[53,31086,12136],{"class":389},[53,31088,31089],{"class":82}," $(",[53,31091,13809],{"class":59},[53,31093,6072],{"class":63},[53,31095,30553],{"class":82},[53,31097,2704],{"class":63},[53,31099,31100],{"class":89}," -name",[53,31102,6072],{"class":63},[53,31104,31071],{"class":82},[53,31106,2704],{"class":63},[53,31108,5944],{"class":389},[53,31110,31111],{"class":59}," wc",[53,31113,31114],{"class":89}," -l",[53,31116,1665],{"class":82},[53,31118,31119],{"class":389},"-eq",[53,31121,25223],{"class":89},[53,31123,30455],{"class":82},[53,31125,6087],{"class":389},[53,31127,31128,31130,31133,31135,31138,31140,31142,31144,31146],{"class":55,"line":221},[53,31129,30462],{"class":89},[53,31131,31132],{"class":63}," \"No or more than one ",[53,31134,31071],{"class":82},[53,31136,31137],{"class":63}," was found under the webapp path $(",[53,31139,30616],{"class":59},[53,31141,30492],{"class":89},[53,31143,6072],{"class":63},[53,31145,30553],{"class":82},[53,31147,30625],{"class":63},[53,31149,31150,31152],{"class":55,"line":242},[53,31151,30470],{"class":89},[53,31153,2574],{"class":89},[53,31155,31156],{"class":55,"line":273},[53,31157,2579],{"class":389},[53,31159,31160],{"class":55,"line":279},[53,31161,31162],{"class":2530},"# copy the information about the current deployed version\n",[53,31164,31165,31168,31170,31173,31175,31177,31179],{"class":55,"line":496},[53,31166,31167],{"class":59},"cp",[53,31169,6072],{"class":63},[53,31171,31172],{"class":82},"$APP_PROPS",[53,31174,2704],{"class":63},[53,31176,6072],{"class":63},[53,31178,30864],{"class":82},[53,31180,30535],{"class":63},[649,31182,31184],{"id":31183},"save-the-war-file-itself-if-configured","Save the war file itself if configured",[18,31186,31187],{},"We remember, in the configuration file there are three possibilities what to do with the war file during the backup\nprocess:",[577,31189,31190,31193,31196],{},[580,31191,31192],{},"always save the war file",[580,31194,31195],{},"never save the war file",[580,31197,31198],{},"save the war file only if the current deployed version is a snapshot",[18,31200,31201,31202,31204,31205,31208],{},"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 ",[50,31203,30965],{}," file has to be read in and the variable ",[50,31206,31207],{},"$version","\nhas to be checked if it contains the string ‘SNAPSHOT’.",[43,31210,31212],{"className":29913,"code":31211,"language":29915,"meta":48,"style":48},"\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",[50,31213,31214,31218,31223,31245,31255,31260,31265,31285,31290,31295,31305,31310,31322,31332,31341,31346,31350,31355,31376,31385,31389,31410,31417,31443,31447],{"__ignoreMap":48},[53,31215,31216],{"class":55,"line":56},[53,31217,500],{"emptyLinePlaceholder":499},[53,31219,31220],{"class":55,"line":86},[53,31221,31222],{"class":2530},"# variable isn't empty\n",[53,31224,31225,31227,31229,31231,31234,31236,31239,31241,31243],{"class":55,"line":126},[53,31226,2540],{"class":389},[53,31228,2307],{"class":82},[53,31230,12136],{"class":389},[53,31232,31233],{"class":389}," -z",[53,31235,6072],{"class":63},[53,31237,31238],{"class":82},"$WAR",[53,31240,2704],{"class":63},[53,31242,30455],{"class":82},[53,31244,6087],{"class":389},[53,31246,31247,31250,31252],{"class":55,"line":163},[53,31248,31249],{"class":82},"SAVE_WAR",[53,31251,390],{"class":389},[53,31253,31254],{"class":63},"0\n",[53,31256,31257],{"class":55,"line":186},[53,31258,31259],{"class":2530},"# decide if war should be saved\n",[53,31261,31262],{"class":55,"line":221},[53,31263,31264],{"class":2530},"# if conf = save war only if current deployed version is a snapshot\n",[53,31266,31267,31269,31271,31273,31275,31277,31279,31281,31283],{"class":55,"line":242},[53,31268,2540],{"class":389},[53,31270,2307],{"class":82},[53,31272,2704],{"class":63},[53,31274,31238],{"class":82},[53,31276,2704],{"class":63},[53,31278,2548],{"class":389},[53,31280,2551],{"class":89},[53,31282,30455],{"class":82},[53,31284,6087],{"class":389},[53,31286,31287],{"class":55,"line":273},[53,31288,31289],{"class":2530},"# check if current deployed version is a snapshot\n",[53,31291,31292],{"class":55,"line":279},[53,31293,31294],{"class":2530},"# read in properties\n",[53,31296,31297,31299,31301,31303],{"class":55,"line":496},[53,31298,986],{"class":89},[53,31300,6072],{"class":63},[53,31302,31172],{"class":82},[53,31304,30535],{"class":63},[53,31306,31307],{"class":55,"line":503},[53,31308,31309],{"class":2530},"# $version has information about current deployed version\n",[53,31311,31312,31314,31316,31318,31320],{"class":55,"line":509},[53,31313,30374],{"class":389},[53,31315,6072],{"class":63},[53,31317,31207],{"class":82},[53,31319,2704],{"class":63},[53,31321,30384],{"class":389},[53,31323,31324,31327,31330],{"class":55,"line":515},[53,31325,31326],{"class":389}," *",[53,31328,31329],{"class":30389},"SNAPSHOT",[53,31331,685],{"class":389},[53,31333,31334,31337,31339],{"class":55,"line":521},[53,31335,31336],{"class":82}," SAVE_WAR",[53,31338,390],{"class":389},[53,31340,30217],{"class":63},[53,31342,31343],{"class":55,"line":527},[53,31344,31345],{"class":82}," ;;\n",[53,31347,31348],{"class":55,"line":533},[53,31349,30410],{"class":389},[53,31351,31352],{"class":55,"line":539},[53,31353,31354],{"class":2530},"# if conf = save war always\n",[53,31356,31357,31360,31362,31364,31366,31368,31370,31372,31374],{"class":55,"line":545},[53,31358,31359],{"class":389},"elif",[53,31361,2307],{"class":82},[53,31363,2704],{"class":63},[53,31365,31238],{"class":82},[53,31367,2704],{"class":63},[53,31369,2548],{"class":389},[53,31371,25223],{"class":89},[53,31373,30455],{"class":82},[53,31375,6087],{"class":389},[53,31377,31378,31381,31383],{"class":55,"line":2070},[53,31379,31380],{"class":82}," SAVE_WAR",[53,31382,390],{"class":389},[53,31384,30217],{"class":63},[53,31386,31387],{"class":55,"line":2075},[53,31388,2579],{"class":389},[53,31390,31391,31393,31395,31397,31400,31402,31404,31406,31408],{"class":55,"line":2081},[53,31392,2540],{"class":389},[53,31394,2307],{"class":82},[53,31396,2704],{"class":63},[53,31398,31399],{"class":82},"$SAVE_WAR",[53,31401,2704],{"class":63},[53,31403,2548],{"class":389},[53,31405,25223],{"class":89},[53,31407,30455],{"class":82},[53,31409,6087],{"class":389},[53,31411,31412,31414],{"class":55,"line":2087},[53,31413,30462],{"class":89},[53,31415,31416],{"class":63}," \"Backup war file: \"\n",[53,31418,31419,31422,31425,31427,31429,31432,31434,31437,31439,31441],{"class":55,"line":2092},[53,31420,31421],{"class":59}," cp",[53,31423,31424],{"class":89}," -v",[53,31426,6072],{"class":63},[53,31428,30553],{"class":82},[53,31430,31431],{"class":63},"\"/../",[53,31433,5009],{"class":89},[53,31435,31436],{"class":63},".war",[53,31438,6072],{"class":63},[53,31440,30864],{"class":82},[53,31442,30535],{"class":63},[53,31444,31445],{"class":55,"line":2097},[53,31446,2579],{"class":389},[53,31448,31449],{"class":55,"line":2103},[53,31450,2579],{"class":389},[649,31452,31454],{"id":31453},"save-the-database-data","Save the database data",[18,31456,31457,31458,31460],{},"For every element in ",[50,31459,30585],{}," a dump is created.",[18,31462,31463,31464,31466,31467,31469,31470,31473,31474,31476],{},"Due to the existing ",[50,31465,29903],{}," with access data information, ",[50,31468,29895],{}," can be called without the parameters ",[50,31471,31472],{},"user"," and\n",[50,31475,7523],{},". However it needs the following information:",[577,31478,31479,31485,31491],{},[580,31480,31481,31482,8526],{},"the path to the config file with the access data (",[50,31483,31484],{},"--defaults-file",[580,31486,31487,31488,8526],{},"the suffix (database name) which access data section of the config file should be used (",[50,31489,31490],{},"--defaults-group-suffix",[580,31492,31493],{},"the database name to know which database should be dumped",[18,31495,31496,31497,31500,31501,31504,31505,31507],{},"If something goes wrong during database dump (e.g. access is denied), the error is redirected to ",[50,31498,31499],{},"/dev/null"," because the\nscript has its own error handling. It checks if ",[50,31502,31503],{},"$?"," is 0 or not. ",[50,31506,31503],{}," 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.",[43,31509,31511],{"className":29913,"code":31510,"language":29915,"meta":48,"style":48},"\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",[50,31512,31513,31517,31531,31535,31540,31595,31600,31620,31651,31656,31661,31672,31677],{"__ignoreMap":48},[53,31514,31515],{"class":55,"line":56},[53,31516,500],{"emptyLinePlaceholder":499},[53,31518,31519,31522,31525,31528],{"class":55,"line":86},[53,31520,31521],{"class":389},"for",[53,31523,31524],{"class":82}," db ",[53,31526,31527],{"class":389},"in",[53,31529,31530],{"class":82}," $DB_NAME\n",[53,31532,31533],{"class":55,"line":126},[53,31534,30369],{"class":389},[53,31536,31537],{"class":55,"line":163},[53,31538,31539],{"class":2530}," # create dump and hide mysqldump errors due to own error handling\n",[53,31541,31542,31545,31548,31550,31552,31554,31557,31559,31562,31564,31566,31568,31570,31573,31575,31577,31579,31581,31584,31586,31589,31592],{"class":55,"line":186},[53,31543,31544],{"class":59}," mysqldump",[53,31546,31547],{"class":89}," --defaults-file=",[53,31549,2704],{"class":63},[53,31551,30600],{"class":82},[53,31553,2704],{"class":63},[53,31555,31556],{"class":89}," --defaults-group-suffix=",[53,31558,2704],{"class":63},[53,31560,31561],{"class":82},"$db",[53,31563,2704],{"class":63},[53,31565,6072],{"class":63},[53,31567,31561],{"class":82},[53,31569,2704],{"class":63},[53,31571,31572],{"class":389}," >",[53,31574,6072],{"class":63},[53,31576,30864],{"class":82},[53,31578,2628],{"class":63},[53,31580,31561],{"class":82},[53,31582,31583],{"class":63},"-",[53,31585,30785],{"class":82},[53,31587,31588],{"class":63},".dump.sql\"",[53,31590,31591],{"class":389}," 2>",[53,31593,31594],{"class":63},"/dev/null\n",[53,31596,31597],{"class":55,"line":221},[53,31598,31599],{"class":2530}," # creating dump was successful, so return value is 0\n",[53,31601,31602,31604,31606,31608,31610,31612,31614,31616,31618],{"class":55,"line":242},[53,31603,6369],{"class":389},[53,31605,2307],{"class":82},[53,31607,2704],{"class":63},[53,31609,31503],{"class":89},[53,31611,2704],{"class":63},[53,31613,2548],{"class":389},[53,31615,2551],{"class":89},[53,31617,30455],{"class":82},[53,31619,6087],{"class":389},[53,31621,31622,31624,31627,31629,31632,31634,31636,31638,31640,31642,31644,31646,31648],{"class":55,"line":273},[53,31623,2563],{"class":89},[53,31625,31626],{"class":63}," \"Create dump for database ",[53,31628,31561],{"class":82},[53,31630,31631],{"class":63},": $(",[53,31633,30616],{"class":59},[53,31635,30492],{"class":89},[53,31637,6072],{"class":63},[53,31639,30864],{"class":82},[53,31641,2628],{"class":63},[53,31643,31561],{"class":82},[53,31645,31583],{"class":63},[53,31647,30785],{"class":82},[53,31649,31650],{"class":63},".dump.sql\")\"\n",[53,31652,31653],{"class":55,"line":279},[53,31654,31655],{"class":2530}," # something went wrong while trying to create dump (e.g. access denied), so return value is not 0 but any other number\n",[53,31657,31658],{"class":55,"line":496},[53,31659,31660],{"class":389}," else\n",[53,31662,31663,31665,31668,31670],{"class":55,"line":503},[53,31664,2563],{"class":89},[53,31666,31667],{"class":63}," \"Problems encountered while trying to create dump for database ",[53,31669,31561],{"class":82},[53,31671,30535],{"class":63},[53,31673,31674],{"class":55,"line":509},[53,31675,31676],{"class":389}," fi\n",[53,31678,31679],{"class":55,"line":515},[53,31680,30415],{"class":389},[649,31682,31684],{"id":31683},"save-specified-files-andor-directories","Save specified files and/or directories",[18,31686,31687],{},"To move the specified files and/or directories to the backup directory, following steps are performed:",[577,31689,31690,31697,31704],{},[580,31691,31692,31693,31696],{},"make sure that the variable ",[50,31694,31695],{},"$CONTENT_TO_BE_MOVED"," isn’t empty",[580,31698,31699,31700,31703],{},"execute the ",[50,31701,31702],{},"move"," operation for each element",[580,31705,31706],{},"if an element doesn’t exist, print an error message",[43,31708,31710],{"className":29913,"code":31709,"language":29915,"meta":48,"style":48},"\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",[50,31711,31712,31716,31721,31741,31753,31757,31762,31796,31814,31831,31835,31849,31853,31857],{"__ignoreMap":48},[53,31713,31714],{"class":55,"line":56},[53,31715,500],{"emptyLinePlaceholder":499},[53,31717,31718],{"class":55,"line":86},[53,31719,31720],{"class":2530},"# make sure that variable $CONTENT_TO_BE_MOVED isn't empty\n",[53,31722,31723,31725,31727,31729,31731,31733,31735,31737,31739],{"class":55,"line":126},[53,31724,2540],{"class":389},[53,31726,2307],{"class":82},[53,31728,12136],{"class":389},[53,31730,31233],{"class":389},[53,31732,6072],{"class":63},[53,31734,31695],{"class":82},[53,31736,2704],{"class":63},[53,31738,30455],{"class":82},[53,31740,6087],{"class":389},[53,31742,31743,31745,31748,31750],{"class":55,"line":163},[53,31744,31521],{"class":389},[53,31746,31747],{"class":82}," i ",[53,31749,31527],{"class":389},[53,31751,31752],{"class":82}," $CONTENT_TO_BE_MOVED\n",[53,31754,31755],{"class":55,"line":186},[53,31756,30369],{"class":389},[53,31758,31759],{"class":55,"line":221},[53,31760,31761],{"class":2530}," # if the given content is a dir or a file, process else throw an error\n",[53,31763,31764,31766,31768,31770,31772,31775,31777,31779,31781,31783,31786,31788,31790,31792,31794],{"class":55,"line":242},[53,31765,6369],{"class":389},[53,31767,2307],{"class":82},[53,31769,5845],{"class":389},[53,31771,6072],{"class":63},[53,31773,31774],{"class":82},"$i",[53,31776,2704],{"class":63},[53,31778,30558],{"class":82},[53,31780,30561],{"class":389},[53,31782,2307],{"class":82},[53,31784,31785],{"class":389},"-f",[53,31787,6072],{"class":63},[53,31789,31774],{"class":82},[53,31791,2704],{"class":63},[53,31793,30455],{"class":82},[53,31795,6087],{"class":389},[53,31797,31798,31800,31803,31805,31807,31809,31811],{"class":55,"line":273},[53,31799,2563],{"class":89},[53,31801,31802],{"class":63}," \"Move $(",[53,31804,30616],{"class":59},[53,31806,30492],{"class":89},[53,31808,6072],{"class":63},[53,31810,31774],{"class":82},[53,31812,31813],{"class":63},"\") into backup dir\"\n",[53,31815,31816,31819,31821,31823,31825,31827,31829],{"class":55,"line":279},[53,31817,31818],{"class":59}," mv",[53,31820,6072],{"class":63},[53,31822,31774],{"class":82},[53,31824,2704],{"class":63},[53,31826,6072],{"class":63},[53,31828,30864],{"class":82},[53,31830,30535],{"class":63},[53,31832,31833],{"class":55,"line":496},[53,31834,31660],{"class":389},[53,31836,31837,31839,31842,31844,31847],{"class":55,"line":503},[53,31838,2563],{"class":89},[53,31840,31841],{"class":63}," \"Can't move ",[53,31843,31774],{"class":82},[53,31845,31846],{"class":63},": doesn't exist\"",[53,31848,1727],{"class":82},[53,31850,31851],{"class":55,"line":509},[53,31852,31676],{"class":389},[53,31854,31855],{"class":55,"line":515},[53,31856,30415],{"class":389},[53,31858,31859],{"class":55,"line":521},[53,31860,2579],{"class":389},[18,31862,31863,31864,31867,31868,31871],{},"These steps are similar for the files/directories to be copied, only the action (",[50,31865,31866],{},"copy",") and the parameter (\n",[50,31869,31870],{},"$CONTENT_TO_BE_COPIED",") are different.",[649,31873,31875],{"id":31874},"dont-forget-to-make-your-script-executable","Don’t forget to make your script executable 😉",[43,31877,31879],{"className":13667,"code":31878,"language":13669,"meta":48,"style":48},"chmod +x backup.sh\n",[50,31880,31881],{"__ignoreMap":48},[53,31882,31883],{"class":55,"line":56},[53,31884,31878],{},[2352,31886,31888],{"id":31887},"automation","Automation",[18,31890,31891],{},"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.",[2352,31893,31895],{"id":31894},"links","Links",[18,31897,31898],{},[585,31899,31902],{"href":29881,"rel":31900,"title":31901},[589],"Github Project","Github Project: automated-backup",[18,31904,31905],{},[585,31906,31909],{"href":29834,"rel":31907,"title":31908},[589],"Link to opening quote","Link to opening quote: Continuous Delivery vs. Continuous Deployment vs. Continuous-Integration",[607,31911,31912],{},"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":48,"searchDepth":86,"depth":86,"links":31914},[31915,31916,31917,31918,31919,31929,31930],{"id":29820,"depth":86,"text":29821},{"id":29839,"depth":86,"text":29840},{"id":29885,"depth":86,"text":29886},{"id":30047,"depth":86,"text":30048},{"id":30272,"depth":86,"text":30273,"children":31920},[31921,31922,31923,31924,31925,31926,31927,31928],{"id":30279,"depth":126,"text":30280},{"id":30418,"depth":126,"text":30419},{"id":30742,"depth":126,"text":30743},{"id":30958,"depth":126,"text":30959},{"id":31183,"depth":126,"text":31184},{"id":31453,"depth":126,"text":31454},{"id":31683,"depth":126,"text":31684},{"id":31874,"depth":126,"text":31875},{"id":31887,"depth":86,"text":31888},{"id":31894,"depth":86,"text":31895},[5568,613],"2013-04-10T12:01:45","https://synyx.de/blog/continuous-deployment-automatic-backup-script/",{},"/blog/continuous-deployment-automatic-backup-script",{"title":29811,"description":48},"blog/continuous-deployment-automatic-backup-script",[31887,3058,31939,31940,29895,31941],"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":31945,"title":31946,"author":31947,"body":31949,"category":32687,"date":32688,"description":32689,"extension":617,"link":32690,"meta":32691,"navigation":499,"path":32692,"seo":32693,"slug":31953,"stem":32695,"tags":32696,"teaser":32699,"__hash__":32700},"blog/blog/running-rxtx-on-your-raspberry-pi.md","Running RXTX on your Raspberry Pi",[31948],"graf",{"type":11,"value":31950,"toc":32679},[31951,31954,31969,31972,31987,31991,31994,32018,32022,32037,32040,32044,32058,32062,32065,32068,32198,32207,32210,32213,32291,32297,32331,32335,32338,32354,32357,32378,32381,32385,32388,32591,32594,32612,32622,32625,32673,32676],[14,31952,31946],{"id":31953},"running-rxtx-on-your-raspberry-pi",[18,31955,31956,31957,31962,31963,31968],{},"A few years ago, a good friend of mine installed a\nsmall ",[585,31958,31961],{"href":31959,"rel":31960,"title":31961},"http://en.wikipedia.org/wiki/Photovoltaic_system",[589],"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 ",[585,31964,31967],{"href":31965,"rel":31966,"title":31967},"http://en.wikipedia.org/wiki/Power_inverter",[589],"inverter"," in this system has a serial\nport interface to transmit data.",[18,31970,31971],{},"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.",[18,31973,31974,31975,31980,31981,31986],{},"So this blog post is about connecting a Raspberry Pi to inverters (or any other device) via RS232. We will use\nthe ",[585,31976,31979],{"href":31977,"rel":31978,"title":31979},"http://www.oracle.com/technetwork/java/index-jsp-141752.html",[589],"Java Communications API"," to\nread received data from the inverters. We don’t use\nthe ",[585,31982,31985],{"href":31983,"rel":31984,"title":31985},"http://www.savagehomeautomation.com/projects/raspberry-pi-installing-a-rs232-serial-port.html",[589],"integrated RS232 port","\nbecause we want to monitore more than just a signle inverter, so we have to use USB-RS232 adapters.",[2352,31988,31990],{"id":31989},"prepare-your-pi","Prepare your Pi",[18,31992,31993],{},"To demonstrate this post, I’m using the following hardware setup:",[577,31995,31996,31999,32002,32009,32012,32015],{},[580,31997,31998],{},"Raspberry Pi – Model B rev 2",[580,32000,32001],{},"SDCard SanDisk Ultra SDHC Class 10",[580,32003,32004],{},[585,32005,32008],{"href":32006,"rel":32007,"title":32008},"http://www.dlink.com/us/en/home-solutions/connect/usb-hubs/dub-h7-7-port-usb-2-0-hub",[589],"D-Link USB hub",[580,32010,32011],{},"Micro USB cable",[580,32013,32014],{},"USB-RS232 adapters",[580,32016,32017],{},"Alfa Networks USB WIFI adapter",[2352,32019,32021],{"id":32020},"connect-cables-and-stuff","Connect Cables and Stuff",[18,32023,32024,32025,32031,32032,32036],{},"First of all we have to get a Pi ready running a Linux with SSH access. The Raspberry guys recommend\nusing ",[585,32026,32030],{"href":32027,"rel":32028,"title":32029},"http://www.raspberrypi.org/downloads",[589],"Raspbian ","Raspbian “wheezy”"," with Hard-Float. Follow the Quick start\nguide ",[585,32033,32034],{"href":32034,"rel":32035},"http://www.raspberrypi.org/quick-start-guide",[589]," to setup your Pi.",[18,32038,32039],{},"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.",[2352,32041,32043],{"id":32042},"configure-wifi","Configure WIFI",[18,32045,32046,32047,32050,32051,17019,32054,32057],{},"Now it’s time to setup the WIFI connection. Install the wpasupplicant package via ",[50,32048,32049],{},"apt-get install wpasupplicant",". Then\ncopy paste ",[50,32052,32053],{},"/etc/network/interfaces",[50,32055,32056],{},"/etc/wpa_supplicant/wpa_supplicant.conf"," from my gists and adapt the files.",[2352,32059,32061],{"id":32060},"serial-adapters","Serial Adapters",[18,32063,32064],{},"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.",[18,32066,32067],{},"To check the USB-RS232 adapters are recognized by the Pi, run:",[43,32069,32071],{"className":45,"code":32070,"language":47,"meta":48,"style":48},"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",[50,32072,32073,32089,32133,32172,32185,32193],{"__ignoreMap":48},[53,32074,32075,32078,32081,32083,32086],{"class":55,"line":56},[53,32076,32077],{"class":59},"root@raspberrypi:/#",[53,32079,32080],{"class":63}," lsusb",[53,32082,5944],{"class":389},[53,32084,32085],{"class":59}," grep",[53,32087,32088],{"class":89}," 232\n",[53,32090,32091,32094,32097,32100,32103,32106,32109,32112,32115,32118,32121,32124,32127,32130],{"class":55,"line":86},[53,32092,32093],{"class":59},"Bus",[53,32095,32096],{"class":89}," 001",[53,32098,32099],{"class":63}," Device",[53,32101,32102],{"class":63}," 011:",[53,32104,32105],{"class":63}," ID",[53,32107,32108],{"class":63}," 0557:2008",[53,32110,32111],{"class":63}," ATEN",[53,32113,32114],{"class":63}," International",[53,32116,32117],{"class":63}," Co.,",[53,32119,32120],{"class":63}," Ltd",[53,32122,32123],{"class":63}," UC-232A",[53,32125,32126],{"class":63}," Serial",[53,32128,32129],{"class":63}," Port",[53,32131,32132],{"class":82}," [pl2303]\n",[53,32134,32135,32137,32139,32141,32144,32146,32149,32152,32155,32158,32161,32163,32166,32169],{"class":55,"line":126},[53,32136,32093],{"class":59},[53,32138,32096],{"class":89},[53,32140,32099],{"class":63},[53,32142,32143],{"class":63}," 010:",[53,32145,32105],{"class":63},[53,32147,32148],{"class":63}," 0403:6001",[53,32150,32151],{"class":63}," Future",[53,32153,32154],{"class":63}," Technology",[53,32156,32157],{"class":63}," Devices",[53,32159,32160],{"class":63}," International,",[53,32162,32120],{"class":63},[53,32164,32165],{"class":63}," FT232",[53,32167,32168],{"class":63}," USB-Serial",[53,32170,32171],{"class":82}," (UART) IC\n",[53,32173,32174,32176,32179,32182],{"class":55,"line":163},[53,32175,32077],{"class":59},[53,32177,32178],{"class":63}," ls",[53,32180,32181],{"class":63}," /dev/ttyUSB",[53,32183,32184],{"class":89},"*\n",[53,32186,32187,32190],{"class":55,"line":186},[53,32188,32189],{"class":59},"/dev/ttyUSB0",[53,32191,32192],{"class":63}," /dev/ttyUSB1\n",[53,32194,32195],{"class":55,"line":221},[53,32196,32197],{"class":59},"root@raspberrypi:/#\n",[18,32199,32200,32201,32203,32204,32206],{},"One important question is, which of the adapters is ",[50,32202,32189],{},"? Smart people will say, ",[50,32205,32189],{}," is the one,\nplugged in first – that’s right.",[18,32208,32209],{},"Lazy people (including myself) are using udev rules to create an alias per USB hub port.",[18,32211,32212],{},"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.",[43,32214,32216],{"className":45,"code":32215,"language":47,"meta":48,"style":48},"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",[50,32217,32218,32231,32263],{"__ignoreMap":48},[53,32219,32220,32222,32224,32226,32229],{"class":55,"line":56},[53,32221,32077],{"class":59},[53,32223,32178],{"class":63},[53,32225,31114],{"class":89},[53,32227,32228],{"class":63}," /dev/ttySerialPort",[53,32230,32184],{"class":89},[53,32232,32233,32236,32238,32240,32242,32244,32247,32250,32253,32256,32258,32260],{"class":55,"line":86},[53,32234,32235],{"class":59},"lrwxrwxrwx",[53,32237,25223],{"class":89},[53,32239,13779],{"class":63},[53,32241,13779],{"class":63},[53,32243,25264],{"class":89},[53,32245,32246],{"class":63}," Jan",[53,32248,32249],{"class":89}," 1",[53,32251,32252],{"class":89}," 1970",[53,32254,32255],{"class":63}," /dev/ttySerialPort1",[53,32257,4084],{"class":82},[53,32259,1084],{"class":389},[53,32261,32262],{"class":63}," ttyUSB1\n",[53,32264,32265,32267,32269,32271,32273,32275,32277,32279,32281,32284,32286,32288],{"class":55,"line":126},[53,32266,32235],{"class":59},[53,32268,25223],{"class":89},[53,32270,13779],{"class":63},[53,32272,13779],{"class":63},[53,32274,25264],{"class":89},[53,32276,32246],{"class":63},[53,32278,32249],{"class":89},[53,32280,32252],{"class":89},[53,32282,32283],{"class":63}," /dev/ttySerialPort2",[53,32285,4084],{"class":82},[53,32287,1084],{"class":389},[53,32289,32290],{"class":63}," ttyUSB0\n",[18,32292,32293,32294],{},"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 ",[50,32295,32296],{},"dialout",[43,32298,32300],{"className":45,"code":32299,"language":47,"meta":48,"style":48},"root@raspberrypi:/# useradd -m -d /home/rxtxpi -s /bin/bash -G dialout rxtxpi\n",[50,32301,32302],{"__ignoreMap":48},[53,32303,32304,32306,32309,32312,32314,32317,32319,32322,32325,32328],{"class":55,"line":56},[53,32305,32077],{"class":59},[53,32307,32308],{"class":63}," useradd",[53,32310,32311],{"class":89}," -m",[53,32313,2459],{"class":89},[53,32315,32316],{"class":63}," /home/rxtxpi",[53,32318,5820],{"class":89},[53,32320,32321],{"class":63}," /bin/bash",[53,32323,32324],{"class":89}," -G",[53,32326,32327],{"class":63}," dialout",[53,32329,32330],{"class":63}," rxtxpi\n",[2352,32332,32334],{"id":32333},"installing-software","Installing Software",[18,32336,32337],{},"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.",[18,32339,32340,32341,32347,32348,32353],{},"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 ",[585,32342,32346],{"href":32343,"rel":32344,"title":32345},"http://rxtx.qbang.org/",[589],"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 ",[585,32349,32350],{"href":32350,"rel":32351,"title":32352},"https://github.com/grafjo/rxtx",[589],"inofficial RXTX maven repository"," as the Maven\nrepository for RXTX.",[18,32355,32356],{},"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.",[43,32358,32360],{"className":45,"code":32359,"language":47,"meta":48,"style":48},"root@raspberrypi:/# apt-get install openjdk-6-jre librxtx-java\n",[50,32361,32362],{"__ignoreMap":48},[53,32363,32364,32366,32369,32372,32375],{"class":55,"line":56},[53,32365,32077],{"class":59},[53,32367,32368],{"class":63}," apt-get",[53,32370,32371],{"class":63}," install",[53,32373,32374],{"class":63}," openjdk-6-jre",[53,32376,32377],{"class":63}," librxtx-java\n",[18,32379,32380],{},"Now the Pi is ready to run a Java application 🙂",[2352,32382,32384],{"id":32383},"running-sample-application","Running Sample Application",[18,32386,32387],{},"The sample application just displays the plugged in serial adapters of your Pi. So switch to your desktop and build the\nsample application:",[43,32389,32391],{"className":45,"code":32390,"language":47,"meta":48,"style":48},"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",[50,32392,32393,32407,32418,32435,32450,32475,32487,32500,32510,32521,32526,32534,32541,32549,32554,32559,32564,32569,32577],{"__ignoreMap":48},[53,32394,32395,32398,32401,32404],{"class":55,"line":56},[53,32396,32397],{"class":59},"joo@jgraf:~$",[53,32399,32400],{"class":63}," git",[53,32402,32403],{"class":63}," clone",[53,32405,32406],{"class":63}," git@github.com:grafjo/rxtxpi.git\n",[53,32408,32409,32412,32415],{"class":55,"line":86},[53,32410,32411],{"class":59},"Cloning",[53,32413,32414],{"class":63}," into",[53,32416,32417],{"class":63}," 'rxtxpi'...\n",[53,32419,32420,32423,32426,32429,32432],{"class":55,"line":126},[53,32421,32422],{"class":59},"remote:",[53,32424,32425],{"class":63}," Counting",[53,32427,32428],{"class":63}," objects:",[53,32430,32431],{"class":63}," 25,",[53,32433,32434],{"class":63}," done.\n",[53,32436,32437,32439,32442,32444,32447],{"class":55,"line":163},[53,32438,32422],{"class":59},[53,32440,32441],{"class":63}," Compressing",[53,32443,32428],{"class":63},[53,32445,32446],{"class":63}," 100%",[53,32448,32449],{"class":82}," (16/16), done.\n",[53,32451,32452,32454,32457,32460,32463,32465,32468,32471,32473],{"class":55,"line":186},[53,32453,32422],{"class":59},[53,32455,32456],{"class":63}," Total",[53,32458,32459],{"class":89}," 25",[53,32461,32462],{"class":82}," (delta ",[53,32464,7598],{"class":89},[53,32466,32467],{"class":82},"), reused 24 (",[53,32469,32470],{"class":59},"delta",[53,32472,2551],{"class":89},[53,32474,685],{"class":82},[53,32476,32477,32480,32482,32484],{"class":55,"line":221},[53,32478,32479],{"class":59},"Receiving",[53,32481,32428],{"class":63},[53,32483,32446],{"class":63},[53,32485,32486],{"class":82}," (25/25), done.\n",[53,32488,32489,32492,32495,32497],{"class":55,"line":242},[53,32490,32491],{"class":59},"Resolving",[53,32493,32494],{"class":63}," deltas:",[53,32496,32446],{"class":63},[53,32498,32499],{"class":82}," (1/1), done.\n",[53,32501,32502,32504,32507],{"class":55,"line":273},[53,32503,32397],{"class":59},[53,32505,32506],{"class":63}," cd",[53,32508,32509],{"class":63}," rxtxpi/\n",[53,32511,32512,32515,32518],{"class":55,"line":279},[53,32513,32514],{"class":59},"joo@jgraf:~/rxtxpi$",[53,32516,32517],{"class":63}," gradle",[53,32519,32520],{"class":63}," distZip\n",[53,32522,32523],{"class":55,"line":496},[53,32524,32525],{"class":59},":compileJava\n",[53,32527,32528,32531],{"class":55,"line":503},[53,32529,32530],{"class":59},"warning:",[53,32532,32533],{"class":82}," [options] bootstrap class path not set in conjunction with -source 1.6\n",[53,32535,32536,32538],{"class":55,"line":509},[53,32537,7598],{"class":59},[53,32539,32540],{"class":63}," warning\n",[53,32542,32543,32546],{"class":55,"line":515},[53,32544,32545],{"class":59},":processResources",[53,32547,32548],{"class":63}," UP-TO-DATE\n",[53,32550,32551],{"class":55,"line":521},[53,32552,32553],{"class":59},":classes\n",[53,32555,32556],{"class":55,"line":527},[53,32557,32558],{"class":59},":jar\n",[53,32560,32561],{"class":55,"line":533},[53,32562,32563],{"class":59},":startScripts\n",[53,32565,32566],{"class":55,"line":539},[53,32567,32568],{"class":59},":distZip\n",[53,32570,32571,32574],{"class":55,"line":545},[53,32572,32573],{"class":59},"BUILD",[53,32575,32576],{"class":63}," SUCCESSFUL\n",[53,32578,32579,32582,32585,32588],{"class":55,"line":2070},[53,32580,32581],{"class":59},"Total",[53,32583,32584],{"class":63}," time:",[53,32586,32587],{"class":89}," 4.458",[53,32589,32590],{"class":63}," secs\n",[18,32592,32593],{},"Copy the application to your Pi:",[43,32595,32597],{"className":45,"code":32596,"language":47,"meta":48,"style":48},"joo@jgraf:~/rxtxpi$ scp build/distributions/rxtxpi.zip rxtxpi@raspberry:/home/rxtxpi\n",[50,32598,32599],{"__ignoreMap":48},[53,32600,32601,32603,32606,32609],{"class":55,"line":56},[53,32602,32514],{"class":59},[53,32604,32605],{"class":63}," scp",[53,32607,32608],{"class":63}," build/distributions/rxtxpi.zip",[53,32610,32611],{"class":63}," rxtxpi@raspberry:/home/rxtxpi\n",[18,32613,32614,32615,32618,32619,986],{},"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 ",[50,32616,32617],{},"DEFAULT_JVM_OPTS=\"-Dgnu.io.rxtx.SerialPorts=/dev/ttySerialPort1:/dev/ttySerialPort2\""," inside the\nrxtx_pi startup script ",[50,32620,32621],{},"rxtx_pi/bin/rxtx_pi",[18,32623,32624],{},"Ok – when every thing was installed successfully, we will get this output from our Java application using RXTX",[43,32626,32628],{"className":45,"code":32627,"language":47,"meta":48,"style":48},"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",[50,32629,32630,32644,32654,32659,32664],{"__ignoreMap":48},[53,32631,32632,32635,32638,32641],{"class":55,"line":56},[53,32633,32634],{"class":59},"rxtxpi@raspberrypi",[53,32636,32637],{"class":63}," ~/rxtx_pi/bin",[53,32639,32640],{"class":82}," $ ",[53,32642,32643],{"class":63},"./rxtx_pi\n",[53,32645,32646,32649,32651],{"class":55,"line":86},[53,32647,32648],{"class":82},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Looking ",[53,32650,31521],{"class":389},[53,32652,32653],{"class":82}," serial ports\n",[53,32655,32656],{"class":55,"line":126},[53,32657,32658],{"class":82},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort1\n",[53,32660,32661],{"class":55,"line":163},[53,32662,32663],{"class":82},"[main] INFO de.synyx.rxtxpi.SerialPortUtils - Found port: /dev/ttySerialPort2\n",[53,32665,32666,32668,32670],{"class":55,"line":186},[53,32667,32634],{"class":59},[53,32669,32637],{"class":63},[53,32671,32672],{"class":82}," $\n",[18,32674,32675],{},"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.",[607,32677,32678],{},"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":48,"searchDepth":86,"depth":86,"links":32680},[32681,32682,32683,32684,32685,32686],{"id":31989,"depth":86,"text":31990},{"id":32020,"depth":86,"text":32021},{"id":32042,"depth":86,"text":32043},{"id":32060,"depth":86,"text":32061},{"id":32333,"depth":86,"text":32334},{"id":32383,"depth":86,"text":32384},[996],"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":31946,"description":32694},"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",[290,32697,32698],"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…","IILUDO8EXdnYtZkxUr-up01We3gJyNea4Apbe5Kvx4A",{"id":32702,"title":32703,"author":32704,"body":32706,"category":33937,"date":33938,"description":33939,"extension":617,"link":33940,"meta":33941,"navigation":499,"path":33942,"seo":33943,"slug":32710,"stem":33945,"tags":33946,"teaser":33948,"__hash__":33949},"blog/blog/monitoring-nih-style-part-2.md","Monitoring – NIH style (part 2)",[32705],"jbuch",{"type":11,"value":32707,"toc":33931},[32708,32711,32721,32724,32728,32731,32747,32762,32766,32776,32779,32784,32787,32795,32798,32806,32809,32812,33047,33050,33059,33140,33143,33146,33248,33251,33254,33299,33313,33316,33789,33799,33808,33818,33821,33827,33830,33833,33837,33857,33869,33876,33879,33890,33896,33899,33908,33911,33925,33928],[14,32709,32703],{"id":32710},"monitoring-nih-style-part-2",[18,32712,32713,32714,32720],{},"This expands on the idea in\nthe ",[585,32715,32719],{"href":32716,"rel":32717,"title":32718},"http://blog.synyx.de/2013/02/monitoring-nih-style",[589],"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.",[18,32722,32723],{},"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.",[2352,32725,32727],{"id":32726},"prototype-2","Prototype #2",[18,32729,32730],{},"General idea:",[577,32732,32733,32741,32744],{},[580,32734,32735,32736,8526],{},"Web interface (",[585,32737,32740],{"href":32738,"rel":32739,"title":32740},"https://web.archive.org/web/20130301163513/http://square.github.com:80/cubism/",[589],"Cubism",[580,32742,32743],{},"real database",[580,32745,32746],{},"real programming language",[18,32748,32749,32750,32755,32756,32761],{},"Cubism (also as recently introduced into ",[585,32751,32754],{"href":32752,"rel":32753,"title":32754},"http://www.jolokia.org/",[589],"Jolokia",") is a wonderful Javascript library\nusing ",[585,32757,32760],{"href":32758,"rel":32759,"title":32760},"http://d3js.org/",[589],"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.",[649,32763,32765],{"id":32764},"client","Client",[18,32767,32768,32769,32775],{},"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 ",[585,32770,32774],{"href":32771,"rel":32772,"title":32773},"https://github.com/square/cubism/wiki/API-Reference",[589],"Cubism API Reference","API documentation"," on the Cubism wiki\nis immensely useful.",[18,32777,32778],{},"Parts of a Cubism page:",[577,32780,32781],{},[580,32782,32783],{},"A metric definition",[18,32785,32786],{},"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).",[577,32788,32789,32792],{},[580,32790,32791],{},"An HTML element on which cubism can append the graphs to.",[580,32793,32794],{},"A cubism context, which sets up cubism-specifics.",[18,32796,32797],{},"Here the graphs are set up, most importantly how much information will be displayed.",[577,32799,32800,32803],{},[580,32801,32802],{},"The metric instances themselves",[580,32804,32805],{},"A set of d3 commands to put the graphs inside the specified HTML element.",[18,32807,32808],{},"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.",[18,32810,32811],{},"HTML parts:",[43,32813,32815],{"className":6829,"code":32814,"language":6831,"meta":48,"style":48},"\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",[50,32816,32817,32825,32840,32847,32862,32866,32874,32889,32902,32921,32929,32956,32963,32973,32983,32991,32997,33005,33014,33022],{"__ignoreMap":48},[53,32818,32819,32821,32823],{"class":55,"line":56},[53,32820,6838],{"class":82},[53,32822,607],{"class":6841},[53,32824,6860],{"class":82},[53,32826,32827,32830,32833,32835,32838],{"class":55,"line":86},[53,32828,32829],{"class":389}," @import",[53,32831,32832],{"class":89}," url",[53,32834,1067],{"class":82},[53,32836,32837],{"class":5805},"//square.github.com/cubism/style.css",[53,32839,1079],{"class":82},[53,32841,32842,32845],{"class":55,"line":126},[53,32843,32844],{"class":59}," #mymetric",[53,32846,5795],{"class":82},[53,32848,32849,32852,32854,32857,32860],{"class":55,"line":163},[53,32850,32851],{"class":89}," min-height",[53,32853,5859],{"class":82},[53,32855,32856],{"class":89},"155",[53,32858,32859],{"class":389},"px",[53,32861,1727],{"class":82},[53,32863,32864],{"class":55,"line":186},[53,32865,7109],{"class":82},[53,32867,32868,32870,32872],{"class":55,"line":221},[53,32869,6958],{"class":82},[53,32871,607],{"class":6841},[53,32873,6860],{"class":82},[53,32875,32876,32878,32880,32882,32884,32887],{"class":55,"line":242},[53,32877,6838],{"class":82},[53,32879,6817],{"class":6841},[53,32881,6975],{"class":59},[53,32883,390],{"class":82},[53,32885,32886],{"class":63},"\"body\"",[53,32888,6860],{"class":82},[53,32890,32891,32893,32895,32898,32900],{"class":55,"line":273},[53,32892,6865],{"class":82},[53,32894,2352],{"class":6841},[53,32896,32897],{"class":82},">Metric:\u003C/",[53,32899,2352],{"class":6841},[53,32901,6860],{"class":82},[53,32903,32904,32906,32908,32910,32912,32915,32917,32919],{"class":55,"line":279},[53,32905,6865],{"class":82},[53,32907,6817],{"class":6841},[53,32909,6975],{"class":59},[53,32911,390],{"class":82},[53,32913,32914],{"class":63},"\"mymetric\"",[53,32916,6982],{"class":82},[53,32918,6817],{"class":6841},[53,32920,6860],{"class":82},[53,32922,32923,32925,32927],{"class":55,"line":496},[53,32924,6958],{"class":82},[53,32926,6817],{"class":6841},[53,32928,6860],{"class":82},[53,32930,32931,32933,32935,32937,32939,32942,32945,32947,32950,32952,32954],{"class":55,"line":503},[53,32932,6838],{"class":82},[53,32934,6502],{"class":6841},[53,32936,6871],{"class":59},[53,32938,390],{"class":82},[53,32940,32941],{"class":63},"\"text/javascript\"",[53,32943,32944],{"class":59}," src",[53,32946,390],{"class":82},[53,32948,32949],{"class":63},"\"http://d3js.org/d3.v2.js\"",[53,32951,6982],{"class":82},[53,32953,6502],{"class":6841},[53,32955,6860],{"class":82},[53,32957,32958,32960],{"class":55,"line":509},[53,32959,6838],{"class":82},[53,32961,32962],{"class":6841},"script\n",[53,32964,32965,32968,32970],{"class":55,"line":515},[53,32966,32967],{"class":59}," type",[53,32969,390],{"class":82},[53,32971,32972],{"class":63},"\"text/javascript\"\n",[53,32974,32975,32978,32980],{"class":55,"line":521},[53,32976,32977],{"class":59}," src",[53,32979,390],{"class":82},[53,32981,32982],{"class":63},"\"http://raw.github.com/square/cubism/master/cubism.v1.js\"\n",[53,32984,32985,32987,32989],{"class":55,"line":527},[53,32986,6982],{"class":82},[53,32988,6502],{"class":6841},[53,32990,6860],{"class":82},[53,32992,32993,32995],{"class":55,"line":533},[53,32994,6838],{"class":82},[53,32996,32962],{"class":6841},[53,32998,32999,33001,33003],{"class":55,"line":539},[53,33000,32967],{"class":59},[53,33002,390],{"class":82},[53,33004,32972],{"class":63},[53,33006,33007,33009,33011],{"class":55,"line":545},[53,33008,32977],{"class":59},[53,33010,390],{"class":82},[53,33012,33013],{"class":63},"\"http://code.jquery.com/jquery-1.8.2.min.js\"\n",[53,33015,33016,33018,33020],{"class":55,"line":2070},[53,33017,6982],{"class":82},[53,33019,6502],{"class":6841},[53,33021,6860],{"class":82},[53,33023,33024,33026,33028,33030,33032,33034,33036,33038,33041,33043,33045],{"class":55,"line":2075},[53,33025,6838],{"class":82},[53,33027,6502],{"class":6841},[53,33029,6871],{"class":59},[53,33031,390],{"class":82},[53,33033,32941],{"class":63},[53,33035,32944],{"class":59},[53,33037,390],{"class":82},[53,33039,33040],{"class":63},"\"mymetrics.js\"",[53,33042,6982],{"class":82},[53,33044,6502],{"class":6841},[53,33046,6860],{"class":82},[18,33048,33049],{},"This will set up your basic structure and can be prettified as much as you care to.",[18,33051,33052,33053,2304],{},"Next up is configuring the\ncubism ",[585,33054,33058],{"href":33055,"rel":33056,"title":33057},"https://github.com/square/cubism/wiki/Cubism#wiki-context",[589],"Cubism Context API","context",[43,33060,33062],{"className":13481,"code":33061,"language":13483,"meta":48,"style":48},"var context = cubism\n .context()\n .serverDelay(500)\n .clientDelay(100)\n .step(10e3)\n .size(960);\n",[50,33063,33064,33076,33084,33098,33112,33126],{"__ignoreMap":48},[53,33065,33066,33068,33071,33073],{"class":55,"line":56},[53,33067,24813],{"class":389},[53,33069,33070],{"class":82}," context ",[53,33072,390],{"class":389},[53,33074,33075],{"class":82}," cubism\n",[53,33077,33078,33080,33082],{"class":55,"line":86},[53,33079,13973],{"class":82},[53,33081,33058],{"class":59},[53,33083,14138],{"class":82},[53,33085,33086,33088,33091,33093,33096],{"class":55,"line":126},[53,33087,13973],{"class":82},[53,33089,33090],{"class":59},"serverDelay",[53,33092,1067],{"class":82},[53,33094,33095],{"class":89},"500",[53,33097,685],{"class":82},[53,33099,33100,33102,33105,33107,33110],{"class":55,"line":163},[53,33101,13973],{"class":82},[53,33103,33104],{"class":59},"clientDelay",[53,33106,1067],{"class":82},[53,33108,33109],{"class":89},"100",[53,33111,685],{"class":82},[53,33113,33114,33116,33119,33121,33124],{"class":55,"line":186},[53,33115,13973],{"class":82},[53,33117,33118],{"class":59},"step",[53,33120,1067],{"class":82},[53,33122,33123],{"class":89},"10e3",[53,33125,685],{"class":82},[53,33127,33128,33130,33133,33135,33138],{"class":55,"line":221},[53,33129,13973],{"class":82},[53,33131,33132],{"class":59},"size",[53,33134,1067],{"class":82},[53,33136,33137],{"class":89},"960",[53,33139,1079],{"class":82},[18,33141,33142],{},"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.",[18,33144,33145],{},"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:",[43,33147,33149],{"className":13481,"code":33148,"language":13483,"meta":48,"style":48},"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",[50,33150,33151,33165,33184,33217,33231,33244],{"__ignoreMap":48},[53,33152,33153,33155,33158,33160,33163],{"class":55,"line":56},[53,33154,24813],{"class":389},[53,33156,33157],{"class":82}," site ",[53,33159,390],{"class":389},[53,33161,33162],{"class":63}," \"TST\"",[53,33164,1727],{"class":82},[53,33166,33167,33169,33172,33174,33177,33179,33182],{"class":55,"line":86},[53,33168,24813],{"class":389},[53,33170,33171],{"class":82}," metricGenerator ",[53,33173,390],{"class":389},[53,33175,33176],{"class":59}," metricGeneratorForHost",[53,33178,1067],{"class":82},[53,33180,33181],{"class":63},"\"localhost:8080\"",[53,33183,1079],{"class":82},[53,33185,33186,33188,33191,33193,33196,33199,33202,33205,33207,33210,33212,33215],{"class":55,"line":126},[53,33187,24813],{"class":389},[53,33189,33190],{"class":82}," mReqTime ",[53,33192,390],{"class":389},[53,33194,33195],{"class":82}," metricGenerator.",[53,33197,33198],{"class":59},"metric",[53,33200,33201],{"class":82},"(site, ",[53,33203,33204],{"class":63},"\"maxRequestTime\"",[53,33206,4562],{"class":82},[53,33208,33209],{"class":59},"divide",[53,33211,1067],{"class":82},[53,33213,33214],{"class":89},"1000",[53,33216,1079],{"class":82},[53,33218,33219,33222,33225,33227,33229],{"class":55,"line":163},[53,33220,33221],{"class":82},"mReqTime.",[53,33223,33224],{"class":59},"toString",[53,33226,1245],{"class":389},[53,33228,13726],{"class":389},[53,33230,13502],{"class":82},[53,33232,33233,33235,33237,33239,33242],{"class":55,"line":186},[53,33234,13791],{"class":389},[53,33236,33157],{"class":82},[53,33238,24323],{"class":389},[53,33240,33241],{"class":63}," \" maxReqT\"",[53,33243,1727],{"class":82},[53,33245,33246],{"class":55,"line":221},[53,33247,13799],{"class":82},[18,33249,33250],{},"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.",[18,33252,33253],{},"Now we bind the metrics to a HTML element:",[43,33255,33257],{"className":6829,"code":33256,"language":6831,"meta":48,"style":48},"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",[50,33258,33259,33264,33269,33274,33279,33284,33289,33294],{"__ignoreMap":48},[53,33260,33261],{"class":55,"line":56},[53,33262,33263],{"class":82},"d3.select(\"#mymetric\").call(function(div) { div.append(\"div\") .attr(\"class\",\n",[53,33265,33266],{"class":55,"line":86},[53,33267,33268],{"class":82},"\"axis\") .call(context.axis().orient(\"top\")); div.selectAll(\".horizon\")\n",[53,33270,33271],{"class":55,"line":126},[53,33272,33273],{"class":82},".data([mReqTime]) .enter().append(\"div\") .attr(\"class\", \"horizon\")\n",[53,33275,33276],{"class":55,"line":163},[53,33277,33278],{"class":82},".call(context.horizon()); div.append(\"div\") .attr(\"class\", \"rule\")\n",[53,33280,33281],{"class":55,"line":186},[53,33282,33283],{"class":82},".call(context.rule()); }); // On mousemove, reposition the chart values to match\n",[53,33285,33286],{"class":55,"line":221},[53,33287,33288],{"class":82},"the rule. context.on(\"focus\", function(i) {\n",[53,33290,33291],{"class":55,"line":242},[53,33292,33293],{"class":82},"d3.selectAll(\".value\").style(\"right\", i == null ? null : context.size() - i +\n",[53,33295,33296],{"class":55,"line":273},[53,33297,33298],{"class":82},"\"px\"); });\n",[18,33300,33301,33302,33308,33309,33312],{},"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, ",[585,33303,33307],{"href":33304,"rel":33305,"title":33306},"https://github.com/square/cubism/wiki/Horizon#wiki-colors",[589],"Cubism Horizon API","color"," only being one of them.\nThe d3 ",[50,33310,33311],{},"data()"," function takes an array of one or more metric definitions.",[18,33314,33315],{},"Now for the metricGenerator, it is not bug free but it works for me TM.",[43,33317,33319],{"className":13481,"code":33318,"language":13483,"meta":48,"style":48},"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",[50,33320,33321,33338,33364,33376,33388,33411,33422,33447,33458,33466,33474,33481,33491,33498,33507,33514,33524,33531,33540,33547,33552,33571,33602,33620,33631,33644,33649,33656,33663,33680,33688,33693,33698,33703,33708,33727,33732,33737,33742,33754,33774,33778,33785],{"__ignoreMap":48},[53,33322,33323,33325,33327,33329,33331,33333,33336],{"class":55,"line":56},[53,33324,24813],{"class":389},[53,33326,33176],{"class":59},[53,33328,1245],{"class":389},[53,33330,13726],{"class":389},[53,33332,7040],{"class":82},[53,33334,33335],{"class":5805},"host",[53,33337,13628],{"class":82},[53,33339,33340,33342,33344,33346,33349,33351,33354,33357,33359,33362],{"class":55,"line":86},[53,33341,6369],{"class":389},[53,33343,7040],{"class":82},[53,33345,12136],{"class":389},[53,33347,33348],{"class":89},"arguments",[53,33350,986],{"class":82},[53,33352,33353],{"class":89},"length",[53,33355,33356],{"class":82},") host ",[53,33358,390],{"class":389},[53,33360,33361],{"class":63}," \"localhost:8080\"",[53,33363,1727],{"class":82},[53,33365,33366,33368,33371,33373],{"class":55,"line":126},[53,33367,13507],{"class":389},[53,33369,33370],{"class":82}," source ",[53,33372,390],{"class":389},[53,33374,33375],{"class":82}," {};\n",[53,33377,33378,33380,33383,33385],{"class":55,"line":163},[53,33379,13507],{"class":389},[53,33381,33382],{"class":82}," cubism_cubeFormatDate ",[53,33384,390],{"class":389},[53,33386,33387],{"class":82}," d3.time.format.iso;\n",[53,33389,33390,33393,33395,33397,33399,33401,33404,33406,33409],{"class":55,"line":186},[53,33391,33392],{"class":82}," source.",[53,33394,33198],{"class":59},[53,33396,1245],{"class":389},[53,33398,13726],{"class":389},[53,33400,7040],{"class":82},[53,33402,33403],{"class":5805},"site",[53,33405,99],{"class":82},[53,33407,33408],{"class":5805},"expression",[53,33410,13628],{"class":82},[53,33412,33413,33415,33418,33420],{"class":55,"line":221},[53,33414,13561],{"class":389},[53,33416,33417],{"class":82}," context.",[53,33419,33198],{"class":59},[53,33421,1139],{"class":82},[53,33423,33424,33427,33429,33432,33434,33437,33439,33441,33443,33445],{"class":55,"line":242},[53,33425,33426],{"class":389}," function",[53,33428,7040],{"class":82},[53,33430,33431],{"class":5805},"start",[53,33433,99],{"class":82},[53,33435,33436],{"class":5805},"stop",[53,33438,99],{"class":82},[53,33440,33118],{"class":5805},[53,33442,99],{"class":82},[53,33444,24507],{"class":5805},[53,33446,13628],{"class":82},[53,33448,33449,33452,33455],{"class":55,"line":273},[53,33450,33451],{"class":389}," var",[53,33453,33454],{"class":82}," url ",[53,33456,33457],{"class":389},"=\n",[53,33459,33460,33463],{"class":55,"line":279},[53,33461,33462],{"class":82}," host ",[53,33464,33465],{"class":389},"+\n",[53,33467,33468,33471],{"class":55,"line":496},[53,33469,33470],{"class":63}," \"/1.0/metric\"",[53,33472,33473],{"class":389}," +\n",[53,33475,33476,33479],{"class":55,"line":503},[53,33477,33478],{"class":63}," \"?site=\"",[53,33480,33473],{"class":389},[53,33482,33483,33486,33489],{"class":55,"line":509},[53,33484,33485],{"class":59}," encodeURIComponent",[53,33487,33488],{"class":82},"(site) ",[53,33490,33465],{"class":389},[53,33492,33493,33496],{"class":55,"line":515},[53,33494,33495],{"class":63}," \"&expression=\"",[53,33497,33473],{"class":389},[53,33499,33500,33502,33505],{"class":55,"line":521},[53,33501,33485],{"class":59},[53,33503,33504],{"class":82},"(expression) ",[53,33506,33465],{"class":389},[53,33508,33509,33512],{"class":55,"line":527},[53,33510,33511],{"class":63}," \"&start=\"",[53,33513,33473],{"class":389},[53,33515,33516,33519,33522],{"class":55,"line":533},[53,33517,33518],{"class":59}," cubism_cubeFormatDate",[53,33520,33521],{"class":82},"(start) ",[53,33523,33465],{"class":389},[53,33525,33526,33529],{"class":55,"line":539},[53,33527,33528],{"class":63}," \"&stop=\"",[53,33530,33473],{"class":389},[53,33532,33533,33535,33538],{"class":55,"line":545},[53,33534,33518],{"class":59},[53,33536,33537],{"class":82},"(stop) ",[53,33539,33465],{"class":389},[53,33541,33542,33545],{"class":55,"line":2070},[53,33543,33544],{"class":63}," \"&step=\"",[53,33546,33473],{"class":389},[53,33548,33549],{"class":55,"line":2075},[53,33550,33551],{"class":82}," step;\n",[53,33553,33554,33557,33559,33562,33564,33566,33569],{"class":55,"line":2081},[53,33555,33556],{"class":82}," d3.",[53,33558,75],{"class":59},[53,33560,33561],{"class":82},"(url, ",[53,33563,5789],{"class":389},[53,33565,7040],{"class":82},[53,33567,33568],{"class":5805},"data",[53,33570,13628],{"class":82},[53,33572,33573,33576,33578,33580,33583,33585,33587,33589,33591,33594,33596,33599],{"class":55,"line":2087},[53,33574,33575],{"class":389}," if",[53,33577,7040],{"class":82},[53,33579,12136],{"class":389},[53,33581,33582],{"class":82},"data) ",[53,33584,13904],{"class":389},[53,33586,13522],{"class":59},[53,33588,1067],{"class":82},[53,33590,24577],{"class":389},[53,33592,33593],{"class":59}," Error",[53,33595,1067],{"class":82},[53,33597,33598],{"class":63},"\"unable to load data\"",[53,33600,33601],{"class":82},"));\n",[53,33603,33604,33607,33609,33611,33613,33615,33618],{"class":55,"line":2092},[53,33605,33606],{"class":82}," data.",[53,33608,13815],{"class":59},[53,33610,1067],{"class":82},[53,33612,5789],{"class":389},[53,33614,7040],{"class":82},[53,33616,33617],{"class":5805},"d",[53,33619,13628],{"class":82},[53,33621,33622,33625,33628],{"class":55,"line":2097},[53,33623,33624],{"class":82}," cubism_cubeFormatDate.",[53,33626,33627],{"class":59},"parse",[53,33629,33630],{"class":82},"(d.date);\n",[53,33632,33633,33636,33638,33641],{"class":55,"line":2103},[53,33634,33635],{"class":82}," d.value ",[53,33637,390],{"class":389},[53,33639,33640],{"class":59}," parseInt",[53,33642,33643],{"class":82},"(d.value);\n",[53,33645,33646],{"class":55,"line":2109},[53,33647,33648],{"class":82}," });\n",[53,33650,33651,33654],{"class":55,"line":2115},[53,33652,33653],{"class":59}," callback",[53,33655,1139],{"class":82},[53,33657,33658,33661],{"class":55,"line":2120},[53,33659,33660],{"class":89}," null",[53,33662,7143],{"class":82},[53,33664,33665,33668,33670,33672,33674,33676,33678],{"class":55,"line":2946},[53,33666,33667],{"class":82}," data.",[53,33669,11192],{"class":59},[53,33671,1067],{"class":82},[53,33673,5789],{"class":389},[53,33675,7040],{"class":82},[53,33677,33617],{"class":5805},[53,33679,13628],{"class":82},[53,33681,33682,33685],{"class":55,"line":2952},[53,33683,33684],{"class":389}," return",[53,33686,33687],{"class":82}," d.value;\n",[53,33689,33690],{"class":55,"line":2964},[53,33691,33692],{"class":82}," }),\n",[53,33694,33695],{"class":55,"line":2973},[53,33696,33697],{"class":82}," );\n",[53,33699,33700],{"class":55,"line":2979},[53,33701,33702],{"class":82}," });\n",[53,33704,33705],{"class":55,"line":2990},[53,33706,33707],{"class":82}," },\n",[53,33709,33710,33713,33715,33717,33719,33722,33724],{"class":55,"line":10443},[53,33711,33712],{"class":63}," \"\"",[53,33714,24349],{"class":389},[53,33716,33157],{"class":82},[53,33718,24323],{"class":389},[53,33720,33721],{"class":63}," \" \"",[53,33723,24349],{"class":389},[53,33725,33726],{"class":82}," expression,\n",[53,33728,33729],{"class":55,"line":26443},[53,33730,33731],{"class":82}," );\n",[53,33733,33734],{"class":55,"line":26448},[53,33735,33736],{"class":82}," };\n",[53,33738,33739],{"class":55,"line":26453},[53,33740,33741],{"class":2530}," // Returns the Cube host.\n",[53,33743,33744,33746,33748,33750,33752],{"class":55,"line":26458},[53,33745,33392],{"class":82},[53,33747,33224],{"class":59},[53,33749,1245],{"class":389},[53,33751,13726],{"class":389},[53,33753,13502],{"class":82},[53,33755,33756,33758,33761,33763,33765,33767,33769,33771],{"class":55,"line":26463},[53,33757,13561],{"class":389},[53,33759,33760],{"class":63}," \"\"",[53,33762,24349],{"class":389},[53,33764,33157],{"class":82},[53,33766,24323],{"class":389},[53,33768,33721],{"class":63},[53,33770,24349],{"class":389},[53,33772,33773],{"class":82}," expression;\n",[53,33775,33776],{"class":55,"line":26468},[53,33777,33736],{"class":82},[53,33779,33780,33782],{"class":55,"line":26473},[53,33781,13791],{"class":389},[53,33783,33784],{"class":82}," source;\n",[53,33786,33787],{"class":55,"line":26478},[53,33788,13799],{"class":82},[18,33790,33791,33792,17540,33795,33798],{},"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",[50,33793,33794],{},"stop-start/stepping",[50,33796,33797],{},"d3.json(url, callback)"," gets the JSON, parses it and hands it over to the callback.",[43,33800,33802],{"className":13667,"code":33801,"language":13669,"meta":48,"style":48},"[{ site: \"TST\", date: \"2006-01-02T15:04:05.000Z\", value: \"12021\" }]\n",[50,33803,33804],{"__ignoreMap":48},[53,33805,33806],{"class":55,"line":56},[53,33807,33801],{},[18,33809,33810,33811,33814,33815,33817],{},"It takes each element of the array, converts the value to an int and finally (quite convoluted) hands an array of\nintegers to the ",[50,33812,33813],{},"callback(null, [12021])",". If it somehow fails to contact the server correctly it will call the\n",[50,33816,24507],{}," with an error.",[18,33819,33820],{},"This finishes up the client side of things. We just need a web-service which delivers the above JSON.",[18,33822,33823,33824],{},"Example Request:\n",[50,33825,33826],{},"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",[18,33828,33829],{},"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.",[18,33831,33832],{},"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.",[649,33834,33836],{"id":33835},"server","Server",[18,33838,33839,33840,33845,33846,7040,33851,4562],{},"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 ",[585,33841,33844],{"href":33842,"rel":33843,"title":33844},"http://golang.org/",[589],"Go"," language\nfrom ",[585,33847,33850],{"href":33848,"rel":33849,"title":33850},"http://en.wikipedia.org/wiki/Rob_Pike",[589],"Rob Pike",[585,33852,33856],{"href":33853,"rel":33854,"title":33855},"http://google.com/",[589],"Google","google",[18,33858,33859,33860,2304,33864],{},"To avoid all too much code show, I have pushed a (cleaned) version of my project\nto ",[585,33861,10976],{"href":33862,"rel":33863,"title":10976},"https://github.com/",[589],[585,33865,33866],{"href":33866,"rel":33867,"title":33868},"https://github.com/BuJo/cubismsource",[589],"cubismsource",[18,33870,33871,33872,33875],{},"There you can follow my ",[13401,33873,33874],{},"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.",[18,33877,33878],{},"The code is split into",[577,33880,33881,33884,33887],{},[580,33882,33883],{},"cubismsource: web-application which delivers the HTML site and the metrics from the database to cubism",[580,33885,33886],{},"jboss2sqlite: application to periodically save the jboss status into the database",[580,33888,33889],{},"jbossinfo: small library for handling the jboss status xml",[18,33891,33892],{},[1773,33893],{"alt":33894,"src":33895},"\"Screenshot from 2013-02-01 14:52:06\"","https://media.synyx.de/uploads//2013/02/Screenshot-from-2013-02-01-145206.png",[18,33897,33898],{},"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).",[18,33900,33901,33902,33907],{},"The result is quite close to something\nlike ",[585,33903,33906],{"href":33904,"rel":33905,"title":33906},"https://web.archive.org/web/20130325101848/http://square.github.com:80/cube/",[589],"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.",[18,33909,33910],{},"So, what did we gain by putting the odd minute here and there into this?",[577,33912,33913,33916,33919,33922],{},[580,33914,33915],{},"The need of a third monitor",[580,33917,33918],{},"Beautiful forms and colors – the co-workers’ envy",[580,33920,33921],{},"Knowledge of visualizing things via Cubism",[580,33923,33924],{},"But, we loose points for the added complexity (~650 LoC) – which makes it less accessible from outside.",[18,33926,33927],{},"As with my previous blog articles – this is less about a “solution”, more a way to understand and learn a little more.",[607,33929,33930],{},"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":48,"searchDepth":86,"depth":86,"links":33932},[33933],{"id":32726,"depth":86,"text":32727,"children":33934},[33935,33936],{"id":32764,"depth":126,"text":32765},{"id":33835,"depth":126,"text":33836},[4122,613],"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":32703,"description":33944},"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",[33947,26806,6501,18100],"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…","QdBT57Gka6GBNRe4Jparu1tOJeI9d1UPNP1j-oqjFvc",{"id":33951,"title":33952,"author":33953,"body":33955,"category":34313,"date":34314,"description":33962,"extension":617,"link":34315,"meta":34316,"navigation":499,"path":34317,"seo":34318,"slug":33959,"stem":34319,"tags":34320,"teaser":34323,"__hash__":34324},"blog/blog/how-sysadmins-monitor-your-java-application-with-jmx.md","How Sysadmins monitor your Java application with JMX",[33954],"ruessel",{"type":11,"value":33956,"toc":34304},[33957,33960,33963,33971,33975,33979,33982,33992,33998,34001,34004,34013,34016,34026,34035,34057,34061,34076,34080,34083,34104,34151,34156,34171,34188,34226,34231,34288,34293,34301],[14,33958,33952],{"id":33959},"how-sysadmins-monitor-your-java-application-with-jmx",[18,33961,33962],{},"some time ago Aljona showed",[18,33964,33965],{},[585,33966,33970],{"href":33967,"rel":33968,"title":33969},"http://blog.synyx.de/2012/05/how-to-monitor-and-manage-your-java-application-with-jmx/",[589],"JMX-Blog by Aljona ","how to monitor and manage your java application with jmx",[2352,33972,33974],{"id":33973},"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.",[649,33976,33978],{"id":33977},"initial-point","initial point:",[18,33980,33981],{},"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.",[649,33983,33985,33986,33991],{"id":33984},"use-jolokia-on-the-server-side","Use ",[585,33987,32754],{"href":33988,"rel":33989,"title":33990},"http://jolokia.org",[589],"JMX on Capsaicin"," on the server-side!",[18,33993,33994],{},[1773,33995],{"alt":33996,"src":33997},"\"jolokia\"","https://media.synyx.de/uploads//2013/02/jolokia.png",[18,33999,34000],{},"Why do i have to use an extra Agent for this? My applicationserver is able to use Mbeans.",[18,34002,34003],{},"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?",[18,34005,34006,34007,34012],{},"There are some more advantages like bulk-requests and it\nsupports ",[585,34008,34011],{"href":32738,"rel":34009,"title":34010},[589],"cubism on github","cubism.js","\nsince version 1.0.5 (Jonathan uses this to get some nicely visualized charts) … and it’s fast.",[18,34014,34015],{},"Just deploy it into your applicationserver it isn’t that big.",[18,34017,34018,34019,34025],{},"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 ",[585,34020,34024],{"href":34021,"rel":34022,"title":34023},"http://labs.consol.de/jmx4perl/",[589],"Jmx4Perl on ConsolLabs ","jmx4perl"," and decided to make use of this.",[18,34027,34028,34029,34034],{},"I installed ",[585,34030,34024],{"href":34031,"rel":34032,"title":34033},"http://search.cpan.org/~roland/jmx4perl/",[589],"Jmx4Perl on CPAN"," and read the documentation.",[43,34036,34038],{"className":29913,"code":34037,"language":29915,"meta":48,"style":48},"\ncpan[1]> install JMX::Jmx4Perl\n\n",[50,34039,34040,34044],{"__ignoreMap":48},[53,34041,34042],{"class":55,"line":56},[53,34043,500],{"emptyLinePlaceholder":499},[53,34045,34046,34049,34051,34054],{"class":55,"line":86},[53,34047,34048],{"class":59},"cpan[1]",[53,34050,4087],{"class":82},[53,34052,34053],{"class":63},"install",[53,34055,34056],{"class":63}," JMX::Jmx4Perl\n",[649,34058,34060],{"id":34059},"the-tools-brought-along-by-jmx4perl-are-really-very-useful","The tools brought along by jmx4perl are really very useful:",[3525,34062,34063,34070],{},[580,34064,34065,34066,34069],{},"The commandlinetool ",[27,34067,34068],{},"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.",[580,34071,34072,34073,34075],{},"With the commandlinetool ",[27,34074,34024],{}," you can easily test requests and get responses in various forms.",[649,34077,34079],{"id":34078},"time-to-hack","Time to hack:",[18,34081,34082],{},"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.",[3525,34084,34085,34088,34091,34098,34101],{},[580,34086,34087],{},"be sure you got the relevant permissions in jolokia (jolokia-access.xml)",[580,34089,34090],{},"search for older Nagios-Plugins and c’n’p the generic parts…",[580,34092,34093,34094],{},"search for the Mbean with j4psh or use type search of jolokia and look for interesting attributes/operations\n",[1773,34095],{"alt":34096,"src":34097},"\"j4psh_screenshot\"","https://media.synyx.de/uploads//2013/02/j4psh_screenshot.png",[580,34099,34100],{},"test the request you have in your mind with e.g jmx4perl",[580,34102,34103],{},"canonicalize the request",[43,34105,34109],{"className":34106,"code":34107,"language":34108,"meta":48,"style":48},"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",[50,34110,34111,34116,34121,34126,34131,34136,34141,34146],{"__ignoreMap":48},[53,34112,34113],{"class":55,"line":56},[53,34114,34115],{},"my $request = new JMX::Jmx4Perl::Request({\n",[53,34117,34118],{"class":55,"line":86},[53,34119,34120],{}," type => READ,\n",[53,34122,34123],{"class":55,"line":126},[53,34124,34125],{}," mbean => \"java.lang:type=Memory\",\n",[53,34127,34128],{"class":55,"line":163},[53,34129,34130],{}," attribute => \"HeapMemoryUsage\",\n",[53,34132,34133],{"class":55,"line":186},[53,34134,34135],{}," });\n",[53,34137,34138],{"class":55,"line":221},[53,34139,34140],{},"#print(Dumper($request));\n",[53,34142,34143],{"class":55,"line":242},[53,34144,34145],{},"my $response = $jmx->request($request);\n",[53,34147,34148],{"class":55,"line":273},[53,34149,34150],{},"#print(Dumper($response));\n",[3525,34152,34153],{"start":273},[580,34154,34155],{},"dereference the response as you need",[43,34157,34159],{"className":34106,"code":34158,"language":34108,"meta":48,"style":48},"my $used_memory=$response->value()->{'used'};\nmy $max_memory=$response->value()->{'max'};\n",[50,34160,34161,34166],{"__ignoreMap":48},[53,34162,34163],{"class":55,"line":56},[53,34164,34165],{},"my $used_memory=$response->value()->{'used'};\n",[53,34167,34168],{"class":55,"line":86},[53,34169,34170],{},"my $max_memory=$response->value()->{'max'};\n",[3525,34172,34173,34176,34179,34182,34185],{"start":496},[580,34174,34175],{},"start a heated debate about sensible values for warning and critical",[580,34177,34178],{},"deploy your Nagios-plugin",[580,34180,34181],{},"copy the script into your plugin-folder of your Nagios-server",[580,34183,34184],{},"install all missing libraries on the Nagios-server",[580,34186,34187],{},"define the command in commands.cfg",[43,34189,34191],{"className":45,"code":34190,"language":47,"meta":48,"style":48},"define command{\n command_name check_jmx4perl_heapspace.pl\n command_line $USER1$/check_jmx4perl_heapspace.pl $HOSTADDRESS$ $ARG1$\n}\n",[50,34192,34193,34200,34208,34222],{"__ignoreMap":48},[53,34194,34195,34197],{"class":55,"line":56},[53,34196,7125],{"class":59},[53,34198,34199],{"class":63}," command{\n",[53,34201,34202,34205],{"class":55,"line":86},[53,34203,34204],{"class":59}," command_name",[53,34206,34207],{"class":63}," check_jmx4perl_heapspace.pl\n",[53,34209,34210,34213,34216,34219],{"class":55,"line":126},[53,34211,34212],{"class":59}," command_line",[53,34214,34215],{"class":82}," $USER1$",[53,34217,34218],{"class":63},"/check_jmx4perl_heapspace.pl",[53,34220,34221],{"class":82}," $HOSTADDRESS$ $ARG1$\n",[53,34223,34224],{"class":55,"line":163},[53,34225,282],{"class":82},[3525,34227,34228],{"start":186},[580,34229,34230],{},"make use of the new command in your services.cfg (or whatever you’ve called your file)",[43,34232,34234],{"className":45,"code":34233,"language":47,"meta":48,"style":48},"\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",[50,34235,34236,34240,34247,34255,34263,34276,34284],{"__ignoreMap":48},[53,34237,34238],{"class":55,"line":56},[53,34239,500],{"emptyLinePlaceholder":499},[53,34241,34242,34244],{"class":55,"line":86},[53,34243,7125],{"class":59},[53,34245,34246],{"class":63}," service{\n",[53,34248,34249,34252],{"class":55,"line":126},[53,34250,34251],{"class":59}," use",[53,34253,34254],{"class":63}," remote-service\n",[53,34256,34257,34260],{"class":55,"line":163},[53,34258,34259],{"class":59}," host_name",[53,34261,34262],{"class":63}," jolokia-host\n",[53,34264,34265,34268,34271,34273],{"class":55,"line":186},[53,34266,34267],{"class":59}," service_description",[53,34269,34270],{"class":63}," HeapSpace",[53,34272,109],{"class":63},[53,34274,34275],{"class":63}," JavaApp\n",[53,34277,34278,34281],{"class":55,"line":221},[53,34279,34280],{"class":59}," check_command",[53,34282,34283],{"class":63}," check_jmx4perl_heapspace.pl!8084\n",[53,34285,34286],{"class":55,"line":242},[53,34287,282],{"class":82},[3525,34289,34290],{"start":521},[580,34291,34292],{},"lean back while watching the heapspace grow…",[18,34294,34295],{},[585,34296,34300],{"href":34297,"rel":34298,"title":34299},"https://github.com/zivis/Scripts/blob/master/check_jmx4perl_heapspace.pl",[589],"check_jmx4perl_heapspace.pl on github","get the complete Nagios-plugin from github",[607,34302,34303],{},"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":48,"searchDepth":86,"depth":86,"links":34305},[34306],{"id":33973,"depth":86,"text":33974,"children":34307},[34308,34309,34311,34312],{"id":33977,"depth":126,"text":33978},{"id":33984,"depth":126,"text":34310},"Use Jolokia on the server-side!",{"id":34059,"depth":126,"text":34060},{"id":34078,"depth":126,"text":34079},[4122],"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":33952,"description":33962},"blog/how-sysadmins-monitor-your-java-application-with-jmx",[7337,290,34321,34322,18100,18101,12072,34108,26512],"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…","MFqu5fP8lFxCs1VVecsJZpCxYTd8MOKoaLxMErncmaw",{"id":34326,"title":34327,"author":34328,"body":34329,"category":34766,"date":34767,"description":34768,"extension":617,"link":34769,"meta":34770,"navigation":499,"path":34771,"seo":34772,"slug":34774,"stem":34775,"tags":34776,"teaser":34781,"__hash__":34782},"blog/blog/setup-selenium-grid.md","Acceptance testing at synyx – Part 3",[11420],{"type":11,"value":34330,"toc":34756},[34331,34334,34349,34353,34367,34370,34374,34377,34381,34384,34464,34467,34473,34480,34526,34532,34541,34545,34551,34558,34569,34598,34601,34670,34674,34677,34693,34702,34706,34712,34720,34724,34727,34733,34736,34743,34750,34753],[14,34332,34327],{"id":34333},"acceptance-testing-at-synyx-part-3",[18,34335,34336,34337,34342,34343,34348],{},"After showing you ",[585,34338,34341],{"href":34339,"rel":34340},"http://blog.synyx.de/2013/01/remote-browsers/",[589],"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 ",[585,34344,34347],{"href":34345,"rel":34346},"http://blog.synyx.de/2013/01/atdd-at-synyx/",[589],"first part of the series"," for the greater context of this blog post.",[649,34350,34352],{"id":34351},"setting-up-the-grid","Setting up the Grid",[18,34354,34355,34356,34360,34361,34366],{},"As mentioned in ",[585,34357,34359],{"href":34339,"rel":34358},[589],"part 2",", its pretty straight forward to set up a\nselenium-grid by following the instructions at the ",[585,34362,34365],{"href":34363,"rel":34364},"http://code.google.com/p/selenium/wiki/Grid2",[589],"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.",[18,34368,34369],{},"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 ;).",[649,34371,34373],{"id":34372},"the-linux-machines","The Linux machines",[18,34375,34376],{},"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.",[649,34378,34380],{"id":34379},"starting-the-hub","Starting the Hub",[18,34382,34383],{},"Selenium provides a “runnable” jar file to start the server by something like",[43,34385,34387],{"className":29913,"code":34386,"language":29915,"meta":48,"style":48},"$ 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",[50,34388,34389,34409,34432,34452,34460],{"__ignoreMap":48},[53,34390,34391,34394,34397,34400,34403,34406],{"class":55,"line":56},[53,34392,34393],{"class":59},"$",[53,34395,34396],{"class":63}," java",[53,34398,34399],{"class":89}," -jar",[53,34401,34402],{"class":63}," selenium-server.jar",[53,34404,34405],{"class":89}," -role",[53,34407,34408],{"class":63}," hub\n",[53,34410,34411,34414,34417,34420,34423,34426,34429],{"class":55,"line":86},[53,34412,34413],{"class":59},"Jan",[53,34415,34416],{"class":63}," 16,",[53,34418,34419],{"class":89}," 2013",[53,34421,34422],{"class":63}," 2:14:39",[53,34424,34425],{"class":63}," PM",[53,34427,34428],{"class":63}," org.openqa.grid.selenium.GridLauncher",[53,34430,34431],{"class":63}," main\n",[53,34433,34434,34437,34440,34443,34446,34449],{"class":55,"line":126},[53,34435,34436],{"class":59},"Information:",[53,34438,34439],{"class":63}," Launching",[53,34441,34442],{"class":63}," a",[53,34444,34445],{"class":63}," selenium",[53,34447,34448],{"class":63}," grid",[53,34450,34451],{"class":63}," server\n",[53,34453,34454,34457],{"class":55,"line":163},[53,34455,34456],{"class":59},"2013-01-16",[53,34458,34459],{"class":63}," 14:14:40.662:INFO:osjs.Server:jetty-7.x.y-SNAPSHOT\n",[53,34461,34462],{"class":55,"line":186},[53,34463,5968],{"class":89},[18,34465,34466],{},"The server binds itself on the configured port and keeps running until you hit CTRL-C while printing out log statements\nto STDOUT.",[18,34468,34469,34470,986],{},"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 ",[50,34471,34472],{},"selenium-hub",[18,34474,34475,34476,34479],{},"For the sake of simplicity I put all my files to ",[50,34477,34478],{},"/opt/selenium/",". This might not be the best “unix-way” but doing so\nhelps me find all the files easily.",[43,34481,34483],{"className":29913,"code":34482,"language":29915,"meta":48,"style":48},"$ 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",[50,34484,34485,34494,34499,34504,34509,34521],{"__ignoreMap":48},[53,34486,34487,34489,34491],{"class":55,"line":56},[53,34488,34393],{"class":59},[53,34490,32178],{"class":63},[53,34492,34493],{"class":63}," /opt/selenium\n",[53,34495,34496],{"class":55,"line":86},[53,34497,34498],{"class":59},"hubconfig.json\n",[53,34500,34501],{"class":55,"line":126},[53,34502,34503],{"class":59},"upstart-selenium-hub\n",[53,34505,34506],{"class":55,"line":163},[53,34507,34508],{"class":59},"selenium-hub\n",[53,34510,34511,34514,34516,34518],{"class":55,"line":186},[53,34512,34513],{"class":59},"selenium-server.jar",[53,34515,4084],{"class":82},[53,34517,1084],{"class":389},[53,34519,34520],{"class":63}," selenium-server-standalone-2.26.0.jar\n",[53,34522,34523],{"class":55,"line":221},[53,34524,34525],{"class":59},"selenium-server-standalone-2.26.0.jar\n",[18,34527,34528,34529,4562],{},"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 (",[50,34530,34531],{},"ln -s selenium-server-standalone-2.26.0.jar selenium-server.jar",[18,34533,34534,34535,34540],{},"If you want to have a detailed look at the files you can check out\nthe ",[585,34536,34539],{"href":34537,"rel":34538},"https://media.synyx.de/uploads//2013/01/synyx-selenium-grid-scripts.tar.gz",[589],"archive containing all scripts"," I\nwrote.",[649,34542,34544],{"id":34543},"managing-the-hub-as-a-service","Managing the Hub as a service",[18,34546,34547,34548,986],{},"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 ",[50,34549,34550],{},"/etc/init.d/$servicename",[18,34552,34553,34554,34557],{},"My upstart-scripts are basically copied from the skeleton-file ubuntu brings (",[50,34555,34556],{},"/etc/init.d/skeleton",") adjusted for my\nneeds.",[18,34559,34560,34561,34564,34565,34568],{},"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 ",[50,34562,34563],{},"/opt/selenium"," and symlinked them to ",[50,34566,34567],{},"/etc/init.d/",". Then the script can be added to the systems\nrunlevels (so that it will be started automatically upon boot).",[43,34570,34572],{"className":29913,"code":34571,"language":29915,"meta":48,"style":48},"ln -s /opt/selenium/upstart-selenium-hub /etc/init.d/selenium-hub\nupdate-rc.d selenium-hub defaults\n",[50,34573,34574,34587],{"__ignoreMap":48},[53,34575,34576,34579,34581,34584],{"class":55,"line":56},[53,34577,34578],{"class":59},"ln",[53,34580,5820],{"class":89},[53,34582,34583],{"class":63}," /opt/selenium/upstart-selenium-hub",[53,34585,34586],{"class":63}," /etc/init.d/selenium-hub\n",[53,34588,34589,34592,34595],{"class":55,"line":86},[53,34590,34591],{"class":59},"update-rc.d",[53,34593,34594],{"class":63}," selenium-hub",[53,34596,34597],{"class":63}," defaults\n",[18,34599,34600],{},"So the hub can be now managed using the “service” command.",[43,34602,34604],{"className":29913,"code":34603,"language":29915,"meta":48,"style":48},"$ 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",[50,34605,34606,34621,34635,34648,34660],{"__ignoreMap":48},[53,34607,34608,34610,34613,34616,34618],{"class":55,"line":56},[53,34609,34393],{"class":59},[53,34611,34612],{"class":63}," sudo",[53,34614,34615],{"class":63}," service",[53,34617,34594],{"class":63},[53,34619,34620],{"class":63}," status\n",[53,34622,34623,34625,34627,34629,34632],{"class":55,"line":86},[53,34624,1058],{"class":59},[53,34626,34594],{"class":63},[53,34628,6924],{"class":63},[53,34630,34631],{"class":63}," not",[53,34633,34634],{"class":63}," running\n",[53,34636,34637,34639,34641,34643,34645],{"class":55,"line":126},[53,34638,34393],{"class":59},[53,34640,34612],{"class":63},[53,34642,34615],{"class":63},[53,34644,34594],{"class":63},[53,34646,34647],{"class":63}," start\n",[53,34649,34650,34652,34654,34656,34658],{"class":55,"line":163},[53,34651,34393],{"class":59},[53,34653,34612],{"class":63},[53,34655,34615],{"class":63},[53,34657,34594],{"class":63},[53,34659,34620],{"class":63},[53,34661,34662,34664,34666,34668],{"class":55,"line":186},[53,34663,1058],{"class":59},[53,34665,34594],{"class":63},[53,34667,6924],{"class":63},[53,34669,34634],{"class":63},[649,34671,34673],{"id":34672},"doing-the-same-for-nodes","Doing the same for Nodes",[18,34675,34676],{},"The procedure for nodes is pretty much the same as for the hub but it has some smaller differences:",[18,34678,34679,34680,34683,34684,34689,34690,34692],{},"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 ",[50,34681,34682],{},"DISPLAY","\nenvironment-variable. Also selenium-server needs a system-property where to find the chromedriver\nbinary (",[585,34685,34688],{"href":34686,"rel":34687},"http://code.google.com/p/selenium/wiki/ChromeDriver",[589],"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 ",[50,34691,34563],{}," and updated my runlevels.",[18,34694,34695,34696,34701],{},"Also we use ",[585,34697,34700],{"href":34698,"rel":34699},"http://en.wikipedia.org/wiki/Vino_%28VNC_server%29",[589],"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.",[649,34703,34705],{"id":34704},"configuring-the-grid","Configuring the grid",[18,34707,34708,34709,34711],{},"Selenium-Server can be configured using command-line arguments or by JSON. I preferred the JSON way and added\nconfiguration-files to ",[50,34710,34478],{}," 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.",[18,34713,34714,34715,986],{},"The available parameters as well as defaults can be best looked up at\nthe ",[585,34716,34719],{"href":34717,"rel":34718},"http://code.google.com/p/selenium/source/browse/#git%2Fjava%2Fserver%2Fsrc%2Forg%2Fopenqa%2Fgrid%2Fcommon%2Fdefaults",[589],"selenium svn",[649,34721,34723],{"id":34722},"getting-some-browsers","Getting some browsers",[18,34725,34726],{},"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.",[18,34728,34729],{},[1773,34730],{"alt":34731,"src":34732},"Console of a Selenium Hub","https://media.synyx.de/uploads//2013/01/grid-console.png",[649,34734,34735],{"id":33058},"Context",[18,34737,34738,34739,986],{},"You request these Browsers using the RemoteWebDriver\nas ",[585,34740,34742],{"href":34339,"rel":34741},[589],"described in the last post",[18,34744,34745,34746,986],{},"In case you want do to something like this you can find all files I mentioned here to\ndownload: ",[585,34747,34749],{"href":34537,"rel":34748},[589],"synyx-selenium-grid-scripts.tar.gz",[18,34751,34752],{},"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…",[607,34754,34755],{},"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":48,"searchDepth":86,"depth":86,"links":34757},[34758,34759,34760,34761,34762,34763,34764,34765],{"id":34351,"depth":126,"text":34352},{"id":34372,"depth":126,"text":34373},{"id":34379,"depth":126,"text":34380},{"id":34543,"depth":126,"text":34544},{"id":34672,"depth":126,"text":34673},{"id":34704,"depth":126,"text":34705},{"id":34722,"depth":126,"text":34723},{"id":33058,"depth":126,"text":34735},[613],"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":34327,"description":34773},"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",[34777,34778,34779,34780,9338,22400],"cloud","grid","linux","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":34784,"title":34785,"author":34786,"body":34787,"category":35158,"date":35159,"description":35160,"extension":617,"link":35161,"meta":35162,"navigation":499,"path":35163,"seo":35164,"slug":35166,"stem":35167,"tags":35168,"teaser":35170,"__hash__":35171},"blog/blog/remote-browsers.md","Acceptance testing at synyx – Part 2",[11420],{"type":11,"value":34788,"toc":35150},[34789,34792,34798,34802,34811,34814,34817,34820,34829,34832,34835,34841,34845,34851,34857,34896,34900,34907,34950,34959,35104,35117,35120,35134,35141,35145,35148],[14,34790,34785],{"id":34791},"acceptance-testing-at-synyx-part-2",[18,34793,19757,34794,34797],{},[585,34795,34347],{"href":34345,"rel":34796},[589]," 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.",[2352,34799,34801],{"id":34800},"running-browsers-elsewhere-selenium-grid","Running browsers elsewhere – Selenium GRID",[18,34803,34804,34805,34810],{},"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 ",[585,34806,34809],{"href":34807,"rel":34808},"http://jenkins-ci.org/",[589],"Jenkins"," is a headless server where no browsers can be installed. Therefore we also\nneed the remoting capability for Jenkins.",[18,34812,34813],{},"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 ;-).",[18,34815,34816],{},"So in my opinion the best choice to execute tests is using Seleniums RemoteWebdriver.",[649,34818,34819],{"id":34780},"RemoteWebDriver",[18,34821,34822,34823,34828],{},"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 ",[585,34824,34827],{"href":34825,"rel":34826},"http://code.google.com/p/selenium/wiki/RemoteWebDriverServer",[589],"Seleniums documentation"," for further details.",[18,34830,34831],{},"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.",[18,34833,34834],{},"The following graphic shows you how a setup using Selenium Grid (hub and nodes) could look like:",[18,34836,34837],{},[1773,34838],{"alt":34839,"src":34840},"How selenium grid looks like","https://media.synyx.de/uploads//2013/01/selenium-grid.png",[649,34842,34844],{"id":34843},"requesting-a-browser-from-the-grid","Requesting a browser from the grid",[18,34846,34847,34848,986],{},"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 ",[50,34849,34850],{},"DesiredCapabilities",[18,34852,34853,34854],{},"The following code snippet shows how to request a chrome version 20 on any operating system from a selenium-server\nlistening to ",[50,34855,34856],{},"http://localhost:4444",[43,34858,34860],{"className":288,"code":34859,"language":290,"meta":48,"style":48},"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",[50,34861,34862,34867,34872,34877,34882,34887,34892],{"__ignoreMap":48},[53,34863,34864],{"class":55,"line":56},[53,34865,34866],{},"DesiredCapabilities cap = new DesiredCapabilities(\"chrome\", \"20\", Platform.ANY);\n",[53,34868,34869],{"class":55,"line":86},[53,34870,34871],{},"// url of selenium-hub/server\n",[53,34873,34874],{"class":55,"line":126},[53,34875,34876],{},"URL url = new URL(\"http://localhost:4444\");\n",[53,34878,34879],{"class":55,"line":163},[53,34880,34881],{},"WebDriver driver = new RemoteWebDriver(url, cap);\n",[53,34883,34884],{"class":55,"line":186},[53,34885,34886],{},"driver.open(targetSystemUrl);\n",[53,34888,34889],{"class":55,"line":221},[53,34890,34891],{},"driver.findElement(By.id(\"number\")).sendKeys(\"42\");\n",[53,34893,34894],{"class":55,"line":242},[53,34895,5968],{},[649,34897,34899],{"id":34898},"execute-tests-with-different-browsers","Execute tests with different browsers",[18,34901,34902,34903,34906],{},"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",[50,34904,34905],{},"-DbrowserProperties=firefox.properties"," to the build commandline.",[43,34908,34910],{"className":288,"code":34909,"language":290,"meta":48,"style":48},"\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",[50,34911,34912,34916,34921,34926,34931,34936,34941,34946],{"__ignoreMap":48},[53,34913,34914],{"class":55,"line":56},[53,34915,500],{"emptyLinePlaceholder":499},[53,34917,34918],{"class":55,"line":86},[53,34919,34920],{},"String fileName = System.getProperty(\"browserProperties\");\n",[53,34922,34923],{"class":55,"line":126},[53,34924,34925],{},"Properties p = loadProperties(fileName);\n",[53,34927,34928],{"class":55,"line":163},[53,34929,34930],{},"DesiredCapabilities c = new DesiredCapabilities(\n",[53,34932,34933],{"class":55,"line":186},[53,34934,34935],{}," p.getProperty(\"browser.name\"),\n",[53,34937,34938],{"class":55,"line":221},[53,34939,34940],{}," p.getProperty(\"browser.version\"),\n",[53,34942,34943],{"class":55,"line":242},[53,34944,34945],{}," p.getProperty(\"browser.platform\")\n",[53,34947,34948],{"class":55,"line":273},[53,34949,1079],{},[18,34951,34952,34953,34958],{},"I use this approach to bind the ",[585,34954,34957],{"href":34955,"rel":34956},"http://maven.apache.org/surefire/maven-surefire-plugin/",[589],"maven-surefire-plugin","\nmultiple times to the test lifecycle phase: once for each browser.",[43,34960,34962],{"className":1980,"code":34961,"language":1982,"meta":48,"style":48},"\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",[50,34963,34964,34969,34974,34979,34984,34989,34994,34999,35004,35009,35013,35018,35023,35028,35033,35037,35042,35046,35051,35055,35059,35063,35067,35072,35076,35081,35085,35089,35094,35099],{"__ignoreMap":48},[53,34965,34966],{"class":55,"line":56},[53,34967,34968],{},"\u003Cplugin>\n",[53,34970,34971],{"class":55,"line":86},[53,34972,34973],{}," \u003Cgroupid>org.apache.maven.plugins\u003C/groupid>\n",[53,34975,34976],{"class":55,"line":126},[53,34977,34978],{}," \u003Cartifactid>maven-surefire-plugin\u003C/artifactid>\n",[53,34980,34981],{"class":55,"line":163},[53,34982,34983],{}," \u003Cversion>2.12\u003C/version>\n",[53,34985,34986],{"class":55,"line":186},[53,34987,34988],{}," \u003Cexecutions>\n",[53,34990,34991],{"class":55,"line":221},[53,34992,34993],{}," \u003Cexecution>\n",[53,34995,34996],{"class":55,"line":242},[53,34997,34998],{}," \u003Cid>default-test\u003C/id>\n",[53,35000,35001],{"class":55,"line":273},[53,35002,35003],{}," \u003Cphase>test\u003C/phase>\n",[53,35005,35006],{"class":55,"line":279},[53,35007,35008],{}," \u003Cgoals>\u003Cgoal>test\u003C/goal>\u003C/goals>\n",[53,35010,35011],{"class":55,"line":496},[53,35012,29271],{},[53,35014,35015],{"class":55,"line":503},[53,35016,35017],{}," \u003Csystempropertyvariables>\n",[53,35019,35020],{"class":55,"line":509},[53,35021,35022],{}," \u003Cbrowserproperties>chrome.properties\u003C/browserproperties>\n",[53,35024,35025],{"class":55,"line":515},[53,35026,35027],{}," \u003C/systempropertyvariables>\n",[53,35029,35030],{"class":55,"line":521},[53,35031,35032],{}," \u003Creportnamesuffix>chrome\u003C/reportnamesuffix>\n",[53,35034,35035],{"class":55,"line":527},[53,35036,29296],{},[53,35038,35039],{"class":55,"line":533},[53,35040,35041],{}," \u003C/execution>\n",[53,35043,35044],{"class":55,"line":539},[53,35045,34993],{},[53,35047,35048],{"class":55,"line":545},[53,35049,35050],{}," \u003Cid>test-firefox\u003C/id>\n",[53,35052,35053],{"class":55,"line":2070},[53,35054,35003],{},[53,35056,35057],{"class":55,"line":2075},[53,35058,35008],{},[53,35060,35061],{"class":55,"line":2081},[53,35062,29271],{},[53,35064,35065],{"class":55,"line":2087},[53,35066,35017],{},[53,35068,35069],{"class":55,"line":2092},[53,35070,35071],{}," \u003Cbrowserproperties>firefox.properties\u003C/browserproperties>\n",[53,35073,35074],{"class":55,"line":2097},[53,35075,35027],{},[53,35077,35078],{"class":55,"line":2103},[53,35079,35080],{}," \u003Creportnamesuffix>firefox\u003C/reportnamesuffix>\n",[53,35082,35083],{"class":55,"line":2109},[53,35084,29296],{},[53,35086,35087],{"class":55,"line":2115},[53,35088,35041],{},[53,35090,35091],{"class":55,"line":2120},[53,35092,35093],{}," \u003C!-- ... more browsers here ... -->\n",[53,35095,35096],{"class":55,"line":2946},[53,35097,35098],{}," \u003C/executions>\n",[53,35100,35101],{"class":55,"line":2952},[53,35102,35103],{},"\u003C/plugin>\n",[18,35105,35106,35107,35112,35113,35116],{},"An alternative to this could be to wrap the configuration\nwithin ",[585,35108,35111],{"href":35109,"rel":35110},"http://maven.apache.org/guides/introduction/introduction-to-profiles.html",[589],"Maven profiles"," so that you can\ncontrol which browsers should be used by ",[50,35114,35115],{},"mvn test -Pfirefox"," and so on.",[18,35118,35119],{},"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.",[18,35121,35122,35123,35126,35127,35130,35131,35133],{},"It worked out pretty well to have the infrastructural code within an abstract baseclass for all tests. This class uses\n",[50,35124,35125],{},"@Befor","e or ",[50,35128,35129],{},"@BeforeClass"," to read needed capabilities from a properties-file, set up ",[50,35132,34819],{}," and point it\nto the system under test. Of course something like a JUnit Rule would also work.",[18,35135,35136,35137,35140],{},"Either way the actual tests don’t have to care about how the WebDriver they use is instanciated: They just use the\nbaseclasses ",[50,35138,35139],{},"getDriver()"," and execute the real testing-code.",[649,35142,35144],{"id":35143},"next-part","Next Part",[18,35146,35147],{},"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.",[607,35149,989],{},{"title":48,"searchDepth":86,"depth":86,"links":35151},[35152],{"id":34800,"depth":86,"text":34801,"children":35153},[35154,35155,35156,35157],{"id":34780,"depth":126,"text":34819},{"id":34843,"depth":126,"text":34844},{"id":34898,"depth":126,"text":34899},{"id":35143,"depth":126,"text":35144},[613],"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":34785,"description":35165},"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",[34777,34778,34780,9338,22400,35169],"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.…","Iij9QBAjGU3XNz4FqFyhKCr9XEjfEWyafsOQsH6TnHI",{"id":35173,"title":35174,"author":35175,"body":35176,"category":35282,"date":35283,"description":48,"extension":617,"link":35284,"meta":35285,"navigation":499,"path":35286,"seo":35287,"slug":35288,"stem":35289,"tags":35290,"teaser":35293,"__hash__":35294},"blog/blog/atdd-at-synyx.md","Acceptance testing at synyx – Part 1",[11420],{"type":11,"value":35177,"toc":35276},[35178,35181,35185,35194,35197,35201,35211,35214,35217,35226,35229,35233,35236,35273],[14,35179,35174],{"id":35180},"acceptance-testing-at-synyx-part-1",[2352,35182,35184],{"id":35183},"overview-why-and-how-we-do-web-testing","Overview – Why and how we do web-testing",[18,35186,35187,35188,35193],{},"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 ",[585,35189,35192],{"href":35190,"rel":35191},"http://de.wikipedia.org/wiki/TDD",[589],"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.",[18,35195,35196],{},"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).",[649,35198,35200],{"id":35199},"arguments-to-do-it","Arguments to do it",[18,35202,35203,35204,35210],{},"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 ",[585,35205,35209],{"href":35206,"rel":35207,"title":35208},"http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/",[589],"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.",[18,35212,35213],{},"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.",[18,35215,35216],{},"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).",[18,35218,35219,35220,35225],{},"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 ",[585,35221,35224],{"href":35222,"rel":35223},"http://www.webopedia.com/TERM/B/Black_Box_Testing.html",[589],"complete blackbox"," (without knowledge\nof the internals of the application) test.",[18,35227,35228],{},"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.",[649,35230,35232],{"id":35231},"how-we-do-it-overview","How we do it – Overview",[18,35234,35235],{},"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.",[577,35237,35238,35246,35249,35252,35255,35264,35267],{},[580,35239,35240,35241],{},"Tests are written in Java/JUnit using ",[585,35242,35245],{"href":35243,"rel":35244},"http://seleniumhq.org/",[589],"Selenium Webdriver",[580,35247,35248],{},"Seleniums RemoteWebdriver allows the browser to run on another host as the test",[580,35250,35251],{},"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",[580,35253,35254],{},"The tests are executed automatically several times – once for each browser we want to ensure compability with",[580,35256,35257,35258,35263],{},"Tests are written in ",[585,35259,35262],{"href":35260,"rel":35261},"http://dannorth.net/introducing-bdd/",[589],"BDD-style"," and use abstractions of actions (Steps) and\npages",[580,35265,35266],{},"Tests are reported in a nice “manager-friendly” way including pie charts and screenshots",[580,35268,35269,35272],{},[585,35270,34809],{"href":34807,"rel":35271},[589]," executes these tests and generates the report continuously against a system that is\nautomatically deployed (continuous deployment)",[18,35274,35275],{},"So stay tuned for detailed information about ATDD / webtests at synyx during the next weeks.",{"title":48,"searchDepth":86,"depth":86,"links":35277},[35278],{"id":35183,"depth":86,"text":35184,"children":35279},[35280,35281],{"id":35199,"depth":126,"text":35200},{"id":35231,"depth":126,"text":35232},[613],"2013-01-23T10:47:24","https://synyx.de/blog/atdd-at-synyx/",{},"/blog/atdd-at-synyx",{"title":35174,"description":48},"atdd-at-synyx","blog/atdd-at-synyx",[28832,35291,28834,9338,35292,22400,35169],"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":35296,"title":35297,"author":35298,"body":35299,"category":35396,"date":35397,"description":35398,"extension":617,"link":35399,"meta":35400,"navigation":499,"path":35401,"seo":35402,"slug":35303,"stem":35403,"tags":35404,"teaser":35412,"__hash__":35413},"blog/blog/android-size-depending-orientation-lock.md","Android: size depending orientation lock",[12861],{"type":11,"value":35300,"toc":35394},[35301,35304,35307,35310,35319,35322,35325,35380,35383,35386,35389,35392],[14,35302,35297],{"id":35303},"android-size-depending-orientation-lock",[18,35305,35306],{},"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.",[18,35308,35309],{},"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:",[43,35311,35313],{"className":1980,"code":35312,"language":1982,"meta":48,"style":48},"android:screenOrientation=\"nosensor\"\n",[50,35314,35315],{"__ignoreMap":48},[53,35316,35317],{"class":55,"line":56},[53,35318,35312],{},[18,35320,35321],{},"This will prevent the Activity to switch orientations if the user rotates the device.",[18,35323,35324],{},"Then create a BaseActivity that all your Activities will be extending. In the onCreate we’ll do the other part of the\ntrick.",[43,35326,35328],{"className":288,"code":35327,"language":290,"meta":48,"style":48},"\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",[50,35329,35330,35334,35339,35343,35348,35353,35358,35363,35368,35372,35376],{"__ignoreMap":48},[53,35331,35332],{"class":55,"line":56},[53,35333,500],{"emptyLinePlaceholder":499},[53,35335,35336],{"class":55,"line":86},[53,35337,35338],{},"@Override\n",[53,35340,35341],{"class":55,"line":126},[53,35342,13038],{},[53,35344,35345],{"class":55,"line":163},[53,35346,35347],{}," if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE\n",[53,35349,35350],{"class":55,"line":186},[53,35351,35352],{}," && (this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) \u003C Configuration.SCREENLAYOUT_SIZE_LARGE) {\n",[53,35354,35355],{"class":55,"line":221},[53,35356,35357],{}," setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n",[53,35359,35360],{"class":55,"line":242},[53,35361,35362],{}," }else if((this.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE){\n",[53,35364,35365],{"class":55,"line":273},[53,35366,35367],{}," setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n",[53,35369,35370],{"class":55,"line":279},[53,35371,12712],{},[53,35373,35374],{"class":55,"line":496},[53,35375,13043],{},[53,35377,35378],{"class":55,"line":503},[53,35379,860],{},[18,35381,35382],{},"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.",[18,35384,35385],{},"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.",[18,35387,35388],{},"Also make sure to call the super.onCreate() in the child classes onCreate() first, otherwise it could load the wrong\nlayout!",[18,35390,35391],{},"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!",[607,35393,989],{},{"title":48,"searchDepth":86,"depth":86,"links":35395},[],[4516,997],"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":35297,"description":35306},"blog/android-size-depending-orientation-lock",[4526,35405,35406,35407,35408,35409,35410,35411],"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":35415,"title":35416,"author":35417,"body":35418,"category":36564,"date":36565,"description":35425,"extension":617,"link":36566,"meta":36567,"navigation":499,"path":36568,"seo":36569,"slug":35422,"stem":36570,"tags":36571,"teaser":36577,"__hash__":36578},"blog/blog/a-small-look-into-google-cloud-messages.md","A small look into Google Cloud Messages",[12861],{"type":11,"value":35419,"toc":36560},[35420,35423,35426,35434,35439,35443,35446,35449,35518,35521,35580,35583,35592,35595,35598,35601,35629,35632,35641,35644,35659,35662,35732,35735,35738,35835,35838,35841,35844,35848,35851,35858,35861,35992,35995,36433,36436,36531,36534,36537,36543,36546,36549,36557],[14,35421,35416],{"id":35422},"a-small-look-into-google-cloud-messages",[18,35424,35425],{},"Within the scope of some Android R&D I took a look at Google’s Cloud Message Service, GCM.",[18,35427,35428,35429,35433],{},"Well, the starter guide at",[585,35430,35431],{"href":35431,"rel":35432},"http://developer.android.com/google/gcm/gs.html",[589]," is almost all you need to get started, so\nI’ll explain my setup and some further instructions for a small test case.",[18,35435,35436],{},[573,35437,35438],{},"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.",[2352,35440,35442],{"id":35441},"the-android-application","The Android Application",[18,35444,35445],{},"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).",[18,35447,35448],{},"For GCM to work, we need to add some permissions to our AndroidManifest:",[43,35450,35452],{"className":1980,"code":35451,"language":1982,"meta":48,"style":48},"\n\u003Cpermission\n android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\n android: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",[50,35453,35454,35458,35463,35468,35473,35478,35483,35488,35493,35498,35503,35508,35513],{"__ignoreMap":48},[53,35455,35456],{"class":55,"line":56},[53,35457,500],{"emptyLinePlaceholder":499},[53,35459,35460],{"class":55,"line":86},[53,35461,35462],{},"\u003Cpermission\n",[53,35464,35465],{"class":55,"line":126},[53,35466,35467],{}," android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\n",[53,35469,35470],{"class":55,"line":163},[53,35471,35472],{}," android:protectionLevel=\"signature\"/>\n",[53,35474,35475],{"class":55,"line":186},[53,35476,35477],{},"\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"/>\n",[53,35479,35480],{"class":55,"line":221},[53,35481,35482],{}," \u003C!-- App receives GCM messages. -->\n",[53,35484,35485],{"class":55,"line":242},[53,35486,35487],{},"\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\"/>\n",[53,35489,35490],{"class":55,"line":273},[53,35491,35492],{}," \u003C!-- GCM connects to Google Services. -->\n",[53,35494,35495],{"class":55,"line":279},[53,35496,35497],{},"\u003Cuses-permission android:name=\"android.permission.INTERNET\"/>\n",[53,35499,35500],{"class":55,"line":496},[53,35501,35502],{}," \u003C!-- GCM requires a Google account. -->\n",[53,35504,35505],{"class":55,"line":503},[53,35506,35507],{},"\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>\n",[53,35509,35510],{"class":55,"line":509},[53,35511,35512],{}," \u003C!-- Keeps the processor from sleeping when a message is received. -->\n",[53,35514,35515],{"class":55,"line":515},[53,35516,35517],{},"\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n",[18,35519,35520],{},"And declare a GCM broadcast receiver within the application tag:",[43,35522,35524],{"className":1980,"code":35523,"language":1982,"meta":48,"style":48},"\n\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",[50,35525,35526,35530,35535,35540,35545,35550,35555,35560,35565,35570,35575],{"__ignoreMap":48},[53,35527,35528],{"class":55,"line":56},[53,35529,500],{"emptyLinePlaceholder":499},[53,35531,35532],{"class":55,"line":86},[53,35533,35534],{},"\u003Creceiver\n",[53,35536,35537],{"class":55,"line":126},[53,35538,35539],{}," android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n",[53,35541,35542],{"class":55,"line":163},[53,35543,35544],{}," android:permission=\"com.google.android.c2dm.permission.SEND\">\n",[53,35546,35547],{"class":55,"line":186},[53,35548,35549],{}," \u003Cintent-filter>\n",[53,35551,35552],{"class":55,"line":221},[53,35553,35554],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\"/>\n",[53,35556,35557],{"class":55,"line":242},[53,35558,35559],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\"/>\n",[53,35561,35562],{"class":55,"line":273},[53,35563,35564],{}," \u003Ccategory android:name=\"com.synyx.cloudmessagetest\"/>\n",[53,35566,35567],{"class":55,"line":279},[53,35568,35569],{}," \u003C/intent-filter>\n",[53,35571,35572],{"class":55,"line":496},[53,35573,35574],{},"\u003C/receiver>\n",[53,35576,35577],{"class":55,"line":503},[53,35578,35579],{},"\u003Cservice android:name=\".GCMIntentService\"/>\n",[18,35581,35582],{},"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:",[43,35584,35586],{"className":288,"code":35585,"language":290,"meta":48,"style":48},"public class GCMIntentService extends GCMBaseIntentService {\n",[50,35587,35588],{"__ignoreMap":48},[53,35589,35590],{"class":55,"line":56},[53,35591,35585],{},[18,35593,35594],{},"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.",[18,35596,35597],{},"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.",[18,35599,35600],{},"First in the onMessage() method, we need to get access to the Main Thread of our App.",[43,35602,35604],{"className":288,"code":35603,"language":290,"meta":48,"style":48},"Handler h = new Handler(Looper.getMainLooper());\nh.post(new Runnable() {\n public void run() {\n }\n}\n",[50,35605,35606,35611,35616,35621,35625],{"__ignoreMap":48},[53,35607,35608],{"class":55,"line":56},[53,35609,35610],{},"Handler h = new Handler(Looper.getMainLooper());\n",[53,35612,35613],{"class":55,"line":86},[53,35614,35615],{},"h.post(new Runnable() {\n",[53,35617,35618],{"class":55,"line":126},[53,35619,35620],{}," public void run() {\n",[53,35622,35623],{"class":55,"line":163},[53,35624,860],{},[53,35626,35627],{"class":55,"line":186},[53,35628,282],{},[18,35630,35631],{},"In the run() method, we get us an Intent from our MainActivity",[43,35633,35635],{"className":288,"code":35634,"language":290,"meta":48,"style":48},"Intent notificationIntent = new Intent(context, CloudMessageTestActivity.class);\n",[50,35636,35637],{"__ignoreMap":48},[53,35638,35639],{"class":55,"line":56},[53,35640,35634],{},[18,35642,35643],{},"And then wrap it with a PendingIntent for the NotificationBuilder",[43,35645,35647],{"className":288,"code":35646,"language":290,"meta":48,"style":48},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n notificationIntent, 0);\n",[50,35648,35649,35654],{"__ignoreMap":48},[53,35650,35651],{"class":55,"line":56},[53,35652,35653],{},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n",[53,35655,35656],{"class":55,"line":86},[53,35657,35658],{}," notificationIntent, 0);\n",[18,35660,35661],{},"Finally, use the NotificationBuilder to create and send the notification",[43,35663,35665],{"className":288,"code":35664,"language":290,"meta":48,"style":48},"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",[50,35666,35667,35672,35677,35682,35687,35692,35697,35702,35707,35712,35717,35722,35727],{"__ignoreMap":48},[53,35668,35669],{"class":55,"line":56},[53,35670,35671],{},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n",[53,35673,35674],{"class":55,"line":86},[53,35675,35676],{}," context);\n",[53,35678,35679],{"class":55,"line":126},[53,35680,35681],{},"builder.setContentIntent(pendingIntent);\n",[53,35683,35684],{"class":55,"line":163},[53,35685,35686],{},"builder.setAutoCancel(true);\n",[53,35688,35689],{"class":55,"line":186},[53,35690,35691],{},"builder.setSmallIcon(R.drawable.ic_launcher);\n",[53,35693,35694],{"class":55,"line":221},[53,35695,35696],{},"//this is added on the server side\n",[53,35698,35699],{"class":55,"line":242},[53,35700,35701],{},"String text = intent.getStringExtra(\"text\");\n",[53,35703,35704],{"class":55,"line":273},[53,35705,35706],{},"builder.setContentText(text);\n",[53,35708,35709],{"class":55,"line":279},[53,35710,35711],{},"builder.setContentTitle(\"New message from the cloud!\");\n",[53,35713,35714],{"class":55,"line":496},[53,35715,35716],{},"Notification noti = builder.build();\n",[53,35718,35719],{"class":55,"line":503},[53,35720,35721],{},"NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n",[53,35723,35724],{"class":55,"line":509},[53,35725,35726],{},"//just set the mId to 1, because we don't care about it in this case\n",[53,35728,35729],{"class":55,"line":515},[53,35730,35731],{},"mNotificationManager.notify(1, noti);\n",[18,35733,35734],{},"That’ it with the GCMIntentService.",[18,35736,35737],{},"Now we let the app register itself with GCM in our MainActivity, which is fairly easy:",[43,35739,35741],{"className":288,"code":35740,"language":290,"meta":48,"style":48},"//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",[50,35742,35743,35748,35753,35757,35762,35767,35772,35777,35782,35787,35792,35797,35802,35807,35812,35817,35822,35826,35831],{"__ignoreMap":48},[53,35744,35745],{"class":55,"line":56},[53,35746,35747],{},"//set your senderId from the API here!\n",[53,35749,35750],{"class":55,"line":86},[53,35751,35752],{}," private static final String SENDER_ID = \"1234567890\";\n",[53,35754,35755],{"class":55,"line":126},[53,35756,35338],{},[53,35758,35759],{"class":55,"line":163},[53,35760,35761],{},"protected void onCreate(Bundle savedInstanceState) {\n",[53,35763,35764],{"class":55,"line":186},[53,35765,35766],{}," GCMRegistrar.checkDevice(this);\n",[53,35768,35769],{"class":55,"line":221},[53,35770,35771],{}," GCMRegistrar.checkManifest(this);\n",[53,35773,35774],{"class":55,"line":242},[53,35775,35776],{}," final String regId = GCMRegistrar.getRegistrationId(this);\n",[53,35778,35779],{"class":55,"line":273},[53,35780,35781],{}," // if we don't have a regId yet, register at gcm\n",[53,35783,35784],{"class":55,"line":279},[53,35785,35786],{}," if (regId.equals(\"\")) {\n",[53,35788,35789],{"class":55,"line":496},[53,35790,35791],{}," GCMRegistrar.register(this, SENDER_ID);\n",[53,35793,35794],{"class":55,"line":503},[53,35795,35796],{}," Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n",[53,35798,35799],{"class":55,"line":509},[53,35800,35801],{}," // just log the registrationId for this test case.\n",[53,35803,35804],{"class":55,"line":515},[53,35805,35806],{}," Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n",[53,35808,35809],{"class":55,"line":521},[53,35810,35811],{}," } else {\n",[53,35813,35814],{"class":55,"line":527},[53,35815,35816],{}," Log.i(this.getClass().getName(), \"Already registered\");\n",[53,35818,35819],{"class":55,"line":533},[53,35820,35821],{}," Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n",[53,35823,35824],{"class":55,"line":539},[53,35825,35806],{},[53,35827,35828],{"class":55,"line":545},[53,35829,35830],{}," }\n",[53,35832,35833],{"class":55,"line":2070},[53,35834,282],{},[18,35836,35837],{},"Don’t forget to replace the SENDER_ID with yours!",[18,35839,35840],{},"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.",[18,35842,35843],{},"With this, we finished our small app and can begin implementing the server part.",[2352,35845,35847],{"id":35846},"the-web-application","The Web Application",[18,35849,35850],{},"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.",[18,35852,35853,35854,8526],{},"Instead of just copying the GCM server library into the project, I searched a bit and found someone, who created a\nrepository for\nit. (",[585,35855,35856],{"href":35856,"rel":35857},"https://github.com/slorber/gcm-server-repository",[589],[18,35859,35860],{},"We start with creating the Sender class, which isn’t that hard either.",[43,35862,35864],{"className":288,"code":35863,"language":290,"meta":48,"style":48},"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",[50,35865,35866,35871,35876,35881,35886,35891,35896,35901,35906,35911,35916,35921,35926,35931,35936,35941,35946,35951,35956,35961,35966,35971,35975,35979,35984,35988],{"__ignoreMap":48},[53,35867,35868],{"class":55,"line":56},[53,35869,35870],{},"public class GCMSender {\n",[53,35872,35873],{"class":55,"line":86},[53,35874,35875],{}," public String apiKey = null;\n",[53,35877,35878],{"class":55,"line":126},[53,35879,35880],{}," public GCMSender(String apiKey) {\n",[53,35882,35883],{"class":55,"line":163},[53,35884,35885],{}," this.apiKey = apiKey;\n",[53,35887,35888],{"class":55,"line":186},[53,35889,35890],{}," }\n",[53,35892,35893],{"class":55,"line":221},[53,35894,35895],{}," public String send(String text, String id) throws IOException {\n",[53,35897,35898],{"class":55,"line":242},[53,35899,35900],{}," Sender sender = new Sender(apiKey);\n",[53,35902,35903],{"class":55,"line":273},[53,35904,35905],{}," Builder builder = new Message.Builder();\n",[53,35907,35908],{"class":55,"line":279},[53,35909,35910],{}," builder.addData(\"text\", text);\n",[53,35912,35913],{"class":55,"line":496},[53,35914,35915],{}," Result result = sender.send(builder.build(), id, 5);\n",[53,35917,35918],{"class":55,"line":503},[53,35919,35920],{}," if (result.getMessageId() != null) {\n",[53,35922,35923],{"class":55,"line":509},[53,35924,35925],{}," String canonicalRegId = result.getCanonicalRegistrationId();\n",[53,35927,35928],{"class":55,"line":515},[53,35929,35930],{}," if (canonicalRegId != null) {\n",[53,35932,35933],{"class":55,"line":521},[53,35934,35935],{}," // same device has more than on registration ID: update database\n",[53,35937,35938],{"class":55,"line":527},[53,35939,35940],{}," return \"same device has more than on registration ID: update database\";\n",[53,35942,35943],{"class":55,"line":533},[53,35944,35945],{}," }\n",[53,35947,35948],{"class":55,"line":539},[53,35949,35950],{}," } else {\n",[53,35952,35953],{"class":55,"line":545},[53,35954,35955],{}," String error = result.getErrorCodeName();\n",[53,35957,35958],{"class":55,"line":2070},[53,35959,35960],{}," if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n",[53,35962,35963],{"class":55,"line":2075},[53,35964,35965],{}," // application has been removed from device - unregister database\n",[53,35967,35968],{"class":55,"line":2081},[53,35969,35970],{}," return \"application has been removed from device - unregister database\";\n",[53,35972,35973],{"class":55,"line":2087},[53,35974,35945],{},[53,35976,35977],{"class":55,"line":2092},[53,35978,35830],{},[53,35980,35981],{"class":55,"line":2097},[53,35982,35983],{}," return null;\n",[53,35985,35986],{"class":55,"line":2103},[53,35987,35890],{},[53,35989,35990],{"class":55,"line":2109},[53,35991,282],{},[18,35993,35994],{},"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:",[43,35996,35998],{"className":6829,"code":35997,"language":6831,"meta":48,"style":48},"\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",[50,35999,36000,36012,36017,36030,36038,36047,36072,36079,36089,36099,36109,36119,36124,36138,36146,36155,36167,36184,36206,36215,36230,36245,36250,36264,36273,36281,36296,36315,36347,36365,36395,36409,36417,36425],{"__ignoreMap":48},[53,36001,36002,36004,36007,36009],{"class":55,"line":56},[53,36003,6838],{"class":102},[53,36005,36006],{"class":82},"%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> ",[53,36008,6838],{"class":102},[53,36010,36011],{"class":82},"%@page\n",[53,36013,36014],{"class":55,"line":86},[53,36015,36016],{"class":82},"contentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n",[53,36018,36019,36022,36025,36028],{"class":55,"line":126},[53,36020,36021],{"class":82},"\u003C!",[53,36023,36024],{"class":6841},"DOCTYPE",[53,36026,36027],{"class":59}," html",[53,36029,6860],{"class":82},[53,36031,36032,36034,36036],{"class":55,"line":163},[53,36033,6838],{"class":82},[53,36035,6831],{"class":6841},[53,36037,6860],{"class":82},[53,36039,36040,36042,36045],{"class":55,"line":186},[53,36041,6865],{"class":82},[53,36043,36044],{"class":6841},"head",[53,36046,6860],{"class":82},[53,36048,36049,36052,36055,36058,36060,36063,36065,36067,36070],{"class":55,"line":221},[53,36050,36051],{"class":82}," \u003C",[53,36053,36054],{"class":6841},"meta",[53,36056,36057],{"class":59}," http-equiv",[53,36059,390],{"class":82},[53,36061,36062],{"class":63},"\"Content-Type\"",[53,36064,1267],{"class":59},[53,36066,390],{"class":82},[53,36068,36069],{"class":63},"\"text/html; charset=UTF-8\"",[53,36071,6887],{"class":82},[53,36073,36074,36076],{"class":55,"line":242},[53,36075,36051],{"class":82},[53,36077,36078],{"class":6841},"link\n",[53,36080,36081,36084,36086],{"class":55,"line":273},[53,36082,36083],{"class":59}," rel",[53,36085,390],{"class":82},[53,36087,36088],{"class":63},"\"stylesheet\"\n",[53,36090,36091,36094,36096],{"class":55,"line":279},[53,36092,36093],{"class":59}," type",[53,36095,390],{"class":82},[53,36097,36098],{"class":63},"\"text/css\"\n",[53,36100,36101,36104,36106],{"class":55,"line":496},[53,36102,36103],{"class":59}," href",[53,36105,390],{"class":82},[53,36107,36108],{"class":63},"\"/GCMTestServer/frontend_resources/style.css\"\n",[53,36110,36111,36114,36116],{"class":55,"line":503},[53,36112,36113],{"class":59}," media",[53,36115,390],{"class":82},[53,36117,36118],{"class":63},"\"screen\"\n",[53,36120,36121],{"class":55,"line":509},[53,36122,36123],{"class":82}," />\n",[53,36125,36126,36128,36131,36134,36136],{"class":55,"line":515},[53,36127,36051],{"class":82},[53,36129,36130],{"class":6841},"title",[53,36132,36133],{"class":82},">GCM Sender\u003C/",[53,36135,36130],{"class":6841},[53,36137,6860],{"class":82},[53,36139,36140,36142,36144],{"class":55,"line":521},[53,36141,6949],{"class":82},[53,36143,36044],{"class":6841},[53,36145,6860],{"class":82},[53,36147,36148,36150,36153],{"class":55,"line":527},[53,36149,6865],{"class":82},[53,36151,36152],{"class":6841},"body",[53,36154,6860],{"class":82},[53,36156,36157,36159,36161,36163,36165],{"class":55,"line":533},[53,36158,36051],{"class":82},[53,36160,14],{"class":6841},[53,36162,36133],{"class":82},[53,36164,14],{"class":6841},[53,36166,6860],{"class":82},[53,36168,36169,36171,36174,36177,36179,36182],{"class":55,"line":539},[53,36170,36051],{"class":82},[53,36172,36173],{"class":102},"c:if",[53,36175,36176],{"class":59}," test",[53,36178,390],{"class":82},[53,36180,36181],{"class":63},"\"${success == true}\"",[53,36183,6860],{"class":82},[53,36185,36186,36189,36191,36194,36196,36199,36202,36204],{"class":55,"line":545},[53,36187,36188],{"class":82}," \u003C",[53,36190,6817],{"class":6841},[53,36192,36193],{"class":59}," class",[53,36195,390],{"class":82},[53,36197,36198],{"class":63},"\"success\"",[53,36200,36201],{"class":82},">sending successful!\u003C/",[53,36203,6817],{"class":6841},[53,36205,6860],{"class":82},[53,36207,36208,36211,36213],{"class":55,"line":2070},[53,36209,36210],{"class":82}," \u003C/",[53,36212,36173],{"class":102},[53,36214,6860],{"class":82},[53,36216,36217,36219,36221,36223,36225,36228],{"class":55,"line":2075},[53,36218,36051],{"class":82},[53,36220,36173],{"class":102},[53,36222,36176],{"class":59},[53,36224,390],{"class":82},[53,36226,36227],{"class":63},"\"${error == true}\"",[53,36229,6860],{"class":82},[53,36231,36232,36234,36236,36238,36240,36243],{"class":55,"line":2081},[53,36233,36188],{"class":82},[53,36235,6817],{"class":6841},[53,36237,36193],{"class":59},[53,36239,390],{"class":82},[53,36241,36242],{"class":63},"\"error\"",[53,36244,6860],{"class":82},[53,36246,36247],{"class":55,"line":2087},[53,36248,36249],{"class":82}," Error at sending!\n",[53,36251,36252,36255,36257,36260,36262],{"class":55,"line":2092},[53,36253,36254],{"class":82}," \u003C",[53,36256,18],{"class":6841},[53,36258,36259],{"class":82},">${errormessage}\u003C/",[53,36261,18],{"class":6841},[53,36263,6860],{"class":82},[53,36265,36266,36269,36271],{"class":55,"line":2097},[53,36267,36268],{"class":82}," \u003C/",[53,36270,6817],{"class":6841},[53,36272,6860],{"class":82},[53,36274,36275,36277,36279],{"class":55,"line":2103},[53,36276,36210],{"class":82},[53,36278,36173],{"class":102},[53,36280,6860],{"class":82},[53,36282,36283,36285,36287,36289,36291,36294],{"class":55,"line":2109},[53,36284,36051],{"class":82},[53,36286,6744],{"class":6841},[53,36288,6852],{"class":59},[53,36290,390],{"class":82},[53,36292,36293],{"class":63},"\"POST\"",[53,36295,6860],{"class":82},[53,36297,36298,36300,36303,36305,36307,36309,36312],{"class":55,"line":2115},[53,36299,36188],{"class":82},[53,36301,36302],{"class":6841},"label",[53,36304,144],{"class":59},[53,36306,390],{"class":82},[53,36308,6876],{"class":63},[53,36310,36311],{"class":82},">Text\u003C/",[53,36313,36314],{"class":6841},"label\n",[53,36316,36317,36320,36322,36324,36326,36328,36330,36332,36334,36336,36338,36340,36343,36345],{"class":55,"line":2120},[53,36318,36319],{"class":82}," >\u003C",[53,36321,6868],{"class":6841},[53,36323,6879],{"class":59},[53,36325,390],{"class":82},[53,36327,6876],{"class":63},[53,36329,6975],{"class":59},[53,36331,390],{"class":82},[53,36333,6876],{"class":63},[53,36335,6871],{"class":59},[53,36337,390],{"class":82},[53,36339,6876],{"class":63},[53,36341,36342],{"class":82}," />\u003C",[53,36344,5298],{"class":6841},[53,36346,6887],{"class":82},[53,36348,36349,36351,36353,36355,36357,36360,36363],{"class":55,"line":2946},[53,36350,36188],{"class":82},[53,36352,36302],{"class":6841},[53,36354,144],{"class":59},[53,36356,390],{"class":82},[53,36358,36359],{"class":63},"\"id\"",[53,36361,36362],{"class":82},">Registration-Id\u003C/",[53,36364,36314],{"class":6841},[53,36366,36367,36369,36371,36373,36375,36377,36379,36381,36383,36385,36387,36389,36391,36393],{"class":55,"line":2952},[53,36368,36319],{"class":82},[53,36370,6868],{"class":6841},[53,36372,6879],{"class":59},[53,36374,390],{"class":82},[53,36376,36359],{"class":63},[53,36378,6975],{"class":59},[53,36380,390],{"class":82},[53,36382,36359],{"class":63},[53,36384,6871],{"class":59},[53,36386,390],{"class":82},[53,36388,6876],{"class":63},[53,36390,36342],{"class":82},[53,36392,5298],{"class":6841},[53,36394,6887],{"class":82},[53,36396,36397,36399,36401,36403,36405,36407],{"class":55,"line":2964},[53,36398,36188],{"class":82},[53,36400,6868],{"class":6841},[53,36402,6871],{"class":59},[53,36404,390],{"class":82},[53,36406,6921],{"class":63},[53,36408,6887],{"class":82},[53,36410,36411,36413,36415],{"class":55,"line":2973},[53,36412,36210],{"class":82},[53,36414,6744],{"class":6841},[53,36416,6860],{"class":82},[53,36418,36419,36421,36423],{"class":55,"line":2979},[53,36420,6949],{"class":82},[53,36422,36152],{"class":6841},[53,36424,6860],{"class":82},[53,36426,36427,36429,36431],{"class":55,"line":2990},[53,36428,6958],{"class":82},[53,36430,6831],{"class":6841},[53,36432,6860],{"class":82},[18,36434,36435],{},"In the corresponding Controller to process the request, send the message:",[43,36437,36439],{"className":288,"code":36438,"language":290,"meta":48,"style":48}," @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",[50,36440,36441,36446,36451,36456,36461,36466,36471,36476,36481,36486,36491,36495,36500,36505,36509,36513,36518,36522,36527],{"__ignoreMap":48},[53,36442,36443],{"class":55,"line":56},[53,36444,36445],{}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n",[53,36447,36448],{"class":55,"line":86},[53,36449,36450],{}," public String send(Model model,\n",[53,36452,36453],{"class":55,"line":126},[53,36454,36455],{}," @RequestParam(\"text\") String text,\n",[53,36457,36458],{"class":55,"line":163},[53,36459,36460],{}," @RequestParam(\"id\") String id) {\n",[53,36462,36463],{"class":55,"line":186},[53,36464,36465],{}," String error = null;\n",[53,36467,36468],{"class":55,"line":221},[53,36469,36470],{}," try {\n",[53,36472,36473],{"class":55,"line":242},[53,36474,36475],{}," error = gcmSender.send(text, id);\n",[53,36477,36478],{"class":55,"line":273},[53,36479,36480],{}," } catch (IOException ex) {\n",[53,36482,36483],{"class":55,"line":279},[53,36484,36485],{}," model.addAttribute(\"error\", true);\n",[53,36487,36488],{"class":55,"line":496},[53,36489,36490],{}," model.addAttribute(\"errormessage\", ex.getMessage());\n",[53,36492,36493],{"class":55,"line":503},[53,36494,12712],{},[53,36496,36497],{"class":55,"line":509},[53,36498,36499],{}," if (error == null) {\n",[53,36501,36502],{"class":55,"line":515},[53,36503,36504],{}," model.addAttribute(\"success\", true);\n",[53,36506,36507],{"class":55,"line":521},[53,36508,15185],{},[53,36510,36511],{"class":55,"line":527},[53,36512,36485],{},[53,36514,36515],{"class":55,"line":533},[53,36516,36517],{}," model.addAttribute(\"errormessage\", error);\n",[53,36519,36520],{"class":55,"line":539},[53,36521,12712],{},[53,36523,36524],{"class":55,"line":545},[53,36525,36526],{}," return \"sender\";\n",[53,36528,36529],{"class":55,"line":2070},[53,36530,860],{},[18,36532,36533],{},"Now we are ready to test it!",[18,36535,36536],{},"Start the app, copy the RegistrationId from the Logs, start the Server, enter the RegistrationId and a small text, and\nthere you go:",[18,36538,36539],{},[1773,36540],{"alt":36541,"src":36542},"\"notification\"","https://media.synyx.de/uploads//2012/12/notification-300x221.jpg",[18,36544,36545],{},"To get a better understanding, you can also read the rest of the starting guide and the other documentation.",[18,36547,36548],{},"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 😛",[18,36550,36551,36552],{},"As promised, here are the full sources:",[585,36553,36556],{"href":36554,"rel":36555},"https://media.synyx.de/uploads//2012/12/CloudMessageTest.zip",[589],"CloudMessageTest",[607,36558,36559],{},"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":48,"searchDepth":86,"depth":86,"links":36561},[36562,36563],{"id":35441,"depth":86,"text":35442},{"id":35846,"depth":86,"text":35847},[4516,997],"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":35416,"description":35425},"blog/a-small-look-into-google-cloud-messages",[4526,34777,36572,36573,36574,36575,36576],"gcm","google-cloud","messages","messaging","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,…","Zo3lVe4ZoQawpHyu6_t-mMTQUwODaiJpqsdkWTxts5M",{"id":36580,"title":36581,"author":36582,"body":36584,"category":36831,"date":36832,"description":36591,"extension":617,"link":36833,"meta":36834,"navigation":499,"path":36835,"seo":36836,"slug":36588,"stem":36837,"tags":36838,"teaser":36840,"__hash__":36841},"blog/blog/synyx-weihnachtsgedicht.md","synyx Weihnachtsgedicht",[36583],"mohr",{"type":11,"value":36585,"toc":36829},[36586,36589,36592,36595,36598,36601,36604,36607,36610,36613,36616,36619,36622,36625,36628,36631,36634,36637,36640,36643,36646,36649,36652,36655,36658,36661,36664,36667,36670,36673,36676,36679,36682,36685,36688,36691,36694,36697,36700,36703,36706,36709,36712,36715,36718,36721,36724,36727,36730,36733,36736,36739,36742,36745,36748,36751,36754,36757,36760,36763,36766,36769,36772,36775,36778,36781,36784,36787,36790,36793,36796,36799,36802,36805,36808,36811,36814,36817,36820,36823,36826],[14,36587,36581],{"id":36588},"synyx-weihnachtsgedicht",[18,36590,36591],{},"Vom weiten Nordpol komm ich her,",[18,36593,36594],{},"Ich muss euch sagen es buggte sehr,",[18,36596,36597],{},"Mein Navi versagte und auch mein Bord-PC,",[18,36599,36600],{},"Mein Schlitten stürzte in einen Haufen Schnee.",[18,36602,36603],{},"Gar hart ich auf meinen Allerwertesten knallte,",[18,36605,36606],{},"Der Knall noch in meinen Ohren schallte.",[18,36608,36609],{},"Zum Glück tat ich mir nichts, war nur verwirrt,",[18,36611,36612],{},"Wohin hatte ich mich bloß verirrt?",[18,36614,36615],{},"Vor Kälte schon ganz rote Ohren,",[18,36617,36618],{},"Die Nase fast schon abgefroren,",[18,36620,36621],{},"Sah ich ein Schild mit “Open Source”,",[18,36623,36624],{},"Das Schicksal führte mich auf den richtigen Kurs.",[18,36626,36627],{},"Die Dame mit den blonden Locken",[18,36629,36630],{},"Sah erstaunt drein – fast erschrocken.",[18,36632,36633],{},"Schließlich passiert’s nicht allzu oft,",[18,36635,36636],{},"Dass der Weihnachtsmann an der Türe klopft.",[18,36638,36639],{},"“Gnädiges Fräulein, ich hab ein Problem",[18,36641,36642],{},"Alles kaputt, das ganze System.",[18,36644,36645],{},"Viele Geschenke, die ich ausliefern muss:",[18,36647,36648],{},"Süßes, Spielzeug, viel Schönes im Überschuss.",[18,36650,36651],{},"Könntet ihr mir vielleicht helfen?",[18,36653,36654],{},"Ihr seid doch sowas wie Software-Elfen.",[18,36656,36657],{},"Habt sicher auch Ersatz-Hardware,",[18,36659,36660],{},"Die Reparatur ist bestimmt nicht schwer.”",[18,36662,36663],{},"“Sind Sie etwa”, sprach sie, “der Weihnachtsmann?",[18,36665,36666],{},"Ich kann’s nicht glauben, manno mann!",[18,36668,36669],{},"So kommen Sie doch erstmal rein,",[18,36671,36672],{},"Da draußen rumsteh’n muss nicht sein.",[18,36674,36675],{},"Und wärmen Sie sich bei uns auf.",[18,36677,36678],{},"Kaffee, Tee – Sie haben Lust worauf?",[18,36680,36681],{},"Folgen Sie mir einfach in die Küche.",[18,36683,36684],{},"Das hier sind übrigens uns’re Fische.”",[18,36686,36687],{},"Und in der Küche, oh welch ein Duft!",[18,36689,36690],{},"Ein Wohlgeruch hing in der Luft.",[18,36692,36693],{},"“Es ist”, sprach sie, “grad Mittagszeit,",[18,36695,36696],{},"Das heißt Kochmuddis Festmahl steht bereit.",[18,36698,36699],{},"Setzen Sie sich erstmal hin,",[18,36701,36702],{},"Ich informiere unser Team.",[18,36704,36705],{},"Lassen Sie es sich gut schmecken,",[18,36707,36708],{},"Das Essen ist zum Finger lecken.",[18,36710,36711],{},"Dr. Kill und die anderen Doktoren",[18,36713,36714],{},"Der Code Clinic haben für Sie offene Ohren.",[18,36716,36717],{},"Vielleicht lässt sich am Navi noch was machen",[18,36719,36720],{},"Und wir finden des Problems Ursachen.”",[18,36722,36723],{},"Gerade hatte ich aufgegessen,",[18,36725,36726],{},"Da kamen – man hatte mich nicht vergessen –",[18,36728,36729],{},"Schon Dr. Kill und die anderen Doktoren.",[18,36731,36732],{},"Und Sie ließen mich erfahren:",[18,36734,36735],{},"“Das ist ein wirklich schwerer Fall:",[18,36737,36738],{},"Unsaub’rer Code, Sonar Violations überall!",[18,36740,36741],{},"Von der mangelnden Testabdeckung will ich gar nicht sprechen,",[18,36743,36744],{},"Die ist geradezu ein Verbrechen.",[18,36746,36747],{},"Wenn Sie woll’n, tun wir das Beste.",[18,36749,36750],{},"Jedoch wär’s für Sie das Schönste,",[18,36752,36753],{},"Das Indy-Team mal anzuhauen,",[18,36755,36756],{},"Auf ihre Maßanfertigungen kann man bauen.",[18,36758,36759],{},"Vielleicht wär auch ein Smartphone nicht verkehrt.",[18,36761,36762],{},"Mit einer Navi-App die sich bewährt.",[18,36764,36765],{},"Auch Mobile Solutions zähl’n zu uns’rem Bereich.",[18,36767,36768],{},"Wenn Sie woll’n mach’n wir uns an die Arbeit gleich.",[18,36770,36771],{},"Damit alle Kinder ihre Geschenke bekommen,",[18,36773,36774],{},"Haben wir uns auch Ihrem alten Navi angenommen.",[18,36776,36777],{},"Und mit ein paar Tricks im Nullkommanix",[18,36779,36780],{},"Nun läuft es wieder dank uns’rem Quickfix.”",[18,36782,36783],{},"Und wie ich so in der Küche stand,",[18,36785,36786],{},"Näherte sich ein bärtiger Jemand.",[18,36788,36789],{},"“Hopp, hopp”, rief es, “alter Gesell,",[18,36791,36792],{},"Hebe die Beine und spute dich schnell.",[18,36794,36795],{},"Repariert ist nun dein Bord-PC,",[18,36797,36798],{},"Auch eingebaut ‘ne SSD.",[18,36800,36801],{},"Damit’s mit den Geschenken läuft,",[18,36803,36804],{},"Selbst wenn man mal ein Bierchen säuft.”",[18,36806,36807],{},"Mein Bord-PC war wieder fit,",[18,36809,36810],{},"Das Navi lotzste mich Schritt für Schritt.",[18,36812,36813],{},"So konnt’ ich fortsetzen meine Reise,",[18,36815,36816],{},"Alle Geschenke ausliefern glücklicherweise.",[18,36818,36819],{},"So rettete synyx das Weihnachtsfest,",[18,36821,36822],{},"Zumindest wenn man Phantasie walten lässt.",[18,36824,36825],{},"Wir wünschen ein schönes Weihnachtsfest und ganz klar:",[18,36827,36828],{},"Habt auch einen guten Rutsch ins neue Jahr!",{"title":48,"searchDepth":86,"depth":86,"links":36830},[],[614],"2012-12-21T13:26:51","https://synyx.de/blog/synyx-weihnachtsgedicht/",{},"/blog/synyx-weihnachtsgedicht",{"title":36581,"description":36591},"blog/synyx-weihnachtsgedicht",[22519,5743,36839],"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":36843,"title":36844,"author":36845,"body":36846,"category":36890,"date":36891,"description":36892,"extension":617,"link":36893,"meta":36894,"navigation":499,"path":36895,"seo":36896,"slug":36850,"stem":36897,"tags":36898,"teaser":36901,"__hash__":36902},"blog/blog/raclette-essen-in-neuen-dimensionen.md","Raclette-Essen in neuen Dimensionen",[11619],{"type":11,"value":36847,"toc":36888},[36848,36851,36854,36857,36860,36870,36873,36879,36882,36885],[14,36849,36844],{"id":36850},"raclette-essen-in-neuen-dimensionen",[18,36852,36853],{},"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?",[18,36855,36856],{},"Also…..",[18,36858,36859],{},"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.",[18,36861,36862,36866],{},[1773,36863],{"alt":36864,"src":36865},"\"helfer\"","https://media.synyx.de/uploads//2012/12/helfer.jpg",[1773,36867],{"alt":36868,"src":36869},"\"zutaten\"","https://media.synyx.de/uploads//2012/12/zutaten.jpg",[18,36871,36872],{},"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.",[18,36874,36875],{},[1773,36876],{"alt":36877,"src":36878},"\"raclette_viele\"","https://media.synyx.de/uploads//2012/12/raclette_viele.jpg",[18,36880,36881],{},"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.",[18,36883,36884],{},"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.",[18,36886,36887],{},"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":48,"searchDepth":86,"depth":86,"links":36889},[],[614],"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":36844,"description":36853},"blog/raclette-essen-in-neuen-dimensionen",[36899,36900,5743],"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…","nFE8Mdau59AdCgX0PILxCgfwEUF-OxIlWSdIqNvtUxY",{"id":36904,"title":36905,"author":36906,"body":36907,"category":36984,"date":36985,"description":36986,"extension":617,"link":36987,"meta":36988,"navigation":499,"path":36989,"seo":36990,"slug":36911,"stem":36992,"tags":36993,"teaser":36997,"__hash__":36998},"blog/blog/professional-scrum-master-schulung.md","Professional Scrum Master Schulung",[13202],{"type":11,"value":36908,"toc":36981},[36909,36912,36921,36928,36931,36937,36940,36964,36967,36973,36976,36978],[14,36910,36905],{"id":36911},"professional-scrum-master-schulung",[18,36913,36914,36915,36920],{},"„Scrum ist ein Framework zur nachhaltigen Entwicklung komplexer Produkte“ (\nQuelle: ",[585,36916,36919],{"href":36917,"rel":36918},"http://www.scrum.org/Scrum-Guides",[589],"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.",[18,36922,36923,36924,36927],{},"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 ",[585,36925,19974],{"href":19972,"rel":36926},[589]," 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.",[18,36929,36930],{},"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.",[18,36932,36933],{},[1773,36934],{"alt":36935,"src":36936},"\"Scrum Prinzipien\"","https://media.synyx.de/uploads//2012/11/20121109_1731021.jpg",[18,36938,36939],{},"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:",[577,36941,36942,36949,36952,36955,36958,36961],{},[580,36943,36944,36945,36948],{},"Scrum Regeln (basierend auf dem ",[585,36946,36919],{"href":36917,"rel":36947},[589]," von Jeff Sutherland und Ken Schwaber)",[580,36950,36951],{},"Scrum Planung",[580,36953,36954],{},"Teams (Formierung, Motivation, Konfliktbewältigung)",[580,36956,36957],{},"‘Defintion of Done’",[580,36959,36960],{},"Skalierung von Scrum (Scrum of Scrum)",[580,36962,36963],{},"Rolle des Scrum Masters",[18,36965,36966],{},"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.",[18,36968,36969],{},[1773,36970],{"alt":36971,"src":36972},"\"PSM Schulung\"","https://media.synyx.de/uploads//2012/11/20121109_111413.jpg",[18,36974,36975],{},"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.",[2352,36977,969],{"id":968},[18,36979,36980],{},"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":48,"searchDepth":86,"depth":86,"links":36982},[36983],{"id":968,"depth":86,"text":969},[614],"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":36905,"description":36991},"„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",[36994,4230,36995,4232,5743,36996],"andrena","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…","ezctbwERVX6okGtAvaaHxvY_2DiGJ34ppeAiaL91SL4",{"id":37000,"title":37001,"author":37002,"body":37003,"category":37110,"date":37111,"description":37112,"extension":617,"link":37113,"meta":37114,"navigation":499,"path":37115,"seo":37116,"slug":37007,"stem":37117,"tags":37118,"teaser":37124,"__hash__":37125},"blog/blog/synyx-besucht-die-mdc-2012-in-stuttgart.md","synyx besucht die MDC 2012 in Stuttgart",[26052],{"type":11,"value":37004,"toc":37101},[37005,37008,37011,37020,37023,37027,37030,37033,37036,37039,37042,37045,37049,37053,37056,37059,37062,37066,37069,37072,37076,37079,37082,37085,37089,37092,37095,37098],[14,37006,37001],{"id":37007},"synyx-besucht-die-mdc-2012-in-stuttgart",[18,37009,37010],{},"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.",[18,37012,37013,37014,37019],{},"Weitere spannende Talks versprachen Markus Jungingers GreenDAO Talk und natürlich auch ",[585,37015,37018],{"href":37016,"rel":37017},"http://vogella.de",[589],"Lars Vogel","\nseine angekündigte Übersicht über die Neuerungen von Android 4(.2)",[18,37021,37022],{},"Da unsere Gruppe aus iOS als auch Android Developern bestand war schnell klar, dass man sich unterschiedliche Talks\nanschauen möchte.",[2352,37024,37026],{"id":37025},"_1tag","1.Tag",[18,37028,37029],{},"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.",[18,37031,37032],{},"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.",[18,37034,37035],{},"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",[18,37037,37038],{},"Ü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:",[18,37040,37041],{},"“Leute beachtet bitte, MD5 ist kein gutes Verschlüsselungsverfahren” worauf aus dem Publikum mehrfach die Frage kam\n“Warum denn nicht?”.",[18,37043,37044],{},"“Hat hier wer schon mal mit SQL gearbeitet?” Ich würde mal sagen, ca 25% der Teilnehmer haben sich hierauf gemeldet…",[2352,37046,37048],{"id":37047},"_2-tag","2. Tag",[649,37050,37052],{"id":37051},"_1-robert-virkus","1. Robert Virkus",[18,37054,37055],{},"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.",[18,37057,37058],{},"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.",[18,37060,37061],{},"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.",[649,37063,37065],{"id":37064},"_2-testdriven-ios-development","2. Testdriven iOS Development",[18,37067,37068],{},"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.",[18,37070,37071],{},"Wüsste ich es nicht besser da wir selbst bei synyx auch iOS Developer haben würde ich hier mehr… nein ich lasse es 🙂",[649,37073,37075],{"id":37074},"_3-greendao-von-markus-junginger","3. GreenDAO von Markus Junginger",[18,37077,37078],{},"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.",[18,37080,37081],{},"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!",[18,37083,37084],{},"Danach ging es mit einer sehr spannend gehaltenen Keynote zu Ende und es wurde Zeit sich auf den Weg nach Hause zu\nmachen.",[2352,37086,37088],{"id":37087},"blicke-über-den-tellerrand","Blicke über den Tellerrand:",[18,37090,37091],{},"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!",[18,37093,37094],{},"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.",[18,37096,37097],{},"Ich denke wir sind 2013 wieder am Start, diesmal dann eventuell mit eigenen Vorträgen um bestimmte Themen gezielt\nanspruchsvoller zu gestalten…",[18,37099,37100],{},"Das war die MDC 2012, vielen Dank an Florian Bender und sein Team!!!",{"title":48,"searchDepth":86,"depth":86,"links":37102},[37103,37104,37109],{"id":37025,"depth":86,"text":37026},{"id":37047,"depth":86,"text":37048,"children":37105},[37106,37107,37108],{"id":37051,"depth":126,"text":37052},{"id":37064,"depth":126,"text":37065},{"id":37074,"depth":126,"text":37075},{"id":37087,"depth":86,"text":37088},[614],"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":37001,"description":37010},"blog/synyx-besucht-die-mdc-2012-in-stuttgart",[4526,37119,5579,3491,37120,37121,37122,21435,37123,5743],"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":37127,"title":37128,"author":37129,"body":37130,"category":37220,"date":37221,"description":37137,"extension":617,"link":37222,"meta":37223,"navigation":499,"path":37224,"seo":37225,"slug":37134,"stem":37226,"tags":37227,"teaser":37230,"__hash__":37231},"blog/blog/problem-mit-maven-3-dependency-resolution.md","Problem mit Maven 3 Dependency Resolution",[5743],{"type":11,"value":37131,"toc":37215},[37132,37135,37138,37141,37147,37156,37159,37163,37168,37177,37184,37187,37191,37197,37200,37205,37210,37213],[14,37133,37128],{"id":37134},"problem-mit-maven-3-dependency-resolution",[18,37136,37137],{},"Im Zuge meiner Bachelorarbeit habe ich ein Projekt von Maven 2 nach Maven 3 migriert.",[18,37139,37140],{},"Dabei bin ich beim bauen des Projekts mit Maven 3 ein paar mal über eine etwas verwirrende Fehlermeldung gestolpert.",[649,37142,37144],{"id":37143},"das-problem",[27,37145,37146],{},"Das Problem:",[43,37148,37150],{"className":13667,"code":37149,"language":13669,"meta":48,"style":48},"Could not find artifact XXX in nexus.synyx.de (http://nexus.synyx.de)\n",[50,37151,37152],{"__ignoreMap":48},[53,37153,37154],{"class":55,"line":56},[53,37155,37149],{},[18,37157,37158],{},"…und das obwohl das Artefakt in der richtigen Version sowohl lokal als auch im Remote-Repository vorhanden ist.",[649,37160,37162],{"id":37161},"die-ursache","Die Ursache:",[18,37164,37165],{},[573,37166,37167],{},"It’s not a bug, it’s a feature!",[18,37169,37170,37171,37176],{},"Nach etwas Recherche hat sich dann herausgestellt, dass es sich dabei um\nein ",[585,37172,37175],{"href":37173,"rel":37174},"https://cwiki.apache.org/confluence/display/MAVEN/Maven+3.x+Compatibility+Notes#Maven3.xCompatibilityNotes-ResolutionfromLocalRepository",[589],"Maven-3-Feature","\nhandelt mit – sagen wir – unglücklich gewählter Fehlermeldung.",[18,37178,37179,37180,37183],{},"Seit Maven 3 merkt sich Maven in den _",[573,37181,37182],{},"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.",[18,37185,37186],{},"Allerdings kann es durch die wenig aussagekräftige Fehlermeldung bei Benutzern leicht zur Verwirrung kommen.",[649,37188,37190],{"id":37189},"die-lösung","Die Lösung:",[18,37192,37193,37194,37196],{},"Tritt dieses Probelm auf sollte man als erstes kontrollieren ob alle Repositories in der POM und der ",[573,37195,10521],{},"\nrichtig angegeben sind und dies dann gegebenenfalls nachholen.",[18,37198,37199],{},"Sollte die Fehlermeldung immer noch auftreten hilft ein:",[18,37201,37202],{},[50,37203,37204],{},"cd ~/.m2/repository/PFAD/ZUM/ARTEFAKT/",[18,37206,37207],{},[50,37208,37209],{},"rm _maven.repositories",[18,37211,37212],{},"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.",[607,37214,989],{},{"title":48,"searchDepth":86,"depth":86,"links":37216},[37217,37218,37219],{"id":37143,"depth":126,"text":37146},{"id":37161,"depth":126,"text":37162},{"id":37189,"depth":126,"text":37190},[613],"2012-11-05T17:16:49","https://synyx.de/blog/problem-mit-maven-3-dependency-resolution/",{},"/blog/problem-mit-maven-3-dependency-resolution",{"title":37128,"description":37137},"blog/problem-mit-maven-3-dependency-resolution",[37228,37229],"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…","m-ajT4OvhJaUs7SUTw6uOawkbuMCmTKUzn_TrvUJq_k",{"id":37233,"title":37234,"author":37235,"body":37236,"category":37621,"date":37622,"description":37623,"extension":617,"link":37624,"meta":37625,"navigation":499,"path":37626,"seo":37627,"slug":37240,"stem":37629,"tags":37630,"teaser":37633,"__hash__":37634},"blog/blog/properly-calculating-time-differences-in-javascript.md","Properly calculating time differences in JavaScript",[27670],{"type":11,"value":37237,"toc":37619},[37238,37241,37248,37269,37298,37301,37336,37339,37353,37361,37400,37403,37423,37434,37443,37462,37477,37538,37544,37581,37594,37606,37617],[14,37239,37234],{"id":37240},"properly-calculating-time-differences-in-javascript",[18,37242,37243,37244,37247],{},"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 ",[573,37245,37246],{},"Date","object and do\nsome calculating.",[18,37249,37250,37251,37254,37255,37258,37259,37261,37262,37265,37266,37268],{},"As a JavaScript veteran you know that you have to use ",[50,37252,37253],{},"new Date()"," instead of ",[50,37256,37257],{},"Date()"," because the second one returns a\nstring for some reason, you recall that the month of October is identified by the number ",[50,37260,21314],{}," because we start counting\nthe months starting at ",[50,37263,37264],{},"0"," and quickly figure out that subtracting two ",[50,37267,37246],{}," objects results in a number which is the\namount of milliseconds passed between two moments.",[43,37270,37272],{"className":13667,"code":37271,"language":13669,"meta":48,"style":48},"\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",[50,37273,37274,37278,37283,37288,37293],{"__ignoreMap":48},[53,37275,37276],{"class":55,"line":56},[53,37277,500],{"emptyLinePlaceholder":499},[53,37279,37280],{"class":55,"line":86},[53,37281,37282],{},"var DAY_IN_MS = 24 * 60 * 60 * 1000;\n",[53,37284,37285],{"class":55,"line":126},[53,37286,37287],{},"var d1 = new Date(2012, 9, 27);\n",[53,37289,37290],{"class":55,"line":163},[53,37291,37292],{},"var d2 = new Date(2012, 9, 28);\n",[53,37294,37295],{"class":55,"line":186},[53,37296,37297],{},"console.log((d2 - d1) / DAY_IN_MS); // yields 1\n",[18,37299,37300],{},"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",[43,37302,37304],{"className":13667,"code":37303,"language":13669,"meta":48,"style":48},"\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",[50,37305,37306,37310,37314,37318,37322,37327,37331],{"__ignoreMap":48},[53,37307,37308],{"class":55,"line":56},[53,37309,500],{"emptyLinePlaceholder":499},[53,37311,37312],{"class":55,"line":86},[53,37313,37282],{},[53,37315,37316],{"class":55,"line":126},[53,37317,37287],{},[53,37319,37320],{"class":55,"line":163},[53,37321,37292],{},[53,37323,37324],{"class":55,"line":186},[53,37325,37326],{},"var d3 = new Date(2012, 9, 29);\n",[53,37328,37329],{"class":55,"line":221},[53,37330,37297],{},[53,37332,37333],{"class":55,"line":242},[53,37334,37335],{},"console.log((d3 - d2) / DAY_IN_MS); // yields 1.0416666666666667\n",[18,37337,37338],{},"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.",[18,37340,37341,37342,37345,37346,99,37349,37352],{},"The JavaScript object created by ",[50,37343,37344],{},"new Date(2012, 9, 28)"," represents midnight on the 28th of October, 2012 ",[573,37347,37348],{},"in your local\ntime zone",[50,37350,37351],{},"new Date(2012, 9, 29)"," represents midnight the following day.",[18,37354,37355,37356,986],{},"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 ",[585,37357,37360],{"href":37358,"rel":37359},"http://www.timeanddate.com/worldclock/clockchange.html?n=37",[589],"daylight savings time",[43,37362,37364],{"className":13667,"code":37363,"language":13669,"meta":48,"style":48},"\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",[50,37365,37366,37370,37375,37380,37385,37390,37395],{"__ignoreMap":48},[53,37367,37368],{"class":55,"line":56},[53,37369,500],{"emptyLinePlaceholder":499},[53,37371,37372],{"class":55,"line":86},[53,37373,37374],{},"> new Date(2012, 9, 29);\n",[53,37376,37377],{"class":55,"line":126},[53,37378,37379],{},"Mon Oct 29 2012 00:00:00 GMT+0100 (CET)\n",[53,37381,37382],{"class":55,"line":163},[53,37383,37384],{},"> new Date(2012, 9, 28);\n",[53,37386,37387],{"class":55,"line":186},[53,37388,37389],{},"Sun Oct 28 2012 00:00:00 GMT+0200 (CEST)\n",[53,37391,37392],{"class":55,"line":221},[53,37393,37394],{},"> (new Date(2012, 9, 29) - new Date(2012, 9, 28)) / 60 / 60 / 100\n",[53,37396,37397],{"class":55,"line":242},[53,37398,37399],{},"25\n",[18,37401,37402],{},"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.",[18,37404,37405,37406,7040,37409,37413,37414,3566,37418,37422],{},"If you Google “JavaScript time difference”, most people just use\n",[50,37407,37408],{},"Math.round",[585,37410,7598],{"href":37411,"rel":37412},"http://psoug.org/snippet/Javascript-Calculate-time-difference-between-two-dates_116.htm",[589],") or simply\nuse flat-out buggy\ncode (",[585,37415,7598],{"href":37416,"rel":37417},"https://web.archive.org/web/20151119010127/http://www-10.lotus.com:80/ldd/ddwiki.nsf/dx/Various_Time_Differences_in_JavaScript",[589],[585,37419,7607],{"href":37420,"rel":37421},"http://www.javascriptkit.com/javatutors/datedifference.shtml",[589],")\nand call it a day (pun intended), but that is not how we roll here.",[18,37424,37425,37426,37429,37430,37433],{},"What do we really mean when we ask ",[573,37427,37428],{},"“How many days have passed between two dates in the calendar”","? We usually mean\n",[573,37431,37432],{},"“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…",[18,37435,37436,37437,37442],{},"Luckily, there is, and that place is ",[585,37438,37441],{"href":37439,"rel":37440},"http://en.wikipedia.org/wiki/Coordinated_Universal_Time",[589],"UTC",". UTC is a time\nmeasuring system that does not have daylight savings time.",[18,37444,37445,3566,37448],{},[27,37446,37447],{},"Edit:",[573,37449,37450,37451,37456,37457,986],{},"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 ",[585,37452,37455],{"href":37453,"rel":37454},"http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.1",[589],"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 ",[585,37458,37461],{"href":37459,"rel":37460},"http://en.wikipedia.org/wiki/Universal_Time#Versions",[589],"UT1",[18,37463,37464,37465,37467,37468,37470,37471,3566,37473,37476],{},"The JavaScript Date API is just as beautiful as most other JavaScript APIs: While the only useful use of the ",[50,37466,37246],{},"\nobject is by using it as a constructor (with ",[50,37469,24577],{},"), the way to use UTC is by using the ",[573,37472,5789],{},[50,37474,37475],{},"Date.UTC"," which\nreturns a unix timestamp. This is the JavaScript time API in a nutshell:",[43,37478,37480],{"className":13667,"code":37479,"language":13669,"meta":48,"style":48},"\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",[50,37481,37482,37486,37490,37495,37500,37508,37513,37518,37523,37528,37533],{"__ignoreMap":48},[53,37483,37484],{"class":55,"line":56},[53,37485,500],{"emptyLinePlaceholder":499},[53,37487,37488],{"class":55,"line":86},[53,37489,37374],{},[53,37491,37492],{"class":55,"line":126},[53,37493,37494],{},"Mon Oct 29 2012 00:00:00 GMT+0100 (CET) // (a somewhat useful object)\n",[53,37496,37497],{"class":55,"line":163},[53,37498,37499],{},"> Date(2012, 9, 29);\n",[53,37501,37502,37505],{"class":55,"line":186},[53,37503,37504],{},"'Mon Nov 05 2012 16:18:12 GMT+0100 (CET)'",[53,37506,37507],{}," // (a string - no relation to the parameters)\n",[53,37509,37510],{"class":55,"line":221},[53,37511,37512],{},"> Date.UTC(2012, 9, 29);\n",[53,37514,37515],{"class":55,"line":242},[53,37516,37517],{},"1351468800000 // (unix time in milliseconds)\n",[53,37519,37520],{"class":55,"line":273},[53,37521,37522],{},"> new Date.UTC(2012, 9, 29); // failure\n",[53,37524,37525],{"class":55,"line":279},[53,37526,37527],{},"TypeError: function UTC() { [native code] } is not a constructor\n",[53,37529,37530],{"class":55,"line":496},[53,37531,37532],{}," at repl:1:9\n",[53,37534,37535],{"class":55,"line":503},[53,37536,37537],{}," [....]\n",[18,37539,37540,37541,37543],{},"The correct calculation, without using ",[50,37542,37408],{}," or other hacks therefore is",[43,37545,37547],{"className":13667,"code":37546,"language":13669,"meta":48,"style":48},"\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",[50,37548,37549,37553,37557,37562,37567,37572,37576],{"__ignoreMap":48},[53,37550,37551],{"class":55,"line":56},[53,37552,500],{"emptyLinePlaceholder":499},[53,37554,37555],{"class":55,"line":86},[53,37556,37282],{},[53,37558,37559],{"class":55,"line":126},[53,37560,37561],{},"var d1 = Date.UTC(2012, 9, 27);\n",[53,37563,37564],{"class":55,"line":163},[53,37565,37566],{},"var d2 = Date.UTC(2012, 9, 28);\n",[53,37568,37569],{"class":55,"line":186},[53,37570,37571],{},"var d3 = Date.UTC(2012, 9, 29);\n",[53,37573,37574],{"class":55,"line":221},[53,37575,37297],{},[53,37577,37578],{"class":55,"line":242},[53,37579,37580],{},"console.log((d3 - d2) / DAY_IN_MS); // yields 1\n",[18,37582,37583,37584,37587,37588,37593],{},"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 ",[573,37585,37586],{},"nearly"," correct, and we are not used to thinking about time\nzones and\nwe ",[585,37589,37592],{"href":37590,"rel":37591},"http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time",[589],"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.",[18,37595,37596,37597,37602,37603,37605],{},"Libraries like ",[585,37598,37601],{"href":37599,"rel":37600},"http://momentjs.com/",[589],"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 ",[50,37604,37408],{}," in there to make it all work.",[18,37607,37608,37609,37612,37613,37616],{},"Just as anybody that has had the pleasure of seeing ",[573,37610,37611],{},"Rent"," will tell you: while a year has ",[573,37614,37615],{},"five hundred twenty-five\nthousand six hundred minutes",", it still is difficult to measure the time of the year.",[607,37618,989],{},{"title":48,"searchDepth":86,"depth":86,"links":37620},[],[613],"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":37234,"description":37628},"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",[2596,6991,37631,37632],"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":37636,"title":37637,"author":37638,"body":37640,"category":37683,"date":37684,"description":37647,"extension":617,"link":37685,"meta":37686,"navigation":499,"path":37687,"seo":37688,"slug":37644,"stem":37689,"tags":37690,"teaser":37696,"__hash__":37697},"blog/blog/opencms-days-2012.md","OpenCms-Days 2012",[37639],"daniel",{"type":11,"value":37641,"toc":37681},[37642,37645,37648,37651,37661,37664,37672,37675,37678],[14,37643,37637],{"id":37644},"opencms-days-2012",[18,37646,37647],{},"Von Montag, den 24.09 und Dienstag, den 25.09, fanden in Köln die OpenCms-Days 2012 statt.",[18,37649,37650],{},"Gemeinsam mit dem ehemaligen synyxer Florian Hopf haben sich Oliver Messner, Fabian Buch und ich schon am Sonntagabend\nnach Köln begeben.",[18,37652,37653,37654,37660],{},"Am Montagmorgen eröffnete Alexander Kandzior von Alkacon die Veranstaltung mit der Vorstellung der\nnagelneuen ",[585,37655,37659],{"href":37656,"rel":37657,"title":37658},"http://www.opencms.org/en/news/120924-opencms-v85-releasenotes.html",[589],"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.",[18,37662,37663],{},"Es folgten Vorträge zum Thema OpenCms und zur Entwicklung mit OpenCms bis hin zur High Availability und zum Continouos\nIntegration.",[18,37665,37666,37667],{},"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: ",[585,37668,37671],{"href":37669,"rel":37670,"title":37671},"http://www.opencms-days.org/news/Developing-OpenCms-with-Gradle/",[589],"Developing OpenCms with Gradle",[18,37673,37674],{},"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.",[18,37676,37677],{},"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.",[18,37679,37680],{},"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":48,"searchDepth":86,"depth":86,"links":37682},[],[996],"2012-10-09T11:45:08","https://synyx.de/blog/opencms-days-2012/",{},"/blog/opencms-days-2012",{"title":37637,"description":37647},"blog/opencms-days-2012",[37691,37692,37693,37694,37695],"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":37699,"title":37700,"author":37701,"body":37702,"category":37757,"date":37758,"description":37709,"extension":617,"link":37759,"meta":37760,"navigation":499,"path":37761,"seo":37762,"slug":37706,"stem":37763,"tags":37764,"teaser":37769,"__hash__":37770},"blog/blog/synyx-goes-to-europa-park.md","synyx goes to Europa Park",[36583],{"type":11,"value":37703,"toc":37755},[37704,37707,37710,37713,37716,37722,37725,37728,37731,37734,37737,37740,37746,37749,37752],[14,37705,37700],{"id":37706},"synyx-goes-to-europa-park",[18,37708,37709],{},"Freitag früh, um kurz nach 7 in Karlsruhe…",[18,37711,37712],{},"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.",[18,37714,37715],{},"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.",[18,37717,37718],{},[1773,37719],{"alt":37720,"src":37721},"\"europapark\"","https://media.synyx.de/uploads//2012/10/europapark.jpg",[18,37723,37724],{},"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 😉",[18,37726,37727],{},"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?",[18,37729,37730],{},"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.",[18,37732,37733],{},"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.",[18,37735,37736],{},"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.",[18,37738,37739],{},"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.",[18,37741,37742],{},[1773,37743],{"alt":37744,"src":37745},"\"europapark_02\"","https://media.synyx.de/uploads//2012/10/europapark_02.jpg",[18,37747,37748],{},"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.",[18,37750,37751],{},"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.",[18,37753,37754],{},"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":48,"searchDepth":86,"depth":86,"links":37756},[],[614],"2012-10-01T16:35:30","https://synyx.de/blog/synyx-goes-to-europa-park/",{},"/blog/synyx-goes-to-europa-park",{"title":37700,"description":37709},"blog/synyx-goes-to-europa-park",[37765,37766,37767,37768,5743],"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…","4ZUMDpwJD6HGYtMqbfJBUcv0zb-RlEy-9rV3L4Ncu4E",{"id":37772,"title":37773,"author":37774,"body":37776,"category":38142,"date":38143,"description":38144,"extension":617,"link":38145,"meta":38146,"navigation":499,"path":38147,"seo":38148,"slug":37780,"stem":38149,"tags":38150,"teaser":38153,"__hash__":38154},"blog/blog/studien-projektarbeit-mit-grails.md","Studien-Projektarbeit mit Grails",[37775],"matt",{"type":11,"value":37777,"toc":38140},[37778,37781,37784,37787,37790,37793,37802,37811,37816,37830,37839,37842,37851,37854,37873,37876,37879,37888,37891,37894,37903,37906,37916,38013,38016,38019,38066,38069,38075,38078,38087,38090,38100,38135,38138],[14,37779,37773],{"id":37780},"studien-projektarbeit-mit-grails",[18,37782,37783],{},"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.",[18,37785,37786],{},"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.",[18,37788,37789],{},"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.",[18,37791,37792],{},"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.",[18,37794,37795,37796,37801],{},"Es stellt sich nun die Frage wie ich überhaupt zum Thema ",[585,37797,37800],{"href":37798,"rel":37799,"title":37800},"http://grails.org/",[589],"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.",[18,37803,37804,37805,37810],{},"Das sollte genug zur Vorgeschichte des Studien-Projekts sein,. In einem kleinen Tutorial zum das\nGrails ",[585,37806,37809],{"href":37807,"rel":37808},"https://web.archive.org/web/20170106062111/http://www.grails.org:80/plugin/spring-security-core",[589],"Spring-Security -Plugin","\ns möchte das Plugin-System von Grails demonstrieren:",[18,37812,37813],{},[27,37814,37815],{},"Meine Umgebung:",[577,37817,37818,37821,37824,37827],{},[580,37819,37820],{},"Ubuntu 12.04",[580,37822,37823],{},"Eclipse IDE (Groovy/Grails Tool Suite von SpringSource)",[580,37825,37826],{},"Grails 2.1.0",[580,37828,37829],{},"Java 1.6.0_24",[18,37831,37832,37833,37838],{},"Um das Grails-Plugin nutzen zu können, ist es nötig das hierzu\nbenötigte ",[585,37834,37837],{"href":37807,"rel":37835,"title":37836},[589],"SpringSecurityPlugin","Plugin","\nzu installieren. Es gibt 2 Möglichkeiten dies zu bewerkstelligen:",[18,37840,37841],{},"Über die Komandozeile:",[43,37843,37845],{"className":13667,"code":37844,"language":13669,"meta":48,"style":48},"grails install-plugin spring-security-core\n",[50,37846,37847],{"__ignoreMap":48},[53,37848,37849],{"class":55,"line":56},[53,37850,37844],{},[18,37852,37853],{},"oder über einen Eintrag in der grails-app/conf/BuildConfig.groovy:",[43,37855,37857],{"className":13667,"code":37856,"language":13669,"meta":48,"style":48},"plugins {\n compile \":spring-security-core:1.2.7.1\"\n}\n",[50,37858,37859,37864,37869],{"__ignoreMap":48},[53,37860,37861],{"class":55,"line":56},[53,37862,37863],{},"plugins {\n",[53,37865,37866],{"class":55,"line":86},[53,37867,37868],{}," compile \":spring-security-core:1.2.7.1\"\n",[53,37870,37871],{"class":55,"line":126},[53,37872,282],{},[18,37874,37875],{},"Da ich die Übersicht der BuildConfig inzwischen zu schätzen weiß, wählte ich die zweite Variante.",[18,37877,37878],{},"Zur Überprüfung/Installation des Plugins muss nun der Befehl",[43,37880,37882],{"className":13667,"code":37881,"language":13669,"meta":48,"style":48},"grails compile\n",[50,37883,37884],{"__ignoreMap":48},[53,37885,37886],{"class":55,"line":56},[53,37887,37881],{},[18,37889,37890],{},"ausgeführt werden. Dieser Befehl löst die eingetragene Plugin-Dependency auf.",[18,37892,37893],{},"Der nächste Schritt nutzt nun gleich ein Skript des installierten Plugin für die Erstellung der nötigen Domain-Klassen.",[43,37895,37897],{"className":13667,"code":37896,"language":13669,"meta":48,"style":48},"grails s2-quickstart \u003CmeinePackageStruktur> User Role\n",[50,37898,37899],{"__ignoreMap":48},[53,37900,37901],{"class":55,"line":56},[53,37902,37896],{},[18,37904,37905],{},"Diese 3 kleinen Befehle reichen für die Grundfunktionalität von SpringSecurity aus.",[18,37907,37908,37909,37915],{},"Zur Überprüfung der Login-Funktionalität macht es Sinn in\nder ",[585,37910,37914],{"href":37911,"rel":37912,"title":37913},"https://web.archive.org/web/20140413052251/http://grails.org:80/Bootstrap+Classes",[589],"Bootstrap","grails-app/conf/Bootstrap.groovy","\nein paar Testdaten einzufügen:",[43,37917,37919],{"className":12891,"code":37918,"language":12893,"meta":48,"style":48},"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",[50,37920,37921,37926,37931,37936,37941,37946,37951,37956,37961,37966,37971,37976,37981,37985,37989,37994,37999,38004,38009],{"__ignoreMap":48},[53,37922,37923],{"class":55,"line":56},[53,37924,37925],{},"class BootStrap{\n",[53,37927,37928],{"class":55,"line":86},[53,37929,37930],{}," def init={servletContext->\n",[53,37932,37933],{"class":55,"line":126},[53,37934,37935],{}," RoleuserRole=Role.findByAuthority('ROLE_USER')?:newRole(authority:'ROLE_USER').save(failOnError:true)\n",[53,37937,37938],{"class":55,"line":163},[53,37939,37940],{}," Role adminRole=Role.findByAuthority('ROLE_ADMIN')?:newRole(authority:'ROLE_ADMIN').save(failOnError:true)\n",[53,37942,37943],{"class":55,"line":186},[53,37944,37945],{}," UseradminUser=User.findByUsername('adminUser')?:newUser(username:'adminUser',enabled:true,password:'topSecret').save(failOnError:true)\n",[53,37947,37948],{"class":55,"line":221},[53,37949,37950],{}," if(!adminUser .authorities.contains(userRole))\n",[53,37952,37953],{"class":55,"line":242},[53,37954,37955],{}," UserRole.create(adminUser,userRole,true)\n",[53,37957,37958],{"class":55,"line":273},[53,37959,37960],{}," if(!adminUser .authorities.contains(adminRole))\n",[53,37962,37963],{"class":55,"line":279},[53,37964,37965],{}," UserRole.create(adminUser,adminRole,true)\n",[53,37967,37968],{"class":55,"line":496},[53,37969,37970],{}," User dummyUser=User.findByUsername('dummyUser')?:newUser(username:'dummyUser',enabled:true,password:'notSoSecret').save(failOnError:true)\n",[53,37972,37973],{"class":55,"line":503},[53,37974,37975],{}," if(!dummyUser.authorities.contains(userRole))\n",[53,37977,37978],{"class":55,"line":509},[53,37979,37980],{}," UserRole.create(dummyUser,userRole,true)\n",[53,37982,37983],{"class":55,"line":515},[53,37984,322],{},[53,37986,37987],{"class":55,"line":521},[53,37988,7109],{},[53,37990,37991],{"class":55,"line":527},[53,37992,37993],{}," assertUser.count()==2\n",[53,37995,37996],{"class":55,"line":533},[53,37997,37998],{}," assertRole.count()==2\n",[53,38000,38001],{"class":55,"line":539},[53,38002,38003],{}," assertUserRole.count()==3\n",[53,38005,38006],{"class":55,"line":545},[53,38007,38008],{}," …\n",[53,38010,38011],{"class":55,"line":2070},[53,38012,282],{},[18,38014,38015],{},"Nun kann man beliebige Teile seiner Applikation von SpringSecurity überwachen lassen.",[18,38017,38018],{},"Ob auf Klassenebene/Methodenebene kann einfach per Annotation gesteuert werden.",[43,38020,38022],{"className":12891,"code":38021,"language":12893,"meta":48,"style":48},"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",[50,38023,38024,38029,38034,38039,38044,38048,38053,38058,38062],{"__ignoreMap":48},[53,38025,38026],{"class":55,"line":56},[53,38027,38028],{},"import org.springframework.dao.DataIntegrityViolationException\n",[53,38030,38031],{"class":55,"line":86},[53,38032,38033],{},"import grails.plugins.springsecurity.Secured\n",[53,38035,38036],{"class":55,"line":126},[53,38037,38038],{},"@Secured(['ROLE_USER'])\n",[53,38040,38041],{"class":55,"line":163},[53,38042,38043],{},"class MyFancyController {\n",[53,38045,38046],{"class":55,"line":186},[53,38047,5968],{},[53,38049,38050],{"class":55,"line":221},[53,38051,38052],{},"@Secured(['ROLE_ADMIN'])\n",[53,38054,38055],{"class":55,"line":242},[53,38056,38057],{},"def mySuperfancyAdminOnlyMethod(){\n",[53,38059,38060],{"class":55,"line":273},[53,38061,5968],{},[53,38063,38064],{"class":55,"line":279},[53,38065,282],{},[18,38067,38068],{},"Ein kurzer Funktionstest auf der Weboberfläche überzeugt von der Funktionalität.",[18,38070,38071],{},[1773,38072],{"alt":38073,"src":38074},"\"Grails SpringSecurity login screen\"","https://media.synyx.de/uploads//2012/09/Bildschirmfoto-vom-2012-09-10-1801541.png",[18,38076,38077],{},"Über den Controller:",[43,38079,38081],{"className":13667,"code":38080,"language":13669,"meta":48,"style":48}," localhost:\u003CmyPort>/\u003CmyApp>/dbconsole\n",[50,38082,38083],{"__ignoreMap":48},[53,38084,38085],{"class":55,"line":56},[53,38086,38080],{},[18,38088,38089],{},"lässt sich die Struktur von der Datenhaltung in SpringSecurity leicht nachvollziehen.",[18,38091,38092,38093,38099],{},"Um noch mehr Funktionalität und auch generierte Admin-Oberflächen zu bekommen, ist es möglich\ndas ",[585,38094,38098],{"href":38095,"rel":38096,"title":38097},"https://web.archive.org/web/20170104185319/http://grails.org/plugin/spring-security-ui",[589],"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:",[577,38101,38102,38105,38108,38111,38114,38117,38120,38123,38126,38129,38132],{},[580,38103,38104],{},"Spring Security ACL which adds support for object-level and method-level authorization using ACLs (access control\nlist)",[580,38106,38107],{},"Spring Security AppInfo which provides a basic UI to view the security configuration",[580,38109,38110],{},"Spring Security CAS which adds support for single sign-on using Jasig CAS",[580,38112,38113],{},"Spring Security OpenID which adds support for OpenID authentication",[580,38115,38116],{},"Spring Security Facebook which adds support for Facebook authentication",[580,38118,38119],{},"Spring Security Kerberos which adds support for single sign-on using Kerberos",[580,38121,38122],{},"Spring Security LDAP which adds support for LDAP and ActiveDirectory authentication",[580,38124,38125],{},"Spring Security Mock which adds support for fake/mock authentication during developement",[580,38127,38128],{},"Spring Security RADIUS which adds support for RADIUS authentication",[580,38130,38131],{},"Spring Security Shibboleth Native SP which adds support for container provided Shibboleth authentication.",[580,38133,38134],{},"Spring Security Twitter which adds support for Twitter authentication",[18,38136,38137],{},"Dies sollte einen kleine Überblick über den Aufbau und die Installation eines Grails-Plugins im Allgemeinen und von\nspringSecurity im speziellen geben können.",[607,38139,989],{},{"title":48,"searchDepth":86,"depth":86,"links":38141},[],[5568],"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":37773,"description":37783},"blog/studien-projektarbeit-mit-grails",[38151,38152,625],"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…","tdjMkPlIVQL35Q9ecSB0rUESlMxdBITjcE_BHdDrQbI",{"id":38156,"title":38157,"author":38158,"body":38159,"category":38224,"date":38225,"description":38226,"extension":617,"link":38227,"meta":38228,"navigation":499,"path":38229,"seo":38230,"slug":38163,"stem":38231,"tags":38232,"teaser":38234,"__hash__":38235},"blog/blog/froscon-7-resumee.md","FrOSCon 7 – resumee",[33954],{"type":11,"value":38160,"toc":38222},[38161,38164,38167,38170,38177,38180,38183,38186,38189,38192,38195,38198,38201,38203,38206,38209,38212,38215],[14,38162,38157],{"id":38163},"froscon-7-resumee",[18,38165,38166],{},"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.",[18,38168,38169],{},"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.",[18,38171,38172,38173],{},"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.",[1773,38174],{"alt":38175,"src":38176},"\"Haltestelle St. Augustin\"","https://media.synyx.de/uploads//2012/09/2012-08-26-00.51.26.jpg",[18,38178,38179],{},"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.",[18,38181,38182],{},"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.",[18,38184,38185],{},"Sternförmiges Ausschwärmen in die verschiedenen Vortragssäle.",[18,38187,38188],{},"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.",[18,38190,38191],{},"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.",[18,38193,38194],{},"Köllsch.",[18,38196,38197],{},"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…",[18,38199,38200],{},"Wieder in der, bereits sehr heimelich angemuteten FH, viel Kaffee – Opensource-Kaffee, um genau zu sein.",[18,38202,38185],{},[18,38204,38205],{},"Auch der Sonntag war mit einem sehr guten Vortragsprogramm gesegnet.",[18,38207,38208],{},"Aus “git-goodies” konnten wir trotz routiniertem Umgang mit git doch einiges mitnehmen.",[18,38210,38211],{},"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,",[18,38213,38214],{},"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.",[18,38216,38217,38218],{},"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. ",[1773,38219],{"alt":38220,"src":38221},"\"Rainbow\"","https://media.synyx.de/uploads//2012/09/2012-08-26-19.13.53-e1346672801416.jpg",{"title":48,"searchDepth":86,"depth":86,"links":38223},[],[996],"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":38157,"description":38166},"blog/froscon-7-resumee",[3491,38233,4231,34779,12072],"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…","fQPSRhWHMXQEzfHyb-ZVDxs_PWztoJDUhcykCUQrxTc",{"id":38237,"title":38238,"author":38239,"body":38241,"category":38557,"date":38558,"description":38559,"extension":617,"link":38560,"meta":38561,"navigation":499,"path":38562,"seo":38563,"slug":38245,"stem":38564,"tags":38565,"teaser":38569,"__hash__":38570},"blog/blog/big-jasper-reports-with-custom-xml-datasource.md","Big Jasper Reports with custom XML datasource",[38240],"steinegger",{"type":11,"value":38242,"toc":38553},[38243,38246,38249,38260,38263,38274,38278,38286,38290,38293,38322,38326,38333,38347,38350,38355,38362,38367,38374,38380,38497,38501,38508,38517,38521,38524,38530,38533,38536,38539,38542,38545,38548,38551],[14,38244,38238],{"id":38245},"big-jasper-reports-with-custom-xml-datasource",[18,38247,38248],{},"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.",[18,38250,38251,38252,38255,38256,38259],{},"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 ",[27,38253,38254],{},"SAXPullParser"," implementation in order to control which is the current parsed tag\nrather than being called by the ",[27,38257,38258],{},"SAXParser"," itself.",[18,38261,38262],{},"So what are the steps to use a SAXPullParser as part of a custom data source:",[3525,38264,38265,38268,38271],{},[580,38266,38267],{},"Create the XML raw data format",[580,38269,38270],{},"Write a custom data source",[580,38272,38273],{},"Incorporate the data source into the report generation",[649,38275,38277],{"id":38276},"sample-project","Sample project",[18,38279,10933,38280,38285],{},[585,38281,38284],{"href":38282,"rel":38283},"https://media.synyx.de/uploads//2012/08/jasperreports.zip",[589],"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.",[1217,38287,38289],{"id":38288},"the-xml-format","The XML format",[18,38291,38292],{},"The XML format is very simple and represents e.g. all Persons of a company:",[43,38294,38296],{"className":1980,"code":38295,"language":1982,"meta":48,"style":48},"\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",[50,38297,38298,38303,38308,38313,38317],{"__ignoreMap":48},[53,38299,38300],{"class":55,"line":56},[53,38301,38302],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[53,38304,38305],{"class":55,"line":86},[53,38306,38307],{},"\u003CPersons>\n",[53,38309,38310],{"class":55,"line":126},[53,38311,38312],{}," \u003CPerson firstname=\"First\" lastname=\"Last\" mail=\"first.last@test.test\" title=\"nice guy\" age=\"47\" status=\"married\"/>\n",[53,38314,38315],{"class":55,"line":163},[53,38316,322],{},[53,38318,38319],{"class":55,"line":186},[53,38320,38321],{},"\u003C/Persons>\n",[1217,38323,38325],{"id":38324},"the-custom-data-source","The custom data source",[18,38327,38328,38329,38332],{},"The custom data source extends the ",[27,38330,38331],{},"JRAbstractTextDataSource"," and therefore the sample implementation has to override\nthe methods",[577,38334,38335,38341],{},[580,38336,38337,38340],{},[573,38338,38339],{},"boolean next():"," Determines whether or not there is another row to display",[580,38342,38343,38346],{},[573,38344,38345],{},"Object getFieldValue(JRField field)",": Requests the value for the given field/cell",[18,38348,38349],{},"It simply implements the Iterator pattern in order to render all columns of the report.",[18,38351,38352],{},[27,38353,38354],{},"Initialization",[18,38356,38357,38358,38361],{},"The constructor of the class expects an Inputstream that represents the XML source. Based on that stream the class\ninitializes a ",[27,38359,38360],{},"XMLStreamReader"," which is basically an iterator over the XML tags.",[18,38363,38364],{},[27,38365,38366],{},"boolean next()",[18,38368,38369,38370,38373],{},"The implementation of the ",[573,38371,38372],{},"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.",[18,38375,38376,38377,38379],{},"Every subsequent call to the ",[573,38378,38372],{}," 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.",[43,38381,38383],{"className":288,"code":38382,"language":290,"meta":48,"style":48}," 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",[50,38384,38385,38390,38395,38400,38405,38410,38415,38420,38425,38430,38435,38440,38445,38450,38455,38460,38465,38469,38474,38479,38484,38488,38493],{"__ignoreMap":48},[53,38386,38387],{"class":55,"line":56},[53,38388,38389],{}," int eventType = xmlStreamReader.getEventType();\n",[53,38391,38392],{"class":55,"line":86},[53,38393,38394],{}," String tagName = null;\n",[53,38396,38397],{"class":55,"line":126},[53,38398,38399],{}," boolean isStart = false;\n",[53,38401,38402],{"class":55,"line":163},[53,38403,38404],{}," while (xmlStreamReader.hasNext()) {\n",[53,38406,38407],{"class":55,"line":186},[53,38408,38409],{}," eventType = xmlStreamReader.next();\n",[53,38411,38412],{"class":55,"line":221},[53,38413,38414],{}," switch (eventType) {\n",[53,38416,38417],{"class":55,"line":242},[53,38418,38419],{}," case XMLEvent.START_ELEMENT:\n",[53,38421,38422],{"class":55,"line":273},[53,38423,38424],{}," isStart = true;\n",[53,38426,38427],{"class":55,"line":279},[53,38428,38429],{}," case XMLEvent.END_ELEMENT:\n",[53,38431,38432],{"class":55,"line":496},[53,38433,38434],{}," tagName = xmlStreamReader.getLocalName();\n",[53,38436,38437],{"class":55,"line":503},[53,38438,38439],{}," // check if there is still a person element left\n",[53,38441,38442],{"class":55,"line":509},[53,38443,38444],{}," if (isStart && PERSON_TAG_NAME.equals(tagName)) {\n",[53,38446,38447],{"class":55,"line":515},[53,38448,38449],{}," return true;\n",[53,38451,38452],{"class":55,"line":521},[53,38453,38454],{}," } else if (!isStart && PERSONS_TAG_NAME.equals(tagName)) {\n",[53,38456,38457],{"class":55,"line":527},[53,38458,38459],{}," // end tag of persons, nothing else to handle\n",[53,38461,38462],{"class":55,"line":533},[53,38463,38464],{}," return false;\n",[53,38466,38467],{"class":55,"line":539},[53,38468,12712],{},[53,38470,38471],{"class":55,"line":545},[53,38472,38473],{}," break;\n",[53,38475,38476],{"class":55,"line":2070},[53,38477,38478],{}," case XMLEvent.END_DOCUMENT:\n",[53,38480,38481],{"class":55,"line":2075},[53,38482,38483],{}," return false;\n",[53,38485,38486],{"class":55,"line":2081},[53,38487,14311],{},[53,38489,38490],{"class":55,"line":2087},[53,38491,38492],{}," isStart = false;\n",[53,38494,38495],{"class":55,"line":2092},[53,38496,7109],{},[18,38498,38499],{},[27,38500,38345],{},[18,38502,38503,38504,38507],{},"The implementation of",[573,38505,38506],{},"getFieldValue(JRField field)"," is very simple, because the attribute name is exactly the name that\nis assigned in the Jasper Report document.",[43,38509,38511],{"className":288,"code":38510,"language":290,"meta":48,"style":48},"return xmlStreamReader.getAttributeValue(null, field.getName());\n",[50,38512,38513],{"__ignoreMap":48},[53,38514,38515],{"class":55,"line":56},[53,38516,38510],{},[649,38518,38520],{"id":38519},"brining-all-together","Brining all together",[18,38522,38523],{},"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.",[18,38525,10933,38526,38529],{},[27,38527,38528],{},"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:",[18,38531,38532],{},"`Running com.jasperreports.ReportGeneratorTest",[18,38534,38535],{},"INFO ReportGenerator - Created report with default XML data source in 4.136 seconds.",[18,38537,38538],{},"INFO ReportGenerator - Created report with custom datasource in 0.684 seconds.",[18,38540,38541],{},"INFO ReportGenerator - Created report with default XML data source in 21.463 seconds.",[18,38543,38544],{},"INFO ReportGenerator - Created report with custom datasource in 3.495 seconds.",[18,38546,38547],{},"Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 30.267 sec`",[18,38549,38550],{},"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.",[607,38552,989],{},{"title":48,"searchDepth":86,"depth":86,"links":38554},[38555,38556],{"id":38276,"depth":126,"text":38277},{"id":38519,"depth":126,"text":38520},[996],"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":38238,"description":38248},"blog/big-jasper-reports-with-custom-xml-datasource",[38566,38567,38568],"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":38572,"title":38573,"author":38574,"body":38575,"category":38673,"date":38674,"description":38675,"extension":617,"link":38676,"meta":38677,"navigation":499,"path":38678,"seo":38679,"slug":38579,"stem":38680,"tags":38681,"teaser":38684,"__hash__":38685},"blog/blog/meine-ausbildung-bei-synyx-teil-2.md","Meine Ausbildung bei synyx – Teil 2",[12861],{"type":11,"value":38576,"toc":38668},[38577,38580,38591,38599,38602,38605,38609,38612,38619,38645,38649,38652,38658,38665],[14,38578,38573],{"id":38579},"meine-ausbildung-bei-synyx-teil-2",[18,38581,38582],{},[573,38583,38584,38585,38590],{},"(Fortsetzung ",[585,38586,38589],{"href":38587,"rel":38588},"http://blog.synyx.de/2011/11/meine-ausbildung-bei-synyx/",[589],"meines Blogposts"," von letztem Jahr)",[18,38592,38593,38594,38598],{},"Das dritte Jahr meiner Ausbildung bei synyx spielte sich zum Großteil in der\nAbteilung ",[585,38595,8916],{"href":38596,"rel":38597},"https://synyx.de/code-clinic-softwareoptimierung/",[589]," ab, in der ich etliche neue Technologien und\nFrameworks kennengelernt und eingesetzt habe.",[649,38600,7906],{"id":38601},"rückblick",[18,38603,38604],{},"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.",[649,38606,38608],{"id":38607},"willkommen-in-der-enterprise-welt","Willkommen in der Enterprise Welt!",[18,38610,38611],{},"Nicht so in der Code Clinic.",[18,38613,38614,38618],{},[1773,38615],{"alt":38616,"src":38617},"\"code_clinic\"","https://media.synyx.de/uploads//2012/07/code_clinic.jpg","\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.",[18,38620,38621,38622,99,38628,38633,38634,38640,38641,38644],{},"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 ",[585,38623,38627],{"href":38624,"rel":38625,"title":38626},"http://www.eclipse.org/birt/phoenix/",[589],"birt","Birt",[585,38629,38632],{"href":38630,"rel":38631,"title":38632},"http://jasperforge.org/projects/jasperreports/",[589],"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 ",[585,38635,38639],{"href":38636,"rel":38637,"title":38638},"http://wicket.apache.org/",[589],"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 ",[585,38642,29172],{"href":29170,"rel":38643,"title":29806},[589]," ein, das diese Arbeit für uns erleichtert und die Änderungen\nautomatisiert vornehmen kann.",[649,38646,38648],{"id":38647},"abschlussprojekt","Abschlussprojekt",[18,38650,38651],{},"Ab und an war ich jedoch auch in anderen Projekten, wie zum Beispiel meinem Abschlussprojekt.",[18,38653,38654],{},[1773,38655],{"alt":48,"src":38656,"title":38657},"https://media.synyx.de/uploads//2012/07/detail_-150x150.jpg","detail_",[18,38659,38660,38664],{},[1773,38661],{"alt":38662,"src":38663},"\"list_snip_\"","https://media.synyx.de/uploads//2012/07/list_snip_.jpg","\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 >.>",[18,38666,38667],{},"Bestanden habe ich aber natürlich und bin seitdem als Junior Developer bei synyx angestellt!",{"title":48,"searchDepth":86,"depth":86,"links":38669},[38670,38671,38672],{"id":38601,"depth":126,"text":7906},{"id":38607,"depth":126,"text":38608},{"id":38647,"depth":126,"text":38648},[5568],"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":38573,"description":38675},"blog/meine-ausbildung-bei-synyx-teil-2",[5579,38682,38683],"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…","APvLrTGR6DcYaFkpKKdGvyQx5pgfZci7sRMIeJ1fp88",{"id":38687,"title":38688,"author":38689,"body":38690,"category":38728,"date":38729,"description":38730,"extension":617,"link":38731,"meta":38732,"navigation":499,"path":38733,"seo":38734,"slug":38694,"stem":38735,"tags":38736,"teaser":38738,"__hash__":38739},"blog/blog/synyx-open-source-projekte-jetzt-auf-github.md","synyx Open Source Projekte jetzt auf GitHub",[11619],{"type":11,"value":38691,"toc":38726},[38692,38695,38698,38714,38717],[14,38693,38688],{"id":38694},"synyx-open-source-projekte-jetzt-auf-github",[18,38696,38697],{},"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.",[18,38699,38700,38701,19999,38707,38713],{},"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 ",[585,38702,38706],{"href":38703,"rel":38704,"title":38705},"https://de.wikipedia.org/wiki/Abspaltung_%28Softwareentwicklung%29",[589],"Wikipedia: Forking / Abspaltung","Forks",[585,38708,38712],{"href":38709,"rel":38710,"title":38711},"http://www.heise.de/glossar/entry/Git-Pull-Request-397971.html",[589],"Heise: Pull Request","Pull Requests",", an um\nInteressierten die Möglichkeit zu bieten an unseren Projekten mitzuarbeiten.",[18,38715,38716],{},"Dank GitHub API, svn2git, Textile/Markdown in Gollum und Git war die Migration vom Redmine-basierten synyx.org zu\nGitHub relativ problemlos.",[18,38718,38719,38720,38725],{},"Wer an unseren Open Source Projekten Interesse hat, kann sich\nauf ",[585,38721,38722],{"href":38722,"rel":38723,"title":38724},"https://github.com/synyx/",[589],"GitHub synyx"," einfach mal umschauen.",{"title":48,"searchDepth":86,"depth":86,"links":38727},[],[614],"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":38688,"description":38697},"blog/synyx-open-source-projekte-jetzt-auf-github",[10976,12072,38737],"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":38741,"title":38742,"author":38743,"body":38744,"category":38996,"date":38997,"description":38998,"extension":617,"link":38999,"meta":39000,"navigation":499,"path":39001,"seo":39002,"slug":38748,"stem":39003,"tags":39004,"teaser":39009,"__hash__":39010},"blog/blog/scheduling-and-asynchronous-execution-with-spring.md","Scheduling and asynchronous execution with Spring",[8328],{"type":11,"value":38745,"toc":38990},[38746,38749,38752,38756,38759,38784,38788,38791,38794,38818,38821,38843,38846,38868,38871,38893,38896,38905,38908,38917,38921,38924,38927,38951,38954,38976,38980,38988],[14,38747,38742],{"id":38748},"scheduling-and-asynchronous-execution-with-spring",[18,38750,38751],{},"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.",[649,38753,38755],{"id":38754},"some-xml-magic","Some xml magic",[18,38757,38758],{},"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.",[43,38760,38762],{"className":1980,"code":38761,"language":1982,"meta":48,"style":48},"\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",[50,38763,38764,38769,38774,38779],{"__ignoreMap":48},[53,38765,38766],{"class":55,"line":56},[53,38767,38768],{},"\u003Cbean id=\"myClass\" class=\"my.project.path.myClass\" />\n",[53,38770,38771],{"class":55,"line":86},[53,38772,38773],{},"\u003Ctask:annotation-driven executor=\"myExecutor\" scheduler=\"myScheduler\" />\n",[53,38775,38776],{"class":55,"line":126},[53,38777,38778],{},"\u003Ctask:executor id=\"myExecutor\" pool-size=\"5\" />\n",[53,38780,38781],{"class":55,"line":163},[53,38782,38783],{},"\u003Ctask:scheduler id=\"myScheduler\" pool-size=\"10\" />\n",[649,38785,38787],{"id":38786},"the-scheduled-annotation","The @Scheduled annotation",[18,38789,38790],{},"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.",[18,38792,38793],{},"If you want periodic scheduling you can use the property fixedRate. In this example the method would be executed every\n42 seconds.",[43,38795,38797],{"className":288,"code":38796,"language":290,"meta":48,"style":48},"@Scheduled(fixedRate = 42000)\npublic void execute() {\n // do something\n}\n",[50,38798,38799,38804,38809,38814],{"__ignoreMap":48},[53,38800,38801],{"class":55,"line":56},[53,38802,38803],{},"@Scheduled(fixedRate = 42000)\n",[53,38805,38806],{"class":55,"line":86},[53,38807,38808],{},"public void execute() {\n",[53,38810,38811],{"class":55,"line":126},[53,38812,38813],{}," // do something\n",[53,38815,38816],{"class":55,"line":163},[53,38817,282],{},[18,38819,38820],{},"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.",[43,38822,38824],{"className":288,"code":38823,"language":290,"meta":48,"style":48},"@Scheduled(cron = \"*/7 * * * * *\")\npublic void execute() {\n // do something\n}\n",[50,38825,38826,38831,38835,38839],{"__ignoreMap":48},[53,38827,38828],{"class":55,"line":56},[53,38829,38830],{},"@Scheduled(cron = \"*/7 * * * * *\")\n",[53,38832,38833],{"class":55,"line":86},[53,38834,38808],{},[53,38836,38837],{"class":55,"line":126},[53,38838,38813],{},[53,38840,38841],{"class":55,"line":163},[53,38842,282],{},[18,38844,38845],{},"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.",[43,38847,38849],{"className":288,"code":38848,"language":290,"meta":48,"style":48},"@Scheduled(cron = \"0 45 9 * * MON-FRI\")\npublic void execute() {\n // do something\n}\n",[50,38850,38851,38856,38860,38864],{"__ignoreMap":48},[53,38852,38853],{"class":55,"line":56},[53,38854,38855],{},"@Scheduled(cron = \"0 45 9 * * MON-FRI\")\n",[53,38857,38858],{"class":55,"line":86},[53,38859,38808],{},[53,38861,38862],{"class":55,"line":126},[53,38863,38813],{},[53,38865,38866],{"class":55,"line":163},[53,38867,282],{},[18,38869,38870],{},"A pretty cool feature is that you even can use placeholders for your cron expression which are resolved against the\nconfigured property-placeholder.",[43,38872,38874],{"className":288,"code":38873,"language":290,"meta":48,"style":48},"@Scheduled(cron = \"${myclass.cron.execute.sth}\")\npublic void execute() {\n // do something\n}\n",[50,38875,38876,38881,38885,38889],{"__ignoreMap":48},[53,38877,38878],{"class":55,"line":56},[53,38879,38880],{},"@Scheduled(cron = \"${myclass.cron.execute.sth}\")\n",[53,38882,38883],{"class":55,"line":86},[53,38884,38808],{},[53,38886,38887],{"class":55,"line":126},[53,38888,38813],{},[53,38890,38891],{"class":55,"line":163},[53,38892,282],{},[18,38894,38895],{},"Define where the properties are loaded from within your application context:",[43,38897,38899],{"className":1980,"code":38898,"language":1982,"meta":48,"style":48},"\u003Ccontext:property-placeholder location=\"classpath:application.properties\"/>\n",[50,38900,38901],{"__ignoreMap":48},[53,38902,38903],{"class":55,"line":56},[53,38904,38898],{},[18,38906,38907],{},"Then the properties are loaded from the file which contains your cron-expressions like this:",[43,38909,38911],{"className":13667,"code":38910,"language":13669,"meta":48,"style":48},"myclass.cron.execute.sth=0 45 9 * * MON-FRI\n",[50,38912,38913],{"__ignoreMap":48},[53,38914,38915],{"class":55,"line":56},[53,38916,38910],{},[649,38918,38920],{"id":38919},"the-async-annotation","The @Async annotation",[18,38922,38923],{},"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.",[18,38925,38926],{},"This is a simple example of a @Async annotated method without a return value.",[43,38928,38930],{"className":288,"code":38929,"language":290,"meta":48,"style":48},"@Async\nvoid execute(String string) {\n // do something asynchronously\n}\n",[50,38931,38932,38937,38942,38947],{"__ignoreMap":48},[53,38933,38934],{"class":55,"line":56},[53,38935,38936],{},"@Async\n",[53,38938,38939],{"class":55,"line":86},[53,38940,38941],{},"void execute(String string) {\n",[53,38943,38944],{"class":55,"line":126},[53,38945,38946],{}," // do something asynchronously\n",[53,38948,38949],{"class":55,"line":163},[53,38950,282],{},[18,38952,38953],{},"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.",[43,38955,38957],{"className":288,"code":38956,"language":290,"meta":48,"style":48},"@Async\nFuture\u003CString> execute(String string) {\n // do something asynchronously\n}\n",[50,38958,38959,38963,38968,38972],{"__ignoreMap":48},[53,38960,38961],{"class":55,"line":56},[53,38962,38936],{},[53,38964,38965],{"class":55,"line":86},[53,38966,38967],{},"Future\u003CString> execute(String string) {\n",[53,38969,38970],{"class":55,"line":126},[53,38971,38946],{},[53,38973,38974],{"class":55,"line":163},[53,38975,282],{},[649,38977,38979],{"id":38978},"further-information","Further information",[18,38981,38982,38983],{},"Have a look at\nthe ",[585,38984,38987],{"href":38985,"rel":38986},"http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#scheduling",[589],"Spring Framework Reference Documentation",[607,38989,989],{},{"title":48,"searchDepth":86,"depth":86,"links":38991},[38992,38993,38994,38995],{"id":38754,"depth":126,"text":38755},{"id":38786,"depth":126,"text":38787},{"id":38919,"depth":126,"text":38920},{"id":38978,"depth":126,"text":38979},[5568,613],"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":38742,"description":38751},"blog/scheduling-and-asynchronous-execution-with-spring",[7037,39005,39006,39007,1010,39008],"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":39012,"title":39013,"author":39014,"body":39015,"category":39181,"date":39182,"description":39183,"extension":617,"link":39184,"meta":39185,"navigation":499,"path":39186,"seo":39187,"slug":39019,"stem":39188,"tags":39189,"teaser":39191,"__hash__":39192},"blog/blog/performance-tuning-maven-opencms-builds-using-postgresql.md","Performance tuning Maven-OpenCms builds using PostgreSQL",[12581],{"type":11,"value":39016,"toc":39179},[39017,39020,39023,39048,39051,39058,39065,39080,39098,39105,39129,39136,39160,39163,39177],[14,39018,39013],{"id":39019},"performance-tuning-maven-opencms-builds-using-postgresql",[18,39021,39022],{},"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:",[43,39024,39026],{"className":13667,"code":39025,"language":13669,"meta":48,"style":48},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 12m34.748s\nuser 2m10.132s\nsys 0m6.836s\n",[50,39027,39028,39033,39038,39043],{"__ignoreMap":48},[53,39029,39030],{"class":55,"line":56},[53,39031,39032],{},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\n",[53,39034,39035],{"class":55,"line":86},[53,39036,39037],{},"real 12m34.748s\n",[53,39039,39040],{"class":55,"line":126},[53,39041,39042],{},"user 2m10.132s\n",[53,39044,39045],{"class":55,"line":163},[53,39046,39047],{},"sys 0m6.836s\n",[18,39049,39050],{},"Uh, more than 12 minutes… definitely too much; even for an OpenCms project.",[18,39052,39053,39054],{},"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 ",[585,39055,10905],{"href":39056,"rel":39057},"http://blog.synyx.de/2011/04/maven-and-opencms/",[589],[18,39059,39060,39061,39064],{},"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 ",[573,39062,39063],{},"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.",[18,39066,39067,39068,39071,39072,39075,39076,39079],{},"One solution is to set the configuration parameter ",[27,39069,39070],{},"fsync"," to value ",[27,39073,39074],{},"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 ",[573,39077,39078],{},"data corruption"," in the event of a power failure or system crash.",[18,39081,39082,39083,39086,39087,39090,39091,39093,39094,39097],{},"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 ",[573,39084,39085],{},"logically"," completed. This can be achieved by setting the configuration parameter *\n",[573,39088,39089],{},"synchronous","commit** to value ",[27,39092,39074],{}," (file postgresql.conf). This comes at the risk of ",[573,39095,39096],{},"data loss* (but not _data\ncorruption",", as with **fsync=off**).",[18,39099,39100,39101,39104],{},"After setting ",[27,39102,39103],{},"synchronous_commit=off"," the build process is much faster:",[43,39106,39108],{"className":13667,"code":39107,"language":13669,"meta":48,"style":48},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 1m47.170s\nuser 2m9.856s\nsys 0m6.688s\n",[50,39109,39110,39114,39119,39124],{"__ignoreMap":48},[53,39111,39112],{"class":55,"line":56},[53,39113,39032],{},[53,39115,39116],{"class":55,"line":86},[53,39117,39118],{},"real 1m47.170s\n",[53,39120,39121],{"class":55,"line":126},[53,39122,39123],{},"user 2m9.856s\n",[53,39125,39126],{"class":55,"line":163},[53,39127,39128],{},"sys 0m6.688s\n",[18,39130,39131,39132,39135],{},"Applying ",[27,39133,39134],{},"fsync=off"," even saves some more seconds:",[43,39137,39139],{"className":13667,"code":39138,"language":13669,"meta":48,"style":48},"oli@rikit:~/develop/projects/foo$ time mvn clean install > mvn.log 2>&1\nreal 1m42.451s\nuser 2m8.744s\nsys 0m6.668s\n",[50,39140,39141,39145,39150,39155],{"__ignoreMap":48},[53,39142,39143],{"class":55,"line":56},[53,39144,39032],{},[53,39146,39147],{"class":55,"line":86},[53,39148,39149],{},"real 1m42.451s\n",[53,39151,39152],{"class":55,"line":126},[53,39153,39154],{},"user 2m8.744s\n",[53,39156,39157],{"class":55,"line":163},[53,39158,39159],{},"sys 0m6.668s\n",[18,39161,39162],{},"For more details on PostgreSQLs WAL mechanism and configuration options have a look at the PostgreSQL documentation:",[577,39164,39165,39171],{},[580,39166,39167],{},[585,39168,39169],{"href":39169,"rel":39170,"title":39169},"http://www.postgresql.org/docs/9.1/static/wal.html",[589],[580,39172,39173],{},[585,39174,39175],{"href":39175,"rel":39176,"title":39175},"http://www.postgresql.org/docs/9.1/static/runtime-config-wal.html",[589],[607,39178,989],{},{"title":48,"searchDepth":86,"depth":86,"links":39180},[],[613],"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":39013,"description":39022},"blog/performance-tuning-maven-opencms-builds-using-postgresql",[39190,10977,37693,1009],"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…","l4LWFO3E0lbS9SWeYBTRbyMTIWzb4fRcVVSyBnBgziM",{"id":39194,"title":39195,"author":39196,"body":39197,"category":39847,"date":39848,"description":39849,"extension":617,"link":39850,"meta":39851,"navigation":499,"path":39852,"seo":39853,"slug":39201,"stem":39854,"tags":39855,"teaser":39861,"__hash__":39862},"blog/blog/how-to-monitor-and-manage-your-java-application-with-jmx.md","How to monitor and manage your Java application with JMX",[8328],{"type":11,"value":39198,"toc":39840},[39199,39202,39205,39220,39295,39298,39302,39305,39395,39398,39405,39409,39423,39428,39433,39456,39459,39464,39469,39493,39496,39518,39523,39528,39552,39558,39597,39606,39610,39613,39618,39621,39627,39632,39635,39641,39644,39647,39681,39684,39734,39738,39741,39744,39804,39807,39811,39817,39824,39831,39838],[14,39200,39195],{"id":39201},"how-to-monitor-and-manage-your-java-application-with-jmx",[18,39203,39204],{},"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).",[18,39206,39207,39208,39211,39212,39215,39216,39219],{},"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 ",[573,39209,39210],{},"waiting",". Then an authorized person (\nthe boss) has to decide about this application. It may be set to ",[573,39213,39214],{},"allowed"," or to ",[573,39217,39218],{},"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.",[43,39221,39223],{"className":288,"code":39222,"language":290,"meta":48,"style":48},"public 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",[50,39224,39225,39230,39235,39240,39245,39249,39254,39259,39263,39268,39273,39277,39282,39287,39291],{"__ignoreMap":48},[53,39226,39227],{"class":55,"line":56},[53,39228,39229],{},"public class JmxDemo {\n",[53,39231,39232],{"class":55,"line":86},[53,39233,39234],{}," private long numberOfWaitingApplications;\n",[53,39236,39237],{"class":55,"line":126},[53,39238,39239],{}," public long getNumberOfWaitingApplications() {\n",[53,39241,39242],{"class":55,"line":163},[53,39243,39244],{}," return numberOfWaitingApplications;\n",[53,39246,39247],{"class":55,"line":186},[53,39248,860],{},[53,39250,39251],{"class":55,"line":221},[53,39252,39253],{}," public long countApplicationsInStatus(String status) {\n",[53,39255,39256],{"class":55,"line":242},[53,39257,39258],{}," // do something and return number of applications with the given status\n",[53,39260,39261],{"class":55,"line":273},[53,39262,860],{},[53,39264,39265],{"class":55,"line":279},[53,39266,39267],{}," public List\u003CString> showWaitingApplications() {\n",[53,39269,39270],{"class":55,"line":496},[53,39271,39272],{}," // do something and return a list of all waiting applications\n",[53,39274,39275],{"class":55,"line":503},[53,39276,860],{},[53,39278,39279],{"class":55,"line":509},[53,39280,39281],{}," public String remindBossAboutWaitingApplications() {\n",[53,39283,39284],{"class":55,"line":515},[53,39285,39286],{}," // remind the boss via email to decide about the waiting applications\n",[53,39288,39289],{"class":55,"line":521},[53,39290,860],{},[53,39292,39293],{"class":55,"line":527},[53,39294,282],{},[18,39296,39297],{},"If you want to use this class as a MBean, a few steps are necessary.",[649,39299,39301],{"id":39300},"_1-not-yet-another-xml-file","1. Not yet another xml file…",[18,39303,39304],{},"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.",[43,39306,39308],{"className":1980,"code":39307,"language":1982,"meta":48,"style":48},"\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",[50,39309,39310,39314,39319,39324,39329,39334,39339,39344,39349,39354,39359,39363,39368,39373,39378,39382,39387,39391],{"__ignoreMap":48},[53,39311,39312],{"class":55,"line":56},[53,39313,500],{"emptyLinePlaceholder":499},[53,39315,39316],{"class":55,"line":86},[53,39317,39318],{},"\u003Cbean id=\"jmxDemo\" class=\"org.synyx.urlaubsverwaltung.jmx.JmxDemo\">\n",[53,39320,39321],{"class":55,"line":126},[53,39322,39323],{}," \u003C!-- maybe you need contructor-injection -->\n",[53,39325,39326],{"class":55,"line":163},[53,39327,39328],{}," \u003C!-- \u003Cconstructor-arg ref=\"myService\" /> -->\n",[53,39330,39331],{"class":55,"line":186},[53,39332,39333],{},"\u003C/bean>\n",[53,39335,39336],{"class":55,"line":221},[53,39337,39338],{}," \u003C!-- you may just copy the following lines -->\n",[53,39340,39341],{"class":55,"line":242},[53,39342,39343],{},"\u003Cbean id=\"exporter\" class=\"org.springframework.jmx.export.MBeanExporter\" lazy-init=\"false\">\n",[53,39345,39346],{"class":55,"line":273},[53,39347,39348],{},"\u003Cproperty name=\"autodetect\" value=\"true\"/>\n",[53,39350,39351],{"class":55,"line":279},[53,39352,39353],{},"\u003Cproperty name=\"namingStrategy\" ref=\"namingStrategy\"/>\n",[53,39355,39356],{"class":55,"line":496},[53,39357,39358],{},"\u003Cproperty name=\"assembler\" ref=\"assembler\"/>\n",[53,39360,39361],{"class":55,"line":503},[53,39362,39333],{},[53,39364,39365],{"class":55,"line":509},[53,39366,39367],{},"\u003Cbean id=\"jmxAttributeSource\" class=\"org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource\"/>\n",[53,39369,39370],{"class":55,"line":515},[53,39371,39372],{},"\u003Cbean id=\"assembler\" class=\"org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler\">\n",[53,39374,39375],{"class":55,"line":521},[53,39376,39377],{},"\u003Cproperty name=\"attributeSource\" ref=\"jmxAttributeSource\"/>\n",[53,39379,39380],{"class":55,"line":527},[53,39381,39333],{},[53,39383,39384],{"class":55,"line":533},[53,39385,39386],{},"\u003Cbean id=\"namingStrategy\" class=\"org.springframework.jmx.export.naming.MetadataNamingStrategy\">\n",[53,39388,39389],{"class":55,"line":539},[53,39390,39377],{},[53,39392,39393],{"class":55,"line":545},[53,39394,39333],{},[18,39396,39397],{},"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.",[18,39399,39400,39401,39404],{},"Setting MBeanExporter’s property ",[573,39402,39403],{},"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.",[649,39406,39408],{"id":39407},"_2-lets-transform-your-spring-bean-to-a-managed-bean","2. Let’s transform your Spring Bean to a Managed Bean!",[18,39410,39411,39412,39415,39416,39419,39420,986],{},"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",[573,39413,39414],{},"@ManagedResource",", Attributes are annotated with ",[573,39417,39418],{},"@ManagedAttribute"," and Methods with ",[573,39421,39422],{},"@ManagedOperation",[18,39424,39425],{},[27,39426,39427],{},"2.1 Bean",[18,39429,39430,39431,986],{},"Mark your Bean with ",[573,39432,39414],{},[43,39434,39436],{"className":288,"code":39435,"language":290,"meta":48,"style":48},"@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"My managed Bean.\")\npublic class JmxDemo {\n // lot of stuff\n}\n",[50,39437,39438,39443,39447,39452],{"__ignoreMap":48},[53,39439,39440],{"class":55,"line":56},[53,39441,39442],{},"@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"My managed Bean.\")\n",[53,39444,39445],{"class":55,"line":86},[53,39446,39229],{},[53,39448,39449],{"class":55,"line":126},[53,39450,39451],{}," // lot of stuff\n",[53,39453,39454],{"class":55,"line":163},[53,39455,282],{},[18,39457,39458],{},"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.",[18,39460,39461],{},[27,39462,39463],{},"2.2 Attributes",[18,39465,39466,39467,986],{},"Annotate the Getter and Setter with ",[573,39468,39418],{},[43,39470,39472],{"className":288,"code":39471,"language":290,"meta":48,"style":48},"@ManagedAttribute(description = \"Get the number of all waiting applications\" )\npublic long getNumberOfWaitingApplications() {\n return numberOfWaitingApplications;\n}\n",[50,39473,39474,39479,39484,39489],{"__ignoreMap":48},[53,39475,39476],{"class":55,"line":56},[53,39477,39478],{},"@ManagedAttribute(description = \"Get the number of all waiting applications\" )\n",[53,39480,39481],{"class":55,"line":86},[53,39482,39483],{},"public long getNumberOfWaitingApplications() {\n",[53,39485,39486],{"class":55,"line":126},[53,39487,39488],{}," return numberOfWaitingApplications;\n",[53,39490,39491],{"class":55,"line":163},[53,39492,282],{},[18,39494,39495],{},"Exposing attributes may be:",[577,39497,39498,39501,39504,39507,39510,39513,39515],{},[580,39499,39500],{},"Basic types",[580,39502,39503],{},"Primitives and their wrappers",[580,39505,39506],{},"String",[580,39508,39509],{},"BigDecimal",[580,39511,39512],{},"BigInteger",[580,39514,37246],{},[580,39516,39517],{},"Arrays and collections of basic types",[18,39519,39520],{},[27,39521,39522],{},"2.2 Methods",[18,39524,39525,39526,986],{},"Annotate each method you wish to expose with ",[573,39527,39422],{},[43,39529,39531],{"className":288,"code":39530,"language":290,"meta":48,"style":48},"@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",[50,39532,39533,39538,39543,39548],{"__ignoreMap":48},[53,39534,39535],{"class":55,"line":56},[53,39536,39537],{},"@ManagedOperation(description = \"Shows a list of all waiting applications with some information.\")\n",[53,39539,39540],{"class":55,"line":86},[53,39541,39542],{},"public List\u003CString> showWaitingApplications() {\n",[53,39544,39545],{"class":55,"line":126},[53,39546,39547],{}," // do something and return a list of all waiting applications\n",[53,39549,39550],{"class":55,"line":163},[53,39551,282],{},[18,39553,39554,39555,986],{},"If your methods have parameters you can describe them further with ",[573,39556,39557],{},"@ManagedOperationParameters",[43,39559,39561],{"className":288,"code":39560,"language":290,"meta":48,"style":48},"@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",[50,39562,39563,39568,39573,39578,39583,39588,39593],{"__ignoreMap":48},[53,39564,39565],{"class":55,"line":56},[53,39566,39567],{},"@ManagedOperation(description = \"Get the number of all applications that have the given status.\")\n",[53,39569,39570],{"class":55,"line":86},[53,39571,39572],{},"@ManagedOperationParameters({\n",[53,39574,39575],{"class":55,"line":126},[53,39576,39577],{}," @ManagedOperationParameter(name = \"status\", description = \"The status may be waiting, allowed, rejected or cancelled.\")\n",[53,39579,39580],{"class":55,"line":163},[53,39581,39582],{},"})\n",[53,39584,39585],{"class":55,"line":186},[53,39586,39587],{},"public long countApplicationsInStatus(String state) {\n",[53,39589,39590],{"class":55,"line":221},[53,39591,39592],{}," // do something and return number of applications with the given status\n",[53,39594,39595],{"class":55,"line":242},[53,39596,282],{},[18,39598,39599,39600,39602,39603,39605],{},"Make sure to annotate your Getter/Setter with ",[573,39601,39418],{}," and not with ",[573,39604,39422],{},". Otherwise your\nmethods won’t work.",[649,39607,39609],{"id":39608},"_3-try-it","3. Try it!",[18,39611,39612],{},"You can now use the functions of your MBean either with JConsole or with other tools. (e.g. JMinix)",[18,39614,39615],{},[27,39616,39617],{},"3.1 JConsole",[18,39619,39620],{},"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.",[18,39622,39623],{},[1773,39624],{"alt":39625,"src":39626},"\"jconsole\"","https://media.synyx.de/uploads//2012/04/jconsole.png",[18,39628,39629],{},[27,39630,39631],{},"3.2 JMinix",[18,39633,39634],{},"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.",[18,39636,39637],{},[1773,39638],{"alt":39639,"src":39640},"\"JMinix\"","https://media.synyx.de/uploads//2012/04/jminix.png",[18,39642,39643],{},"You can include it easily in your Maven based web application:",[18,39645,39646],{},"Add JMinix as dependency in your pom.xml",[43,39648,39650],{"className":1980,"code":39649,"language":1982,"meta":48,"style":48},"\n\u003Cdependency>\n \u003CgroupId>org.jminix\u003C/groupId>\n \u003CartifactId>jminix\u003C/artifactId>\n \u003Cversion>1.0.0\u003C/version>\n\u003C/dependency>\n",[50,39651,39652,39656,39661,39666,39671,39676],{"__ignoreMap":48},[53,39653,39654],{"class":55,"line":56},[53,39655,500],{"emptyLinePlaceholder":499},[53,39657,39658],{"class":55,"line":86},[53,39659,39660],{},"\u003Cdependency>\n",[53,39662,39663],{"class":55,"line":126},[53,39664,39665],{}," \u003CgroupId>org.jminix\u003C/groupId>\n",[53,39667,39668],{"class":55,"line":163},[53,39669,39670],{}," \u003CartifactId>jminix\u003C/artifactId>\n",[53,39672,39673],{"class":55,"line":186},[53,39674,39675],{}," \u003Cversion>1.0.0\u003C/version>\n",[53,39677,39678],{"class":55,"line":221},[53,39679,39680],{},"\u003C/dependency>\n",[18,39682,39683],{},"JMinix uses a simple HttpServlet that you have to register and map to an url-pattern in your web.xml",[43,39685,39687],{"className":1980,"code":39686,"language":1982,"meta":48,"style":48},"\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",[50,39688,39689,39694,39699,39704,39709,39714,39719,39724,39729],{"__ignoreMap":48},[53,39690,39691],{"class":55,"line":56},[53,39692,39693],{},"\u003C!-- JMX -->\n",[53,39695,39696],{"class":55,"line":86},[53,39697,39698],{},"\u003Cservlet>\n",[53,39700,39701],{"class":55,"line":126},[53,39702,39703],{}," \u003Cservlet-name>JmxMiniConsoleServlet\u003C/servlet-name>\n",[53,39705,39706],{"class":55,"line":163},[53,39707,39708],{}," \u003Cservlet-class>org.jminix.console.servlet.MiniConsoleServlet\u003C/servlet-class>\n",[53,39710,39711],{"class":55,"line":186},[53,39712,39713],{},"\u003C/servlet>\n",[53,39715,39716],{"class":55,"line":221},[53,39717,39718],{},"\u003Cservlet-mapping>\n",[53,39720,39721],{"class":55,"line":242},[53,39722,39723],{},"\u003Cservlet-name>JmxMiniConsoleServlet\u003C/servlet-name>\n",[53,39725,39726],{"class":55,"line":273},[53,39727,39728],{},"\u003Curl-pattern>/jmx/*\u003C/url-pattern>\n",[53,39730,39731],{"class":55,"line":279},[53,39732,39733],{},"\u003C/servlet-mapping>\n",[649,39735,39737],{"id":39736},"_4-notifications","4. Notifications",[18,39739,39740],{},"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.",[18,39742,39743],{},"Example: You want to be notified if a user logs in.",[43,39745,39747],{"className":288,"code":39746,"language":290,"meta":48,"style":48},"@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",[50,39748,39749,39754,39759,39763,39768,39773,39778,39782,39786,39791,39796,39800],{"__ignoreMap":48},[53,39750,39751],{"class":55,"line":56},[53,39752,39753],{},"@ManagedResource(objectName = \"mbeans:name=myJmxDemoBean\", description = \"Manage some 'Urlaubsverwaltung' problems.\")\n",[53,39755,39756],{"class":55,"line":86},[53,39757,39758],{},"public class JmxDemoReady implements NotificationPublisherAware {\n",[53,39760,39761],{"class":55,"line":126},[53,39762,39451],{},[53,39764,39765],{"class":55,"line":163},[53,39766,39767],{}," private NotificationPublisher notificationPublisher;\n",[53,39769,39770],{"class":55,"line":186},[53,39771,39772],{}," public void notifyAboutLogin(String msg) {\n",[53,39774,39775],{"class":55,"line":221},[53,39776,39777],{}," notificationPublisher.sendNotification(new Notification(\"Login Action\", this, 0, msg));\n",[53,39779,39780],{"class":55,"line":242},[53,39781,860],{},[53,39783,39784],{"class":55,"line":273},[53,39785,13033],{},[53,39787,39788],{"class":55,"line":279},[53,39789,39790],{}," public void setNotificationPublisher(NotificationPublisher notificationPublisher) {\n",[53,39792,39793],{"class":55,"line":496},[53,39794,39795],{}," this.notificationPublisher = notificationPublisher;\n",[53,39797,39798],{"class":55,"line":503},[53,39799,860],{},[53,39801,39802],{"class":55,"line":509},[53,39803,282],{},[18,39805,39806],{},"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.",[649,39808,39810],{"id":39809},"_5-further-information","5. Further information:",[18,39812,39813],{},[585,39814,38987],{"href":39815,"rel":39816},"http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/jmx.html",[589],[18,39818,39819],{},[585,39820,39823],{"href":39821,"rel":39822},"http://docs.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html",[589],"About JConsole",[18,39825,39826],{},[585,39827,39830],{"href":39828,"rel":39829},"http://code.google.com/p/jminix/",[589],"About JMinix",[18,39832,39833],{},[585,39834,39837],{"href":39835,"rel":39836},"http://blog.synyx.de/2011/11/elektronische-urlaubsverwaltung-made-by-youngsters",[589],"About the vacation management web application",[607,39839,989],{},{"title":48,"searchDepth":86,"depth":86,"links":39841},[39842,39843,39844,39845,39846],{"id":39300,"depth":126,"text":39301},{"id":39407,"depth":126,"text":39408},{"id":39608,"depth":126,"text":39609},{"id":39736,"depth":126,"text":39737},{"id":39809,"depth":126,"text":39810},[5568,613],"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":39195,"description":39204},"blog/how-to-monitor-and-manage-your-java-application-with-jmx",[39856,39857,39858,34322,39859,39860,18102,1010],"annotation","jconsole","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…","Lf-pqL8fxMUtFEesPQZ2fYbpNoUAqH8DOQaGCkCDsBA",{"id":39864,"title":39865,"author":39866,"body":39867,"category":39968,"date":39969,"description":39970,"extension":617,"link":39971,"meta":39972,"navigation":499,"path":39973,"seo":39974,"slug":39871,"stem":39975,"tags":39976,"teaser":39977,"__hash__":39978},"blog/blog/my-take-on-things-java-community-events-vs-java-conferences.md","My take on things – Java Community events vs. Java Conferences",[26052],{"type":11,"value":39868,"toc":39966},[39869,39872,39875,39880,39883,39888,39893,39900,39903,39908,39913,39916,39919,39923,39928,39931,39934,39939,39942,39945,39950,39953,39956,39959,39963],[14,39870,39865],{"id":39871},"my-take-on-things-java-community-events-vs-java-conferences",[18,39873,39874],{},"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.",[18,39876,39877],{},[27,39878,39879],{},"Organisation:",[18,39881,39882],{},"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.",[18,39884,39885],{},[573,39886,39887],{},"+1 für die Devoxx",[18,39889,39890],{},[27,39891,39892],{},"Verpflegung:",[18,39894,39895,39896,39899],{},"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 ",[573,39897,39898],{},"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 🙂",[18,39901,39902],{},"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 🙂",[18,39904,39905],{},[573,39906,39907],{},"+1 für die JAX",[18,39909,39910],{},[27,39911,39912],{},"Technische Ausstattung:",[18,39914,39915],{},"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.",[18,39917,39918],{},"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).",[18,39920,39921],{},[573,39922,39887],{},[18,39924,39925],{},[27,39926,39927],{},"Anspruch Sessions, Workshops:",[18,39929,39930],{},"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.",[18,39932,39933],{},"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.",[18,39935,39936],{},[27,39937,39938],{},"Speaker:",[18,39940,39941],{},"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.",[18,39943,39944],{},"Ich mag hier keinen Punkt für die eine oder andere Seite geben.",[18,39946,39947],{},[27,39948,39949],{},"Umgebung:",[18,39951,39952],{},"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.",[18,39954,39955],{},"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.",[18,39957,39958],{},"Dennoch gebe ich aufgrund meiner persönlichen Situation hier den Punkt an die JAX.",[18,39960,39961],{},[573,39962,39907],{},[18,39964,39965],{},"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":48,"searchDepth":86,"depth":86,"links":39967},[],[614],"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":39865,"description":39874},"blog/my-take-on-things-java-community-events-vs-java-conferences",[3059,3491,19739,290,4231,6503],"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":39980,"title":39981,"author":39982,"body":39983,"category":40193,"date":40194,"description":48,"extension":617,"link":40195,"meta":40196,"navigation":499,"path":40197,"seo":40198,"slug":39987,"stem":40199,"tags":40200,"teaser":40201,"__hash__":40202},"blog/blog/konferenz-logbuch-bedcon-2012.md","Konferenz-Logbuch BedCon 2012",[11420],{"type":11,"value":39984,"toc":40186},[39985,39988,39992,40001,40005,40008,40011,40014,40017,40026,40029,40032,40036,40039,40046,40049,40052,40055,40058,40067,40070,40079,40082,40085,40088,40091,40094,40097,40100,40103,40112,40121,40124,40128,40131,40134,40137,40140,40143,40150,40153,40156,40159,40167,40176,40179,40182],[14,39986,39981],{"id":39987},"konferenz-logbuch-bedcon-2012",[649,39989,39991],{"id":39990},"dienstag","Dienstag",[18,39993,39994,39995,40000],{},"15:32 Rebecca gibt mir die Unterlagen, Tickets und Infos zur ",[585,39996,39999],{"href":39997,"rel":39998},"http://bed-con.org",[589],"BedCon",". Perfekt organisiert – sie hat\nsich bereits um alles gekümmert.",[649,40002,40004],{"id":40003},"mittwoch-anreise","Mittwoch – Anreise",[18,40006,40007],{},"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.",[18,40009,40010],{},"17:00 Wir sind im Zug und es geht los. Wir öffnen ein Bier – eine Stunde vor der offiziellen synyx Bier-Time.",[18,40012,40013],{},"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.",[18,40015,40016],{},"21:07 Unser Reiseproviant ist leer, wir siedeln um ins Bordbistro.",[18,40018,40019,40020,40025],{},"22:10 Markus’ intensive Recherche zahlt sich aus, denn er navigiert uns astrein zum Hotel. Das Zimmer\nbzw. ",[585,40021,40024],{"href":40022,"rel":40023},"http://www.residenz-2000.de/",[589],"das Hotel"," allgemein ist super.",[18,40027,40028],{},"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.",[18,40030,40031],{},"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.",[649,40033,40035],{"id":40034},"donnerstag-erster-konferenztag","Donnerstag – Erster Konferenztag",[18,40037,40038],{},"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.",[18,40040,40041,40042,40045],{},"8:40 Wir kommen nach einer kurzen Suche im Informatikerbau der ",[585,40043,19047],{"href":19045,"rel":40044},[589]," an\nund holen unsere Badges ab.",[18,40047,40048],{},"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.",[18,40050,40051],{},"9:45 Der Talk ist zu Ende, es gibt recht spannende Fragen und Diskussionen zum Thema Testen, TDD und Methodik.",[18,40053,40054],{},"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.",[18,40056,40057],{},"11:24 Modularisierung wagen – Recht witzig gemachte Two Man Show über die Modularisierung von monolitischen Builds,\nquasi ein Erfahrungsbericht.",[18,40059,40060,40061,40066],{},"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 ",[585,40062,40065],{"href":40063,"rel":40064},"http://maven.apache.org/plugins/maven-enforcer-plugin/",[589],"Maven Enforcer Plugin"," einzusetzen.",[18,40068,40069],{},"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.",[18,40071,40072,40073,40078],{},"13:42 Ich werde an ",[585,40074,40077],{"href":40075,"rel":40076},"http://www.hello2morrow.com/products/sonargraph",[589],"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.",[18,40080,40081],{},"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.",[18,40083,40084],{},"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).",[18,40086,40087],{},"15:37 Auf dem Gang verwickle ich mich in Gespräche über die Arbeitsweise in anderen Firmen und freue mich, bei synyx zu\narbeiten.",[18,40089,40090],{},"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.",[18,40092,40093],{},"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.",[18,40095,40096],{},"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.",[18,40098,40099],{},"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.",[18,40101,40102],{},"19:00 Treffen in der Lobby des Hotels zum Abmarsch nach Kreuzberg zur Nahrnugsaufnahme.",[18,40104,40105,40106,40111],{},"20:12 Wir landen in einem Laden namens “",[585,40107,40110],{"href":40108,"rel":40109},"http://www.knofi.de/",[589],"Knofi","“. Es gibt leckeres orientalisches Essen. Alle sind\nbegeistert, nur es war kaum Knoblauch drin.",[18,40113,40114,40115,40120],{},"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 ",[585,40116,40119],{"href":40117,"rel":40118},"http://franken-bar.de/",[589],"Franken",". Wir trinken Bier und Cuba Libre.",[18,40122,40123],{},"2:34 Wir kommen mit dem Taxi nach Hause und ich falle totmüde ins Bett.",[649,40125,40127],{"id":40126},"freitag-zweiter-konferenztag","Freitag – Zweiter Konferenztag",[18,40129,40130],{},"6:45 zzzZzzZz grmpf zzZzz argh grml. Aufstehen, duschen, Frühstück, packen. Hmpf.",[18,40132,40133],{},"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.",[18,40135,40136],{},"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.",[18,40138,40139],{},"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.",[18,40141,40142],{},"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.",[18,40144,40145,40146,40149],{},"11:20 Wir bekommen einen groben Überblick über ",[585,40147,29172],{"href":29170,"rel":40148},[589]," 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.",[18,40151,40152],{},"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.",[18,40154,40155],{},"13:18 Der JPA-Vortrag ist dermaßen voll dass ich zum CoffeeScript-Talk gehe: Warum Kaffee gut für Entwickler ist.",[18,40157,40158],{},"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.",[18,40160,40161,40162,40166],{},"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 ",[585,40163,27689],{"href":40164,"rel":40165},"http://coffeescript.org/",[589],". Die setzen wir nach dem Talk auf dem Gang noch etwas fort.",[18,40168,40169,40170,40175],{},"14:30 Schemaevolution in einer ",[585,40171,40174],{"href":40172,"rel":40173},"http://www.mongodb.org/",[589],"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.",[18,40177,40178],{},"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.",[18,40180,40181],{},"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.",[649,40183,40185],{"id":40184},"eindrücke","Eindrücke",{"title":48,"searchDepth":86,"depth":86,"links":40187},[40188,40189,40190,40191,40192],{"id":39990,"depth":126,"text":39991},{"id":40003,"depth":126,"text":40004},{"id":40034,"depth":126,"text":40035},{"id":40126,"depth":126,"text":40127},{"id":40184,"depth":126,"text":40185},[614],"2012-04-02T13:40:16","https://synyx.de/blog/konferenz-logbuch-bedcon-2012/",{},"/blog/konferenz-logbuch-bedcon-2012",{"title":39981,"description":48},"blog/konferenz-logbuch-bedcon-2012",[8320,3491,4230,4231],"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":40204,"title":40205,"author":40206,"body":40207,"category":40220,"date":40221,"description":48,"extension":617,"link":40222,"meta":40223,"navigation":499,"path":40224,"seo":40225,"slug":40226,"stem":40227,"tags":40228,"teaser":40230,"__hash__":40231},"blog/blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben.md","Unsere Azubis erzählen wie sie ihre Ausbildung bei synyx erleben",[11619],{"type":11,"value":40208,"toc":40218},[40209,40212],[14,40210,40205],{"id":40211},"unsere-azubis-erzählen-wie-sie-ihre-ausbildung-bei-synyx-erleben",[18,40213,40214],{},[1773,40215],{"alt":48,"src":40216,"title":40217},"https://media.synyx.de/uploads//2012/03/aljona_matze.jpg","aljona_matze",{"title":48,"searchDepth":86,"depth":86,"links":40219},[],[5568],"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":40205,"description":48},"unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben","blog/unsere-azubis-erzahlen-wie-sie-ihre-ausbildung-bei-synyx-erleben",[5579,40229],"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":40233,"title":40234,"author":40235,"body":40237,"category":40388,"date":40389,"description":40390,"extension":617,"link":40391,"meta":40392,"navigation":499,"path":40393,"seo":40394,"slug":40241,"stem":40395,"tags":40396,"teaser":40401,"__hash__":40402},"blog/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git.md","New Homepage with nanoc, Twitter Bootstrap, LESS and Git",[40236],"buch",{"type":11,"value":40238,"toc":40379},[40239,40242,40245,40253,40256,40260,40263,40272,40275,40279,40298,40302,40305,40309,40318,40322,40331,40335,40338,40347,40350,40353,40362,40365,40374,40377],[14,40240,40234],{"id":40241},"new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[18,40243,40244],{},"With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic\ndecisions filling our needs.",[18,40246,40247,40248,40252],{},"Our previous websites were always implemented in OpenCms since we are ",[585,40249,40251],{"href":5449,"rel":40250},[589],"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?",[18,40254,40255],{},"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.",[2352,40257,40259],{"id":40258},"finding-the-right-tool-for-our-needs","Finding the right tool for our needs",[18,40261,40262],{},"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.",[18,40264,40265,40266,40271],{},"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 ",[585,40267,40270],{"href":40268,"rel":40269},"http://nanoc.stoneship.org/",[589],"nanoc"," which is simple to set-up, supports various\ntemplating formats, is very flexible, has good documentation and is easy to use.",[18,40273,40274],{},"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.",[2352,40276,40278],{"id":40277},"templating","Templating",[18,40280,40281,40282,99,40287,3038,40292,40297],{},"For templating we\nuse ",[585,40283,40286],{"href":40284,"rel":40285},"https://ruby-doc.org/stdlib/libdoc/erb/rdoc/ERB.html",[589],"“ERB”",[585,40288,40291],{"href":40289,"rel":40290},"https://getbootstrap.com/",[589],"“Twitter Bootstrap”",[585,40293,40296],{"href":40294,"rel":40295},"https://lesscss.org",[589],"“LESS”"," for CSS. Nanoc supports precompiling LESS and compressing CSS.",[649,40299,40301],{"id":40300},"erb","ERB",[18,40303,40304],{},"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.",[649,40306,40308],{"id":40307},"less","LESS",[18,40310,40311,40312,40317],{},"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 ",[585,40313,40316],{"href":40314,"rel":40315},"http://sass-lang.com/",[589],"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.",[649,40319,40321],{"id":40320},"twitter-bootstrap","Twitter Bootstrap",[18,40323,40324,40325,40330],{},"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 ",[585,40326,40329],{"href":40327,"rel":40328},"http://960.gs/",[589],"960 Grid System"," is the use of LESS in Twitter Bootstrap, so for example the\ngrid columns can be altered by changing variables.",[2352,40332,40334],{"id":40333},"deployment-and-publishing","Deployment and Publishing",[18,40336,40337],{},"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:",[43,40339,40341],{"className":13667,"code":40340,"language":13669,"meta":48,"style":48},"rake deploy:post_receive\n",[50,40342,40343],{"__ignoreMap":48},[53,40344,40345],{"class":55,"line":56},[53,40346,40340],{},[18,40348,40349],{},"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.",[18,40351,40352],{},"So adding new content or changing some styling is as easy as doing that locally, commiting and publishing by",[43,40354,40356],{"className":13667,"code":40355,"language":13669,"meta":48,"style":48},"git push stage\n",[50,40357,40358],{"__ignoreMap":48},[53,40359,40360],{"class":55,"line":56},[53,40361,40355],{},[18,40363,40364],{},"or",[43,40366,40368],{"className":13667,"code":40367,"language":13669,"meta":48,"style":48},"git push live\n",[50,40369,40370],{"__ignoreMap":48},[53,40371,40372],{"class":55,"line":56},[53,40373,40367],{},[18,40375,40376],{},"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.",[607,40378,989],{},{"title":48,"searchDepth":86,"depth":86,"links":40380},[40381,40382,40387],{"id":40258,"depth":86,"text":40259},{"id":40277,"depth":86,"text":40278,"children":40383},[40384,40385,40386],{"id":40300,"depth":126,"text":40301},{"id":40307,"depth":126,"text":40308},{"id":40320,"depth":126,"text":40321},{"id":40333,"depth":86,"text":40334},[613],"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":40234,"description":40244},"blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[40397,10147,40398,40399,40307,40270,12072,37693,25086,40400,5743],"cms","git","homepage","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":40404,"title":40405,"author":40406,"body":40408,"category":40737,"date":40738,"description":40739,"extension":617,"link":40740,"meta":40741,"navigation":499,"path":40742,"seo":40743,"slug":40412,"stem":40744,"tags":40745,"teaser":40746,"__hash__":40747},"blog/blog/developers-developers-developers.md","Developers, Developers, Developers!",[40407],"speaker-schalanda",{"type":11,"value":40409,"toc":40732},[40410,40413,40416,40435,40443,40460,40466,40515,40521,40524,40528,40542,40548,40569,40574,40582,40585,40589,40598,40604,40619,40634,40649,40664,40673,40694,40709,40713,40722,40725],[14,40411,40405],{"id":40412},"developers-developers-developers",[18,40414,40415],{},"Belgium, November 2011. More than three and a half thousand people are surging into Antwerp’s megaplex, the Kinepolis.\nIt’s Devoxx-time!",[18,40417,40418,25844,40423,40428,40429,40434],{},[585,40419,40422],{"href":40420,"rel":40421},"http://vimeo.com/32170355",[589],"Devoxx 2011",[585,40424,40427],{"href":40425,"rel":40426},"http://vimeo.com/royvanrijn",[589],"Roy van Rijn"," on ",[585,40430,40433],{"href":40431,"rel":40432},"http://vimeo.com",[589],"Vimeo"," (\nMit dem Laden des Videos akzeptieren Sie die Datenschutzerklärung von Vimeo).",[18,40436,40437,40438,986],{},"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 ",[585,40439,40442],{"href":40440,"rel":40441},"http://blog.synyx.de/2011/11/reasons-why-i-go-to-devoxx/",[589],"Reasons why I go to Devoxx",[577,40444,40445,40448,40451],{},[580,40446,40447],{},"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.",[580,40449,40450],{},"Great diversity in intelligent and friendly attendees, which also showed on the ad-hoc polls on the whiteboards\nprovided in the hallway.",[580,40452,40453,40454,40459],{},"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 ",[585,40455,40458],{"href":40456,"rel":40457},"https://web.archive.org/web/20171113063602/http://wall.devoxx.com:80/",[589],"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.",[18,40461,40462],{},[1773,40463],{"alt":40464,"src":40465},"\"Devoxx 2011 - Twitter wall\"","https://media.synyx.de/uploads//2011/11/devoxx11_twitterwall.jpg",[577,40467,40468,40506,40509,40512],{},[580,40469,40470,40471,99,40476,99,40480,99,40485,99,40490,99,40495,23969,40500,40505],{},"Large spectrum of (Java- and JVM-related) topics with great diversity. There were a lot of talks about alternative\nlanguages on the JVM\nlike ",[585,40472,40475],{"href":40473,"rel":40474},"http://www.scala-lang.org/",[589],"Scala",[585,40477,40479],{"href":25281,"rel":40478},[589],"JRuby",[585,40481,40484],{"href":40482,"rel":40483},"http://clojure.org/",[589],"Clojure",[585,40486,40489],{"href":40487,"rel":40488},"https://groovy-lang.org/",[589],"Groovy",[585,40491,40494],{"href":40492,"rel":40493},"http://confluence.jetbrains.net/display/Kotlin/Welcome",[589],"Kotlin",[585,40496,40499],{"href":40497,"rel":40498},"http://fantom.org/",[589],"Fantom",[585,40501,40504],{"href":40502,"rel":40503},"http://ceylon-lang.org/",[589],"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.",[580,40507,40508],{},"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.",[580,40510,40511],{},"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.",[580,40513,40514],{},"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.",[18,40516,40517],{},[1773,40518],{"alt":40519,"src":40520},"\"Devoxx 2011 - Big screen\"","https://media.synyx.de/uploads//2011/11/devoxx11_screen1.jpg",[18,40522,40523],{},"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.",[2352,40525,40527],{"id":40526},"university-days","University days",[18,40529,10933,40530,40535,40536,40541],{},[585,40531,40534],{"href":40532,"rel":40533},"http://devoxx.com/display/DV11/Continuous+Delivery",[589],"Continuous Delivery"," by David Farley was really good. He\nrecapped some things I already knew from reading the book he co-authored (see\nmy ",[585,40537,40540],{"href":40538,"rel":40539},"http://blog.synyx.de/2011/08/continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline/",[589],"Review of Continuous Delivery",")\nbut also told a lot about how they implemented continuous delivery at LMAX.",[18,40543,40544],{},[1773,40545],{"alt":40546,"src":40547},"\"Devoxx 2011 - The well-grounded Java developer\"","https://media.synyx.de/uploads//2011/11/devoxx11_wellgrounded2.jpg",[18,40549,40550,40551,40556,40557,40562,40563,40568],{},"Another very informative and entertaining talk\nwas ",[585,40552,40555],{"href":40553,"rel":40554},"http://devoxx.com/display/DV11/The+Well-Grounded+Java+Developer",[589],"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 ",[585,40558,40561],{"href":40559,"rel":40560},"http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html",[589],"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 ",[585,40564,40567],{"href":40565,"rel":40566},"http://en.wikipedia.org/wiki/Otter",[589],"otter"," metaphore for the concurrency part of the session.",[18,40570,40571],{},[1773,40572],{"alt":40546,"src":40573},"https://media.synyx.de/uploads//2011/11/devoxx11_well_grounded1.jpg",[18,40575,40576,40581],{},[585,40577,40580],{"href":40578,"rel":40579},"http://devoxx.com/display/DV11/Jenkins++From+Continuous+Integration+to+Continuous+Delivery",[589],"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.",[18,40583,40584],{},"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.",[2352,40586,40588],{"id":40587},"conference-days","Conference days",[18,40590,40591,40592,40597],{},"Wednesday was the first of three conference days (as opposed to university days) and started with three keynotes.\nFirst ",[585,40593,40596],{"href":40594,"rel":40595},"http://twitter.com/stephan007",[589],"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.",[18,40599,40600],{},[1773,40601],{"alt":40602,"src":40603},"\"Josh Bloch at Devoxx 2011\"","https://media.synyx.de/uploads//2011/11/devoxx11_joshbloch1.jpg",[18,40605,40606,40607,40612,40613,40618],{},"There were also awesome talks on this day,\nprominently ",[585,40608,40611],{"href":40609,"rel":40610},"http://devoxx.com/display/DV11/Java++The+Good%2C+the+Bad%2C+and+the+Ugly+Parts",[589],"Java: The Good, the Bad, and the Ugly Parts","\nby Joshua Bloch\nand ",[585,40614,40617],{"href":40615,"rel":40616},"http://devoxx.com/display/DV11/Language+++Library+co-evolution+in+Java+SE+8",[589],"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.",[18,40620,40621,40622,40627,40628,40633],{},"Much of the charm of Devoxx are its informal BOF sessions. Particularly\nthe ",[585,40623,40626],{"href":40624,"rel":40625},"http://devoxx.com/display/DV11/Less+annoying+Java+standards",[589],"Less annoying Java standards"," BOF with Ben Evans and\nMartijn Verburg was very interesting. They presented\nthe ",[585,40629,40632],{"href":40630,"rel":40631},"http://java.net/projects/ljc-london-jug/pages/AdoptAJSRProgram",[589],"Adopt a JSR Program"," and discussed how the Java\ncommunity could participate on the JCP more easily.",[18,40635,40636,40637,40642,40643,40648],{},"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 ",[585,40638,40641],{"href":40639,"rel":40640},"http://typesafe.com/",[589],"Typesafe"," held a talk about [Above the Clouds: Introducing Akka](",[585,40644,40647],{"href":40645,"rel":40646},"http://akka.io/%3EAkka",[589],"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.",[18,40650,40651,40652,40657,40658,40663],{},"Another very interesting talk\nwas ",[585,40653,40656],{"href":40654,"rel":40655},"http://devoxx.com/display/DV11/The+Disruptor%2C+High+Performance+Inter-Thread+Messaging",[589],"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 ",[585,40659,40662],{"href":40660,"rel":40661},"http://code.google.com/p/disruptor/",[589],"Disruptor"," framework and walked the audience through an example utilizing it\nin a simple application.",[18,40665,40666,40667,40672],{},"And even more highlights took place on this day: Dick Wall (of Java Posse fame) talked\nabout ",[585,40668,40671],{"href":40669,"rel":40670},"http://devoxx.com/display/DV11/Courage+in+Software+Development",[589],"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.",[18,40674,40675,40676,40681,40682,40687,40688,40693],{},"Following the official conference program (in parallel to the BOF sessions) there was a screening of the recently\nreleased ",[585,40677,40680],{"href":40678,"rel":40679},"https://web.archive.org/web/20151116171536/http://www.us.movie.tintin.com:80/",[589],"Tintin movie"," and after that\nthe anniversary party in the nearby ",[585,40683,40686],{"href":40684,"rel":40685},"https://web.archive.org/web/20120528164636/http://noxxantwerp.eu/",[589],"Noxx"," night\nclub – of course with a dancing ",[585,40689,40692],{"href":40690,"rel":40691},"http://kenai.com/projects/duke",[589],"Duke",". 😉",[18,40695,40696,40697,40702,40703,40708],{},"On the fifth and last day we sadly had only time for\nthe ",[585,40698,40701],{"href":40699,"rel":40700},"http://devoxx.com/display/DV11/Technical+Discussion+Panel",[589],"Technical Discussion Panel"," and one more talk. Most of\nus\nchose ",[585,40704,40707],{"href":40705,"rel":40706},"http://devoxx.com/display/DV11/The+Evolution+of+Java++Past%2C+Present%2C+and+Future",[589],"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).",[2352,40710,40712],{"id":40711},"summary","Summary",[18,40714,40715,40716,40721],{},"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 ",[585,40717,40720],{"href":40718,"rel":40719},"http://www.devoxx.fr/",[589],"Devoxx France",", if you are a French native speaker) to any serious Java (or\nJVM-based language) developer.",[18,40723,40724],{},"And in case you wondered about the title of this blog post:",[18,40726,40727,8761],{},[585,40728,40731],{"href":40729,"rel":40730},"https://www.youtube.com/embed/8To-6VIJZRE",[589],"Video laden",{"title":48,"searchDepth":86,"depth":86,"links":40733},[40734,40735,40736],{"id":40526,"depth":86,"text":40527},{"id":40587,"depth":86,"text":40588},{"id":40711,"depth":86,"text":40712},[614],"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":40405,"description":40415},"blog/developers-developers-developers",[3491,19738],"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…","3doRV3U2v9GTiPWiYBLxrB8R9wuWA1DUkgZwjc_5OPM",{"id":40749,"title":40442,"author":40750,"body":40751,"category":40834,"date":40835,"description":40836,"extension":617,"link":40837,"meta":40838,"navigation":499,"path":40839,"seo":40840,"slug":40755,"stem":40842,"tags":40843,"teaser":40844,"__hash__":40845},"blog/blog/reasons-why-i-go-to-devoxx.md",[11420],{"type":11,"value":40752,"toc":40832},[40753,40756,40764,40771,40777,40793,40806,40819],[14,40754,40442],{"id":40755},"reasons-why-i-go-to-devoxx",[18,40757,40758,40759,40763],{},"Yet another year is almost over. One of the reasons I notice this is\nbecause ",[585,40760,4729],{"href":40761,"rel":40762},"http://devoxx.com/display/DV11/Home",[589]," 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…",[18,40765,40766,40767,40770],{},"A big challenge in our business is ",[27,40768,40769],{},"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.",[18,40772,40773,40776],{},[27,40774,40775],{},"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.",[18,40778,40779,40780,40785,40786,40789,40790],{},"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, ",[585,40781,40784],{"href":40782,"rel":40783},"http://www.nealford.com/",[589],"Neal Ford","), some the tools, libraries and\nframeworks we are using now (e. g. ",[585,40787,38639],{"href":38636,"rel":40788},[589],") and some simply increased our knowledge and\nbrought us up to date (like the “what’s new in” JPA, Spring or Java talks). ",[27,40791,40792],{},"Go see the stuff you can easily use in\nyour daily work.",[18,40794,40795,40796,40801,40802,40805],{},"In addition to the interesting topics of the talk you just have to look at\nthe ",[585,40797,40800],{"href":40798,"rel":40799},"http://devoxx.com/display/DV11/Devoxxians",[589],"Cast",". There are many well-known ",[27,40803,40804],{},"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.",[18,40807,40808,40809,40812,40813,40818],{},"Another big thing that makes me really looking forward to next week is that I’ll spend a ",[27,40810,40811],{},"nice week in Antwerp with so\nmany of my co-workers and other friends",". I’m looking forward visiting ",[585,40814,40817],{"href":40815,"rel":40816},"http://www.kellys.be/",[589],"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).",[18,40820,40821,40822,40827,40828,40831],{},"And the last thing why I’m going there is optimism: As I requested in\nmy ",[585,40823,40826],{"href":40824,"rel":40825},"http://blog.synyx.de/2010/12/devoxx-2010-revisited/",[589],"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 ",[27,40829,40830],{},"better coffee"," this\ntime :). Be sure, Synyx will report about it…",{"title":48,"searchDepth":86,"depth":86,"links":40833},[],[613],"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":40442,"description":40841},"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",[3491,19738,290],"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":40847,"title":40848,"author":40849,"body":40850,"category":40969,"date":40970,"description":40971,"extension":617,"link":40972,"meta":40973,"navigation":499,"path":40974,"seo":40975,"slug":40854,"stem":40976,"tags":40977,"teaser":40981,"__hash__":40982},"blog/blog/elektronische-urlaubsverwaltung-made-by-youngsters.md","Elektronische Urlaubsverwaltung made by Youngsters",[8328],{"type":11,"value":40851,"toc":40967},[40852,40855,40858,40861,40864,40869,40872,40875,40878,40881,40884,40904,40907,40910,40913,40916,40919,40922,40925,40928,40931,40937,40940,40943,40946,40949,40952,40955,40958,40961,40964],[14,40853,40848],{"id":40854},"elektronische-urlaubsverwaltung-made-by-youngsters",[18,40856,40857],{},"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.",[18,40859,40860],{},"Dies ist nicht nur zeitaufwändig, sondern auch einfach nicht zeitgemäß für eine junge Software-Schmiede wie Synyx.",[18,40862,40863],{},"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:",[18,40865,40866],{},[27,40867,40868],{},"Ein Urlaubsverwaltungs-Tool als Webapplikation muss her.",[18,40870,40871],{},"Als Grundgerüst der Applikation wurden drei verschiedene Arten von Usern festgelegt:",[18,40873,40874],{},"der normale User (Antragssteller: Urlaub beantragen),",[18,40876,40877],{},"der Chef (Vergabeberechtigter: Urlaubsantrag genehmigen/ablehnen) und",[18,40879,40880],{},"das Office (eine Art Super-User mit Verwaltungs- und Editierfunktion: Urlaubsantrag bearbeiten).",[18,40882,40883],{},"Essentielle Grundfunktionen des Urlaubsverwaltungs-Tool sollen sein:",[577,40885,40886,40889,40892,40895,40898,40901],{},[580,40887,40888],{},"Die Authentifizierung soll mittels bereits vorhandenen LDAP-Benutzerdaten der Mitarbeiter erfolgen.",[580,40890,40891],{},"Urlaubsanträge sollen digital validiert werden, das heißt Antragssteller und Antraggenehmigender müssen jeweils über\neinen einzigartigen Identifikationsnachweis (‘digitale Unterschrift’) verfügen.",[580,40893,40894],{},"Eine Interaktion mit dem firmeninternen Google-Kalender soll für automatisierte Übersichtlichkeit sorgen, welche\nMitarbeiter in welchem Zeitraum im Urlaub sind.",[580,40896,40897],{},"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.",[580,40899,40900],{},"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.",[580,40902,40903],{},"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.",[18,40905,40906],{},"Was die programmatischen Werkzeuge betrifft, wurde uns freie Hand gelassen.",[18,40908,40909],{},"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.",[18,40911,40912],{},"Zunächst erschien uns der Arbeitsauftrag ziemlich trivial.",[18,40914,40915],{},"Bei der Konzeption des Tools allerdings stießen wir doch schnell an einige Stolpersteine.",[18,40917,40918],{},"Zu nennen wäre hier beispielsweise:",[18,40920,40921],{},"Die LDAP-Authentifizierung erschien auf den ersten Blick einfacher, bis wir feststellten, was für ein komplexes und\nweites Feld Spring Security eigentlich darstellt.",[18,40923,40924],{},"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)",[18,40926,40927],{},"Auch die Interaktion mit einem Google-Kalender ist ein Feld, in das es sich noch einzulesen gilt.",[18,40929,40930],{},"Den inneren Kern unseres Tools, die Domain-Objekte, haben wir nun letztendlich in die Klassen Antrag, Person,\nUrlaubsanspruch und Urlaubskonto gegliedert.",[18,40932,40933],{},[1773,40934],{"alt":40935,"src":40936},"\"Domainobjekte\"","https://media.synyx.de/uploads//2011/11/Domainobjekte1.png",[18,40938,40939],{},"Als formelle Stolpersteine bei der Konzeption und Implementierung der Domain-Objekte und Services erwies sich die\nUnterscheidung in ‘normalen’ Urlaub und Resturlaub.",[18,40941,40942],{},"Resturlaub ist übrig gebliebener Jahresurlaub aus dem vorherigen Jahr, den ein Mitarbeiter mit ins neue Jahr nimmt.",[18,40944,40945],{},"Zwei wichtige Daten sind also der 1. Januar und der 1. April.",[18,40947,40948],{},"Am 1. Januar wird aus dem verbliebenem Urlaub des vorigen Jahres Resturlaub, während am 1. April dieser Resturlaub\nverfällt.",[18,40950,40951],{},"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.",[18,40953,40954],{},"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.",[18,40956,40957],{},"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.",[18,40959,40960],{},"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.",[18,40962,40963],{},"Das Design betreffend (z.B. dynamische Menü-Navigation oder Kalenderdarstellung ‘DatePicker’) werden wir aller\nWahrscheinlichkeit nach zu Bibliotheken von jQuery greifen.",[18,40965,40966],{},"Bevor wir allerdings überhaupt an das i-Tüpfelchen Design denken können, haben wir noch einen langen Weg vor uns….",{"title":48,"searchDepth":86,"depth":86,"links":40968},[],[5568],"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":40848,"description":40857},"blog/elektronische-urlaubsverwaltung-made-by-youngsters",[5579,38682,40978,5743,40979,12074,40980],"spring-mvc","urlaubsantrag","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…","Iciij-U2SfXBfMkPBt4jEMrE_4Z6koLV-sClbDtNMa0",{"id":40984,"title":40985,"author":40986,"body":40987,"category":41301,"date":41302,"description":41303,"extension":617,"link":41304,"meta":41305,"navigation":499,"path":41306,"seo":41307,"slug":40991,"stem":41308,"tags":41309,"teaser":41311,"__hash__":41312},"blog/blog/make-software-projects-fit-for-git.md","Make software-projects fit for git",[33954],{"type":11,"value":40988,"toc":41299},[40989,40992,40995,41001,41006,41027,41030,41036,41041,41051,41057,41062,41065,41096,41101,41104,41133,41136,41164,41167,41170,41237,41240,41243,41248,41251,41254,41257,41287,41290,41293,41296],[14,40990,40985],{"id":40991},"make-software-projects-fit-for-git",[18,40993,40994],{},"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…",[18,40996,40997],{},[1773,40998],{"alt":40999,"src":41000},"\"git-logo\"","https://media.synyx.de/uploads//2011/10/git-logo.png",[18,41002,41003,986],{},[573,41004,41005],{},"Source code has to be accessible",[18,41007,41008,41009,41015,41016,41022,41023,41026],{},"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 ",[585,41010,41014],{"href":41011,"rel":41012,"title":41013},"http://eagain.net/gitweb/?p=gitosis.git",[589],"Gitosis download","gitosis"," to serve these repositories, but we think\nabout using ",[585,41017,41021],{"href":41018,"rel":41019,"title":41020},"https://github.com/sitaramc/gitolite/wiki",[589],"gitolite on GitHub","gitolite"," because of better/easier\naccess-management. You can also host at ",[585,41024,9390],{"href":33862,"rel":41025,"title":13838},[589],", they do great work and it´s their daily\nbusiness.",[18,41028,41029],{},"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?",[18,41031,41032],{},[1773,41033],{"alt":41034,"src":41035},"\"chiliproject-logo\"","https://media.synyx.de/uploads//2011/10/chiliproject-logo.png",[18,41037,41038,986],{},[573,41039,41040],{},"Software should do what it is intended for",[18,41042,41043,41044,41050],{},"To reach this goal we collect production requirements and fragment the subsequent work into processable packets with\na *\n*project-management tool** called redmine, more precisely ",[585,41045,41049],{"href":41046,"rel":41047,"title":41048},"https://www.chiliproject.org/",[589],"ChiliProject","**chiliproject\n**",", an rapidly evolving fork of redmine.",[18,41052,41053],{},[1773,41054],{"alt":41055,"src":41056},"\"jenkins_logo\"","https://media.synyx.de/uploads//2011/10/jenkins_logo.png",[18,41058,41059,1073],{},[573,41060,41061],{},"Software is something executable",[18,41063,41064],{},"plain source-code is for documentation purposes 😉",[18,41066,41067,41068,41073,41074,41077,41078,41082,41083,41089,41090,8526],{},"We have to build it. Most of our projects are written in Java and built\nwith ",[585,41069,41072],{"href":25317,"rel":41070,"title":41071},[589],"Maven","Apache Maven",". To build the written code automatically and pursue the process of\n",[27,41075,41076],{},"continuous integration"," we utilize a tool named hudson, more precisely ",[585,41079,41081],{"href":34807,"rel":41080,"title":34809},[589],"**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 ",[585,41084,41088],{"href":41085,"rel":41086,"title":41087},"http://blog.synyx.de/2011/05/opensource-is-not-just-about-the-license/",[589],"opensource is not just about the license","blogpost","\nwritten by ",[585,41091,41095],{"href":41092,"rel":41093,"title":41094},"http://blog.synyx.de/autoren/?uid=14",[589],"Fabian Buch","Fabian",[18,41097,41098],{},[27,41099,41100],{},"So first mission is to make ChiliProject fit for git.",[18,41102,41103],{},"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:",[43,41105,41107],{"className":45,"code":41106,"language":47,"meta":48,"style":48},"Host git.domain.tld\nUser git\nIdentityFile ~/.ssh/git_key.priv\n",[50,41108,41109,41117,41125],{"__ignoreMap":48},[53,41110,41111,41114],{"class":55,"line":56},[53,41112,41113],{"class":59},"Host",[53,41115,41116],{"class":63}," git.domain.tld\n",[53,41118,41119,41122],{"class":55,"line":86},[53,41120,41121],{"class":59},"User",[53,41123,41124],{"class":63}," git\n",[53,41126,41127,41130],{"class":55,"line":126},[53,41128,41129],{"class":59},"IdentityFile",[53,41131,41132],{"class":63}," ~/.ssh/git_key.priv\n",[18,41134,41135],{},"Now we need to clone the repository manually by login to the machine running Chili and do:",[43,41137,41139],{"className":45,"code":41138,"language":47,"meta":48,"style":48},"mkdir /path/to/git/repositories/\ncd /path/to/git/repositories/\ngit clone git@gitosis.domain.tld:gitrepo.git\n",[50,41140,41141,41149,41155],{"__ignoreMap":48},[53,41142,41143,41146],{"class":55,"line":56},[53,41144,41145],{"class":59},"mkdir",[53,41147,41148],{"class":63}," /path/to/git/repositories/\n",[53,41150,41151,41153],{"class":55,"line":86},[53,41152,6498],{"class":89},[53,41154,41148],{"class":63},[53,41156,41157,41159,41161],{"class":55,"line":126},[53,41158,40398],{"class":59},[53,41160,32403],{"class":63},[53,41162,41163],{"class":63}," git@gitosis.domain.tld:gitrepo.git\n",[18,41165,41166],{},"for sure git has to be installed on the server running Chili. And the repository already exists…",[18,41168,41169],{},"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:",[43,41171,41173],{"className":45,"code":41172,"language":47,"meta":48,"style":48},"*/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",[50,41174,41175],{"__ignoreMap":48},[53,41176,41177,41179,41182,41184,41186,41188,41190,41192,41194,41196,41199,41201,41204,41206,41209,41211,41214,41216,41218,41221,41224,41226,41228,41231,41234],{"class":55,"line":56},[53,41178,5009],{"class":389},[53,41180,41181],{"class":82},"/5 ",[53,41183,5009],{"class":389},[53,41185,1058],{"class":389},[53,41187,1058],{"class":389},[53,41189,1058],{"class":389},[53,41191,144],{"class":389},[53,41193,31747],{"class":82},[53,41195,31527],{"class":389},[53,41197,41198],{"class":63}," /path/to/git/repositories/*/",[53,41200,30366],{"class":82},[53,41202,41203],{"class":389},"do",[53,41205,32506],{"class":89},[53,41207,41208],{"class":82}," $i && ",[53,41210,40398],{"class":59},[53,41212,41213],{"class":63}," fetch",[53,41215,30366],{"class":82},[53,41217,40398],{"class":59},[53,41219,41220],{"class":63}," reset",[53,41222,41223],{"class":63}," refs/remotes/origin/master",[53,41225,30366],{"class":82},[53,41227,13332],{"class":389},[53,41229,41230],{"class":389}," >>",[53,41232,41233],{"class":82},"/dev/null ",[53,41235,41236],{"class":389},"2>&1\n",[18,41238,41239],{},"jap, you can log into a file instead of /dev/null, but we trust… 🙂",[18,41241,41242],{},"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.",[18,41244,41245],{},[27,41246,41247],{},"Get Jenkins to work with git",[18,41249,41250],{},"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!)",[18,41252,41253],{},"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?)",[18,41255,41256],{},"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:",[43,41258,41260],{"className":45,"code":41259,"language":47,"meta":48,"style":48},"[user]\n name = \"Jenkins\"\n email = \"jenkins@jenkins.domain.tld\"\n",[50,41261,41262,41267,41277],{"__ignoreMap":48},[53,41263,41264],{"class":55,"line":56},[53,41265,41266],{"class":82},"[user]\n",[53,41268,41269,41272,41274],{"class":55,"line":86},[53,41270,41271],{"class":59}," name",[53,41273,1245],{"class":63},[53,41275,41276],{"class":63}," \"Jenkins\"\n",[53,41278,41279,41282,41284],{"class":55,"line":126},[53,41280,41281],{"class":59}," email",[53,41283,1245],{"class":63},[53,41285,41286],{"class":63}," \"jenkins@jenkins.domain.tld\"\n",[18,41288,41289],{},"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)",[18,41291,41292],{},"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",[18,41294,41295],{},"Finally you can build the project(small prayer could help 😉 ) and enjoy the built software and Jenkins´ expandable\nworkflow…",[607,41297,41298],{},"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":48,"searchDepth":86,"depth":86,"links":41300},[],[4122,613,996],"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":40985,"description":40994},"blog/make-software-projects-fit-for-git",[41310,290,10977,12072,25655,6503],"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…","vCNC4CoaSUI5ng_rt7Nu-7xypFBFlUaDbA6FV5gZLNA",{"id":41314,"title":41315,"author":41316,"body":41318,"category":41652,"date":41653,"description":41654,"extension":617,"link":41655,"meta":41656,"navigation":499,"path":41657,"seo":41658,"slug":41660,"stem":41661,"tags":41662,"teaser":41663,"__hash__":41664},"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",[41317],"hopf",{"type":11,"value":41319,"toc":41650},[41320,41323,41341,41344,41350,41353,41356,41370,41403,41406,41546,41549,41645,41648],[14,41321,41315],{"id":41322},"testing-webapp-startup-on-jenkins-using-maven-tomcat-and-web-driver",[18,41324,41325,41326,41331,41332,41335,41336,986],{},"Modern web applications often consist of quite some configuration files that should at least be tested for validity.\nThink\nof ",[585,41327,41330],{"href":41328,"rel":41329},"http://static.springsource.org/spring/docs/3.0.6.RELEASE/spring-framework-reference/html/mvc.html",[589],"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 (",[585,41333,34809],{"href":34807,"rel":41334},[589]," or Hudson) using\nthe ",[585,41337,41340],{"href":41338,"rel":41339},"https://web.archive.org/web/20150531090420/http://mojo.codehaus.org/tomcat-maven-plugin/",[589],"Tomcat Maven Plugin",[18,41342,41343],{},"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:",[43,41345,41348],{"className":41346,"code":41347,"language":6283},[6282],"\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",[50,41349,41347],{"__ignoreMap":48},[18,41351,41352],{},"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.",[18,41354,41355],{},"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.",[18,41357,41358,41359,41364,41365,41369],{},"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 ",[585,41360,41363],{"href":41361,"rel":41362},"http://seleniumhq.org/projects/webdriver/",[589],"Web Driver project"," which merged\nwith ",[585,41366,41368],{"href":35243,"rel":41367},[589],"Selenium"," in Selenium 2.0. Add the dependency to your build:",[43,41371,41373],{"className":1980,"code":41372,"language":1982,"meta":48,"style":48},"\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",[50,41374,41375,41379,41384,41389,41394,41399],{"__ignoreMap":48},[53,41376,41377],{"class":55,"line":56},[53,41378,39660],{},[53,41380,41381],{"class":55,"line":86},[53,41382,41383],{}," \u003CgroupId>org.seleniumhq.selenium\u003C/groupId>\n",[53,41385,41386],{"class":55,"line":126},[53,41387,41388],{}," \u003CartifactId>selenium-htmlunit-driver\u003C/artifactId>\n",[53,41390,41391],{"class":55,"line":163},[53,41392,41393],{}," \u003Cversion>2.3.1\u003C/version>\n",[53,41395,41396],{"class":55,"line":186},[53,41397,41398],{}," \u003Cscope>test\u003C/scope>\n",[53,41400,41401],{"class":55,"line":221},[53,41402,39680],{},[18,41404,41405],{},"A simple webtest that just calls a page and checks for a certain html element might look like this:",[43,41407,41409],{"className":288,"code":41408,"language":290,"meta":48,"style":48},"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",[50,41410,41411,41416,41421,41426,41431,41436,41441,41446,41451,41456,41461,41466,41471,41476,41480,41485,41490,41495,41499,41504,41509,41514,41519,41524,41528,41533,41538,41542],{"__ignoreMap":48},[53,41412,41413],{"class":55,"line":56},[53,41414,41415],{},"import org.junit.Test;\n",[53,41417,41418],{"class":55,"line":86},[53,41419,41420],{},"import org.openqa.selenium.By;\n",[53,41422,41423],{"class":55,"line":126},[53,41424,41425],{},"import org.openqa.selenium.NoSuchElementException;\n",[53,41427,41428],{"class":55,"line":163},[53,41429,41430],{},"import org.openqa.selenium.WebDriver;\n",[53,41432,41433],{"class":55,"line":186},[53,41434,41435],{},"import org.openqa.selenium.WebElement;\n",[53,41437,41438],{"class":55,"line":221},[53,41439,41440],{},"import org.openqa.selenium.htmlunit.HtmlUnitDriver;\n",[53,41442,41443],{"class":55,"line":242},[53,41444,41445],{},"import static org.junit.Assert.assertNotNull;\n",[53,41447,41448],{"class":55,"line":273},[53,41449,41450],{},"import static org.junit.Assert.fail;\n",[53,41452,41453],{"class":55,"line":279},[53,41454,41455],{},"/**\n",[53,41457,41458],{"class":55,"line":496},[53,41459,41460],{}," * Simple web test that just queries the login page through the controller.\n",[53,41462,41463],{"class":55,"line":503},[53,41464,41465],{}," * @author Florian Hopf, Synyx GmbH & Co. KG, hopf@synyx.de\n",[53,41467,41468],{"class":55,"line":509},[53,41469,41470],{}," */\n",[53,41472,41473],{"class":55,"line":515},[53,41474,41475],{},"public class LoginPageWebtest {\n",[53,41477,41478],{"class":55,"line":521},[53,41479,928],{},[53,41481,41482],{"class":55,"line":527},[53,41483,41484],{}," public void testPage() {\n",[53,41486,41487],{"class":55,"line":533},[53,41488,41489],{}," WebDriver driver = new HtmlUnitDriver();\n",[53,41491,41492],{"class":55,"line":539},[53,41493,41494],{}," driver.get(\"http://localhost:8081/url/that/redirects/to/login/\");\n",[53,41496,41497],{"class":55,"line":545},[53,41498,36470],{},[53,41500,41501],{"class":55,"line":2070},[53,41502,41503],{}," // Find the text input element by its name\n",[53,41505,41506],{"class":55,"line":2075},[53,41507,41508],{}," WebElement element = driver.findElement(By.name(\"username\"));\n",[53,41510,41511],{"class":55,"line":2081},[53,41512,41513],{}," assertNotNull(element);\n",[53,41515,41516],{"class":55,"line":2087},[53,41517,41518],{}," } catch (NoSuchElementException ex) {\n",[53,41520,41521],{"class":55,"line":2092},[53,41522,41523],{}," fail(\"Startup of context failed. See console output for more information, : \" + ex.getMessage());\n",[53,41525,41526],{"class":55,"line":2097},[53,41527,12712],{},[53,41529,41530],{"class":55,"line":2103},[53,41531,41532],{}," //Close the browser\n",[53,41534,41535],{"class":55,"line":2109},[53,41536,41537],{}," driver.quit();\n",[53,41539,41540],{"class":55,"line":2115},[53,41541,860],{},[53,41543,41544],{"class":55,"line":2120},[53,41545,282],{},[18,41547,41548],{},"Execute the Webtest classes in your profile (we are using a naming convention to distinguish web tests from normal unit\ntests):",[43,41550,41552],{"className":1980,"code":41551,"language":1982,"meta":48,"style":48},"\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",[50,41553,41554,41558,41563,41568,41573,41577,41582,41587,41592,41597,41602,41607,41612,41617,41622,41627,41632,41636,41641],{"__ignoreMap":48},[53,41555,41556],{"class":55,"line":56},[53,41557,34968],{},[53,41559,41560],{"class":55,"line":86},[53,41561,41562],{}," \u003CgroupId>org.apache.maven.plugins\u003C/groupId>\n",[53,41564,41565],{"class":55,"line":126},[53,41566,41567],{}," \u003CartifactId>maven-surefire-plugin\u003C/artifactId>\n",[53,41569,41570],{"class":55,"line":163},[53,41571,41572],{}," \u003Cexecutions>\n",[53,41574,41575],{"class":55,"line":186},[53,41576,34993],{},[53,41578,41579],{"class":55,"line":221},[53,41580,41581],{}," \u003Cid>run-webtests\u003C/id>\n",[53,41583,41584],{"class":55,"line":242},[53,41585,41586],{}," \u003Cphase>integration-test\u003C/phase>\n",[53,41588,41589],{"class":55,"line":273},[53,41590,41591],{}," \u003Cgoals>\n",[53,41593,41594],{"class":55,"line":279},[53,41595,41596],{}," \u003Cgoal>test\u003C/goal>\n",[53,41598,41599],{"class":55,"line":496},[53,41600,41601],{}," \u003C/goals>\n",[53,41603,41604],{"class":55,"line":503},[53,41605,41606],{}," \u003Cconfiguration>\n",[53,41608,41609],{"class":55,"line":509},[53,41610,41611],{}," \u003Cincludes>\n",[53,41613,41614],{"class":55,"line":515},[53,41615,41616],{}," \u003Cinclude>**/*Webtest.java\u003C/include>\n",[53,41618,41619],{"class":55,"line":521},[53,41620,41621],{}," \u003C/includes>\n",[53,41623,41624],{"class":55,"line":527},[53,41625,41626],{}," \u003Cskip>false\u003C/skip>\n",[53,41628,41629],{"class":55,"line":533},[53,41630,41631],{}," \u003C/configuration>\n",[53,41633,41634],{"class":55,"line":539},[53,41635,35041],{},[53,41637,41638],{"class":55,"line":545},[53,41639,41640],{}," \u003C/executions>\n",[53,41642,41643],{"class":55,"line":2070},[53,41644,35103],{},[18,41646,41647],{},"That’s all. In case there is an error in your configuration you will be notified by your CI server that the webtest\nfailed.",[607,41649,989],{},{"title":48,"searchDepth":86,"depth":86,"links":41651},[],[613],"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":41315,"description":41659},"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",[22827,10977,22400,26512,35169],"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":41666,"title":41667,"author":41668,"body":41669,"category":41771,"date":41772,"description":41773,"extension":617,"link":41774,"meta":41775,"navigation":499,"path":41776,"seo":41777,"slug":41673,"stem":41778,"tags":41779,"teaser":41785,"__hash__":41786},"blog/blog/number-formats-and-jdbc-voodoo.md","Number formats and JDBC voodoo",[8219],{"type":11,"value":41670,"toc":41769},[41671,41674,41677,41680,41691,41694,41697,41706,41717,41735,41742,41751,41758,41767],[14,41672,41667],{"id":41673},"number-formats-and-jdbc-voodoo",[18,41675,41676],{},"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…",[18,41678,41679],{},"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:",[43,41681,41685],{"className":41682,"code":41683,"language":41684,"meta":48,"style":48},"language-sql shiki shiki-themes github-light github-dark","update distances set distance = 12.250 where id = 1;\n","sql",[50,41686,41687],{"__ignoreMap":48},[53,41688,41689],{"class":55,"line":56},[53,41690,41683],{},[18,41692,41693],{},"And guess what happens? The row with the ID 1 is updated to the value 12.250.",[18,41695,41696],{},"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:",[43,41698,41700],{"className":41682,"code":41699,"language":41684,"meta":48,"style":48},"update distances set distance = '12.250' where id = 1;\n",[50,41701,41702],{"__ignoreMap":48},[53,41703,41704],{"class":55,"line":56},[53,41705,41699],{},[18,41707,41708,41709,41712,41713,41716],{},"No major problem one may think – oracle will just implicitly call the ",[50,41710,41711],{},"TO_NUMBER()"," function and everything should work\nlike a charm. Should – but does not. At least not always. Sometimes it fails with ",[50,41714,41715],{},"ORA-01722: invalid number",". But why?",[18,41718,41719,41720,41722,41723,41726,41727,41730,41731,41734],{},"Let’s take a close look at the ",[50,41721,41711],{}," function. The function can be configured through the (optional)\n",[50,41724,41725],{},"nlsparams",", where one is ",[50,41728,41729],{},"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 ",[50,41732,41733],{},"NLS_NUMERIC_CHARACTERS"," is used. If this variable\nis not set either, the oracle default value is used, which is ‘.,’ (UK format).",[18,41736,41737,41738,41741],{},"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 ",[50,41739,41740],{},"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):",[43,41743,41745],{"className":41682,"code":41744,"language":41684,"meta":48,"style":48},"update distances set distance = '12,250' where id = 1;\n",[50,41746,41747],{"__ignoreMap":48},[53,41748,41749],{"class":55,"line":56},[53,41750,41744],{},[18,41752,41753,41754,41757],{},"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",[50,41755,41756],{},"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.",[18,41759,41760,41761,41763,41764,41766],{},"So if your application connects your database through JDBC and you rely on the implicit ",[50,41762,41711],{}," conversion of\nOracle, make sure the system property ",[50,41765,41756],{}," is set to the correct value!",[607,41768,989],{},{"title":48,"searchDepth":86,"depth":86,"links":41770},[],[613],"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":41667,"description":41676},"blog/number-formats-and-jdbc-voodoo",[41780,41781,41782,41783,41784],"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":41788,"title":41789,"author":41790,"body":41791,"category":41997,"date":41998,"description":41999,"extension":617,"link":42000,"meta":42001,"navigation":499,"path":42002,"seo":42003,"slug":41795,"stem":42005,"tags":42006,"teaser":42008,"__hash__":42009},"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",[40407],{"type":11,"value":41792,"toc":41995},[41793,41796,41836,41842,41845,41854,41857,41860,41883,41886,41892,41895,41923,41937,41946,41949,41965,41970,41976,41987],[14,41794,41789],{"id":41795},"continuous-delivery-or-how-i-learned-to-stop-worrying-and-love-the-pipeline",[18,41797,41798,41799,41802,41803,41807,41808,41813,41814,41819,41820,41825,41826,41819,41831,4562],{},"Following our principle of ",[573,41800,41801],{},"Continuous Skill Enhancement"," here at ",[585,41804,41806],{"href":3471,"rel":41805},[589],"Synyx"," I\nrecently read the\nbook ",[585,41809,41812],{"href":41810,"rel":41811},"http://www.informit.com/store/product.aspx?isbn=0321601912",[589],"Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation","\nby ",[585,41815,41818],{"href":41816,"rel":41817},"http://jezhumble.net/",[589],"Jez Humble"," (from ",[585,41821,41824],{"href":41822,"rel":41823},"http://www.thoughtworks.com/",[589],"ThoughtWorks",")\nand ",[585,41827,41830],{"href":41828,"rel":41829},"http://www.davefarley.net/",[589],"David Farley",[585,41832,41835],{"href":41833,"rel":41834},"http://www.lmaxtrader.co.uk/",[589],"LMAX",[18,41837,41838],{},[1773,41839],{"alt":41840,"src":41841},"\"Continuous Delivery\"","https://media.synyx.de/uploads//2011/08/continuous_delivery_cover.jpeg",[18,41843,41844],{},"The book consists of three distinct parts.",[18,41846,41847,41848,41853],{},"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 ",[585,41849,41852],{"href":41850,"rel":41851},"http://www.informit.com/store/product.aspx?isbn=0321336380",[589],"other books"," for that. But of course you’re already\nfamiliar with these topics so it’s just a little refresher.",[18,41855,41856],{},"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.",[18,41858,41859],{},"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.",[18,41861,41862,41863,41867,41868,41871,41872,17019,41877,41882],{},"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 ",[585,41864,26565],{"href":41865,"rel":41866},"http://www.puppetlabs.com/",[589],", 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 ",[585,41869,41072],{"href":25317,"rel":41870},[589],". In the following\nchapter the authors give an introduction to different revision control systems\nlike ",[585,41873,41876],{"href":41874,"rel":41875},"http://subversion.apache.org/",[589],"Subversion",[585,41878,41881],{"href":41879,"rel":41880},"http://gitscm.com/",[589],"Git",", 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.",[18,41884,41885],{},"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.",[18,41887,41888,41889,41891],{},"The concepts detailed in ",[573,41890,40534],{}," 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.",[18,41893,41894],{},"Some of the distilled concepts are:",[577,41896,41897,41904,41907,41910,41917],{},[580,41898,41899,41900,41903],{},"Build binaries exactly ",[27,41901,41902],{},"once",", store them in your artifact repository and promote them through the complete\ndeployment pipeline.",[580,41905,41906],{},"Only promote builds into staging or production that pass all unit and acceptance tests.",[580,41908,41909],{},"The development, testing, UAT and staging environments should be as similar as possible to the production environment.",[580,41911,41912,41913,41916],{},"Automate ",[27,41914,41915],{},"everything",": builds, configuration, tests. Human interaction is prone to error, try to avoid it wherever\npossible.",[580,41918,41919,41920],{},"Use version control for ",[27,41921,41922],{},"everything, including the configuration of underlying operating systems and infrastructure\nsuch as networking equipment.",[18,41924,41925,41927,41928,41933,41934,986],{},[573,41926,40534],{}," has rightfully received much praise around the Internet and especially in the recently popularized\nDevOps movement. In 2011, the authors also won a ",[585,41929,41932],{"href":41930,"rel":41931},"http://drdobbs.com/joltawards/231500080?pgno=7",[589],"Jolt Excellence Award","\nin the category ",[573,41935,41936],{},"The Best Books",[18,41938,41939,41940,41945],{},"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 ",[585,41941,41944],{"href":41942,"rel":41943},"http://continuousdelivery.com/",[589],"supporting website"," of the book.",[18,41947,41948],{},"Example:",[577,41950,41951,41958],{},[580,41952,41953],{},[585,41954,41957],{"href":41955,"rel":41956},"http://bit.ly/bibNp0",[589],"bit.ly/bibNp0",[580,41959,41960],{},[585,41961,41964],{"href":41962,"rel":41963},"http://continuousdelivery.com/go/bibNp0",[589],"continuousdelivery.com/go/bibNp0",[18,41966,41967,41968,986],{},"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",[573,41969,40534],{},[18,41971,41972,41973,41975],{},"As a conclusion, I can really recommend reading ",[573,41974,40534],{}," 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.",[18,41977,41978,41979,5859,41982,986],{},"If you’re hooked now you might want to read the sample chapter from ",[573,41980,41981],{},"Continuous\nDelivery",[585,41983,41986],{"href":41984,"rel":41985},"http://www.informit.com/content/images/9780321601919/samplepages/0321601912.pdf",[589],"Chapter 5 – Anatomy of the Deployment Pipeline",[18,41988,41989,41990],{},"Oh, and by the way: ",[585,41991,41994],{"href":41992,"rel":41993},"https://jobs.synyx.de/",[589],"We’re hiring!",{"title":48,"searchDepth":86,"depth":86,"links":41996},[],[613],"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":41789,"description":42004},"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",[42007,31939,31940,22827,19860,6503],"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…","f_JR-CIvgZ2VCVSWsQjdTvAK1X1BsmKrdOPqauLk5lc",{"id":42011,"title":42012,"author":42013,"body":42014,"category":42041,"date":42042,"description":42027,"extension":617,"link":42043,"meta":42044,"navigation":499,"path":42045,"seo":42046,"slug":42018,"stem":42047,"tags":42048,"teaser":42050,"__hash__":42051},"blog/blog/endlich-mal-mit-profis-arbeiten.md","Endlich mal mit Profis arbeiten?",[11619],{"type":11,"value":42015,"toc":42039},[42016,42019,42028,42031],[14,42017,42012],{"id":42018},"endlich-mal-mit-profis-arbeiten",[18,42020,42021,42025],{},[1773,42022],{"alt":42023,"src":42024},"\" Java Entwickler wanted\"","https://media.synyx.de/uploads//2011/07/java_entwickler_wanted.jpg",[27,42026,42027],{},"Wir suchen ab sofort Verstärkung für unser Individualsoftware-Team!",[18,42029,42030],{},"Interessante Projekte, nette Arbeitsatmosphäre und alles, was man sonst so braucht.",[18,42032,42033,42034],{},"Schau mal rein, egal ob Du zum reinen Entwickler, zum Softwarearchitekten oder zum Kommunikationsgenie\ntendierst. ",[585,42035,42038],{"href":41992,"rel":42036,"title":42037},[589],"Java Entwickler wanted!","Mehr Infos",{"title":48,"searchDepth":86,"depth":86,"links":42040},[],[613],"2011-07-06T10:15:59","https://synyx.de/blog/endlich-mal-mit-profis-arbeiten/",{},"/blog/endlich-mal-mit-profis-arbeiten",{"title":42012,"description":42027},"blog/endlich-mal-mit-profis-arbeiten",[8847,290,42049,5743],"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…","OU-Bssp8svksPjHhMZcY_bdkyP4iXjbnMH0ig1Zq5jI",{"id":42053,"title":42054,"author":42055,"body":42056,"category":43924,"date":43925,"description":43926,"extension":617,"link":43927,"meta":43928,"navigation":499,"path":43929,"seo":43930,"slug":42060,"stem":43932,"tags":43933,"teaser":43938,"__hash__":43939},"blog/blog/the-tale-of-jboss-and-the-7-little-logging-frameworks.md","The Tale of JBoss and the 7 Little Logging Frameworks",[40407],{"type":11,"value":42057,"toc":43920},[42058,42061,42068,42074,42077,42091,42109,42129,42138,42147,42156,42161,42164,42173,42241,42250,42308,42323,43698,43701,43740,43755,43773,43776,43826,43830,43887,43894,43898,43917],[14,42059,42054],{"id":42060},"the-tale-of-jboss-and-the-7-little-logging-frameworks",[18,42062,42063,42064,42067],{},"At Synyx we’re currently taking care of a rather large legacy project for one of our customers in the course of\nour ",[585,42065,8916],{"href":38596,"rel":42066},[589]," 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.",[18,42069,42070],{},[1773,42071],{"alt":42072,"src":42073},"\"7 Little Logging Frameworks\"","https://media.synyx.de/uploads//2011/06/7dwarves.jpg",[18,42075,42076],{},"7 Little Logging Frameworks on their way into your code base",[18,42078,42079,42080,42085,42086,42090],{},"The original developers of the system suffered a serious case of the\nwell-known ",[585,42081,42084],{"href":42082,"rel":42083},"http://en.wikipedia.org/wiki/Not_Invented_Here",[589],"NIH syndrome"," and thus a lot\nof ",[585,42087,20923],{"href":42088,"rel":42089},"http://www.martinfowler.com/bliki/TechnicalDebt.html",[589]," has been piled up over the course of its\ndevelopment.",[18,42092,42093,42094,99,42099,3038,42104,986],{},"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 ",[585,42095,42098],{"href":42096,"rel":42097},"http://commons.apache.org/logging/",[589],"Apache Commons Logging",[585,42100,42103],{"href":42101,"rel":42102},"http://www.slf4j.org/",[589],"SLF4J",[585,42105,42108],{"href":42106,"rel":42107},"http://logging.apache.org/log4j/",[589],"Log4j",[18,42110,42111,42112,3038,42117,42122,42123,42128],{},"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 ",[585,42113,42116],{"href":42114,"rel":42115},"http://bsnyderblog.blogspot.com/2007/08/my-soapbox-for-slf4j.html",[589],"My Soapbox for SLF4J",[585,42118,42121],{"href":42119,"rel":42120},"http://blog.frankel.ch/thoughts-on-java-logging-and-slf4j",[589],"Thoughts on Java logging and SLF4J"," for a more detailed\ndiscussion of SLF4J’s features. Did I mention SLF4J also ",[585,42124,42127],{"href":42125,"rel":42126},"http://www.slf4j.org/android/",[589],"works on Android","?",[18,42130,42131,42132,42137],{},"SLF4J thankfully makes it easy to ",[585,42133,42136],{"href":42134,"rel":42135},"http://www.slf4j.org/legacy.html",[589],"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.",[18,42139,42140,42141,42146],{},"As you might have already guessed from the title of this blog post, the project uses the\nestablished ",[585,42142,42145],{"href":42143,"rel":42144},"http://www.jboss.org/",[589],"JBoss"," Application Server. Unfortunately it’s currently stuck at version\n4.0.3.SP1 but that’s another topic.",[18,42148,42149,42150,42155],{},"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 ",[585,42151,42154],{"href":42152,"rel":42153},"http://www.slf4j.org/apidocs/org/slf4j/Logger.html",[589],"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.",[18,42157,42158],{},[1773,42159],{"alt":42160,"src":42101},"\"SLF4J Logo\"",[18,42162,42163],{},"Simple Logging Facade for Java",[18,42165,42166,42167,42172],{},"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 ",[585,42168,42171],{"href":42169,"rel":42170},"http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management",[589],"dependency management","\nfeature and added the following declarations in the parent POM for the project’s components:",[43,42174,42176],{"className":1980,"code":42175,"language":1982,"meta":48,"style":48},"\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",[50,42177,42178,42183,42188,42193,42198,42203,42208,42213,42217,42222,42227,42232,42236],{"__ignoreMap":48},[53,42179,42180],{"class":55,"line":56},[53,42181,42182],{},"\u003CdependencyManagement>\n",[53,42184,42185],{"class":55,"line":86},[53,42186,42187],{}," \u003Cdependency>\n",[53,42189,42190],{"class":55,"line":126},[53,42191,42192],{}," \u003CgroupId>commons-logging\u003C/groupId>\n",[53,42194,42195],{"class":55,"line":163},[53,42196,42197],{}," \u003CartifactId>commons-logging\u003C/artifactId>\n",[53,42199,42200],{"class":55,"line":186},[53,42201,42202],{}," \u003Cversion>1.1.1\u003C/version>\n",[53,42204,42205],{"class":55,"line":221},[53,42206,42207],{}," \u003Cscope>provided\u003C/scope>\n",[53,42209,42210],{"class":55,"line":242},[53,42211,42212],{}," \u003C/dependency>\n",[53,42214,42215],{"class":55,"line":273},[53,42216,42187],{},[53,42218,42219],{"class":55,"line":279},[53,42220,42221],{}," \u003CgroupId>org.slf4j\u003C/groupId>\n",[53,42223,42224],{"class":55,"line":496},[53,42225,42226],{}," \u003CartifactId>jcl-over-slf4j\u003C/artifactId>\n",[53,42228,42229],{"class":55,"line":503},[53,42230,42231],{}," \u003Cversion>1.6.1\u003C/version>\n",[53,42233,42234],{"class":55,"line":509},[53,42235,42212],{},[53,42237,42238],{"class":55,"line":515},[53,42239,42240],{},"\u003C/dependencyManagement>\n",[18,42242,42243,42244,42249],{},"By setting\nthe ",[585,42245,42248],{"href":42246,"rel":42247},"http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope",[589],"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:",[43,42251,42253],{"className":1980,"code":42252,"language":1982,"meta":48,"style":48},"\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",[50,42254,42255,42259,42264,42269,42274,42279,42284,42289,42294,42299,42304],{"__ignoreMap":48},[53,42256,42257],{"class":55,"line":56},[53,42258,39660],{},[53,42260,42261],{"class":55,"line":86},[53,42262,42263],{}," \u003CgroupId>org.apache.ws.commons.axiom\u003C/groupId>\n",[53,42265,42266],{"class":55,"line":126},[53,42267,42268],{}," \u003CartifactId>axiom-api\u003C/artifactId>\n",[53,42270,42271],{"class":55,"line":163},[53,42272,42273],{}," \u003Cversion>1.2\u003C/version>\n",[53,42275,42276],{"class":55,"line":186},[53,42277,42278],{}," \u003Cexclusions>\n",[53,42280,42281],{"class":55,"line":221},[53,42282,42283],{}," \u003Cexclusion>\n",[53,42285,42286],{"class":55,"line":242},[53,42287,42288],{}," \u003CartifactId>commons-logging\u003C/artifactId>\n",[53,42290,42291],{"class":55,"line":273},[53,42292,42293],{}," \u003CgroupId>commons-logging\u003C/groupId>\n",[53,42295,42296],{"class":55,"line":279},[53,42297,42298],{}," \u003C/exclusion>\n",[53,42300,42301],{"class":55,"line":496},[53,42302,42303],{}," \u003C/exclusions>\n",[53,42305,42306],{"class":55,"line":503},[53,42307,39680],{},[18,42309,42310,42311,42316,42317,42322],{},"In order to analyze the dependency trees of our components\nthe ",[585,42312,42315],{"href":42313,"rel":42314},"http://maven.apache.org/plugins/maven-dependency-plugin/",[589],"Maven dependency plugin"," turned out to be very useful,\nespecially the ",[585,42318,42321],{"href":42319,"rel":42320},"http://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html",[589],"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:",[43,42324,42326],{"className":29913,"code":42325,"language":29915,"meta":48,"style":48},"[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",[50,42327,42328,42333,42338,42343,42356,42361,42366,42371,42383,42394,42405,42416,42427,42438,42443,42448,42453,42458,42463,42468,42473,42484,42498,42513,42529,42544,42559,42574,42589,42604,42619,42637,42652,42667,42682,42697,42713,42729,42744,42762,42777,42793,42806,42821,42836,42851,42864,42877,42892,42905,42918,42931,42946,42959,42972,42985,42996,43009,43022,43037,43052,43067,43082,43097,43113,43127,43143,43159,43173,43187,43201,43217,43231,43245,43259,43273,43285,43297,43309,43321,43335,43347,43361,43373,43385,43397,43403,43409,43415,43421,43427,43439,43453,43467,43481,43493,43505,43517,43529,43543,43557,43569,43581,43593,43605,43617,43629,43641,43647,43658,43664,43670,43675,43681,43687,43693],{"__ignoreMap":48},[53,42329,42330],{"class":55,"line":56},[53,42331,42332],{"class":82},"[INFO] [dependency:tree {execution: default-cli}]\n",[53,42334,42335],{"class":55,"line":86},[53,42336,42337],{"class":82},"[INFO] com.example:swing-client:jar:1.1.0-SNAPSHOT\n",[53,42339,42340],{"class":55,"line":126},[53,42341,42342],{"class":82},"[INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.1:compile\n",[53,42344,42345,42348,42350,42353],{"class":55,"line":163},[53,42346,42347],{"class":82},"[INFO] ",[53,42349,2665],{"class":389},[53,42351,42352],{"class":59}," \\-",[53,42354,42355],{"class":63}," log4j:log4j:jar:1.2.16:compile\n",[53,42357,42358],{"class":55,"line":186},[53,42359,42360],{"class":82},"[INFO] +- jgoodies:plastic:jar:1.2.0:compile\n",[53,42362,42363],{"class":55,"line":221},[53,42364,42365],{"class":82},"[INFO] +- com.toedter:jcalendar:jar:1.3.2:compile\n",[53,42367,42368],{"class":55,"line":242},[53,42369,42370],{"class":82},"[INFO] +- com.example:jms:jar:1.0.1-SNAPSHOT:compile\n",[53,42372,42373,42375,42377,42380],{"class":55,"line":273},[53,42374,42347],{"class":82},[53,42376,2665],{"class":389},[53,42378,42379],{"class":59}," +-",[53,42381,42382],{"class":63}," jboss:jboss-system:jar:4.0.2:compile\n",[53,42384,42385,42387,42389,42391],{"class":55,"line":279},[53,42386,42347],{"class":82},[53,42388,2665],{"class":389},[53,42390,42379],{"class":59},[53,42392,42393],{"class":63}," jms:jms:jar:1.0.2:compile\n",[53,42395,42396,42398,42400,42402],{"class":55,"line":496},[53,42397,42347],{"class":82},[53,42399,2665],{"class":389},[53,42401,42379],{"class":59},[53,42403,42404],{"class":63}," org.quartz-scheduler:quartz:jar:1.2.3:compile\n",[53,42406,42407,42409,42411,42413],{"class":55,"line":503},[53,42408,42347],{"class":82},[53,42410,2665],{"class":389},[53,42412,42379],{"class":59},[53,42414,42415],{"class":63}," jboss:jbossmq-client:jar:4.0.2:compile\n",[53,42417,42418,42420,42422,42424],{"class":55,"line":509},[53,42419,42347],{"class":82},[53,42421,2665],{"class":389},[53,42423,42379],{"class":59},[53,42425,42426],{"class":63}," jboss:jboss-jmx:jar:4.0.2:compile\n",[53,42428,42429,42431,42433,42435],{"class":55,"line":515},[53,42430,42347],{"class":82},[53,42432,2665],{"class":389},[53,42434,42352],{"class":59},[53,42436,42437],{"class":63}," javax.ejb:ejb:jar:2.1:compile\n",[53,42439,42440],{"class":55,"line":521},[53,42441,42442],{"class":82},"[INFO] +- flex:flex-messaging-common:jar:1.0.0:compile\n",[53,42444,42445],{"class":55,"line":527},[53,42446,42447],{"class":82},"[INFO] +- flex:flex-messaging-core:jar:1.0.0:compile\n",[53,42449,42450],{"class":55,"line":533},[53,42451,42452],{"class":82},"[INFO] +- flex:flex-messaging-opt:jar:1.0.0:compile\n",[53,42454,42455],{"class":55,"line":539},[53,42456,42457],{"class":82},"[INFO] +- flex:flex-messaging-proxy:jar:1.0.0:compile\n",[53,42459,42460],{"class":55,"line":545},[53,42461,42462],{"class":82},"[INFO] +- flex:flex-messaging-remoting:jar:1.0.0:compile\n",[53,42464,42465],{"class":55,"line":2070},[53,42466,42467],{"class":82},"[INFO] +- poi:poi-2.5.1-final:jar:20040804:compile\n",[53,42469,42470],{"class":55,"line":2075},[53,42471,42472],{"class":82},"[INFO] +- com.example:docengine:jar:1.0.0-SNAPSHOT:compile\n",[53,42474,42475,42477,42479,42481],{"class":55,"line":2081},[53,42476,42347],{"class":82},[53,42478,2665],{"class":389},[53,42480,42379],{"class":59},[53,42482,42483],{"class":63}," com.example:webfw:jar:1.0.2-SNAPSHOT:compile\n",[53,42485,42486,42488,42490,42493,42495],{"class":55,"line":2087},[53,42487,42347],{"class":82},[53,42489,2665],{"class":389},[53,42491,42492],{"class":389}," |",[53,42494,42379],{"class":59},[53,42496,42497],{"class":63}," com.example:dms_client:jar:1.0.2:compile\n",[53,42499,42500,42502,42504,42506,42508,42510],{"class":55,"line":2092},[53,42501,42347],{"class":82},[53,42503,2665],{"class":389},[53,42505,42492],{"class":389},[53,42507,42492],{"class":389},[53,42509,42352],{"class":59},[53,42511,42512],{"class":63}," org.apache.axis2:axis2:jar:1.1.1:compile\n",[53,42514,42515,42517,42519,42521,42523,42526],{"class":55,"line":2097},[53,42516,42347],{"class":82},[53,42518,2665],{"class":389},[53,42520,42492],{"class":389},[53,42522,42492],{"class":389},[53,42524,42525],{"class":59}," +-",[53,42527,42528],{"class":63}," org.apache.ws.commons.axiom:axiom-dom:jar:1.2:compile\n",[53,42530,42531,42533,42535,42537,42539,42541],{"class":55,"line":2103},[53,42532,42347],{"class":82},[53,42534,2665],{"class":389},[53,42536,42492],{"class":389},[53,42538,42492],{"class":389},[53,42540,42525],{"class":59},[53,42542,42543],{"class":63}," org.apache.ws.commons.axiom:axiom-impl:jar:1.2:compile\n",[53,42545,42546,42548,42550,42552,42554,42556],{"class":55,"line":2109},[53,42547,42347],{"class":82},[53,42549,2665],{"class":389},[53,42551,42492],{"class":389},[53,42553,42492],{"class":389},[53,42555,42525],{"class":59},[53,42557,42558],{"class":63}," ant:ant:jar:1.6.5:compile\n",[53,42560,42561,42563,42565,42567,42569,42571],{"class":55,"line":2115},[53,42562,42347],{"class":82},[53,42564,2665],{"class":389},[53,42566,42492],{"class":389},[53,42568,42492],{"class":389},[53,42570,42525],{"class":59},[53,42572,42573],{"class":63}," woodstox:wstx-asl:jar:3.0.1:compile\n",[53,42575,42576,42578,42580,42582,42584,42586],{"class":55,"line":2120},[53,42577,42347],{"class":82},[53,42579,2665],{"class":389},[53,42581,42492],{"class":389},[53,42583,42492],{"class":389},[53,42585,42525],{"class":59},[53,42587,42588],{"class":63}," org.apache.ws.commons.schema:XmlSchema:jar:1.2:compile\n",[53,42590,42591,42593,42595,42597,42599,42601],{"class":55,"line":2946},[53,42592,42347],{"class":82},[53,42594,2665],{"class":389},[53,42596,42492],{"class":389},[53,42598,42492],{"class":389},[53,42600,42525],{"class":59},[53,42602,42603],{"class":63}," annogen:annogen:jar:0.1.0:compile\n",[53,42605,42606,42608,42610,42612,42614,42616],{"class":55,"line":2952},[53,42607,42347],{"class":82},[53,42609,2665],{"class":389},[53,42611,42492],{"class":389},[53,42613,42492],{"class":389},[53,42615,42525],{"class":59},[53,42617,42618],{"class":63}," commons-httpclient:commons-httpclient:jar:3.0.1:compile\n",[53,42620,42621,42623,42625,42627,42629,42632,42634],{"class":55,"line":2964},[53,42622,42347],{"class":82},[53,42624,2665],{"class":389},[53,42626,42492],{"class":389},[53,42628,42492],{"class":389},[53,42630,42631],{"class":389}," |",[53,42633,42352],{"class":59},[53,42635,42636],{"class":63}," commons-codec:commons-codec:jar:1.2:compile\n",[53,42638,42639,42641,42643,42645,42647,42649],{"class":55,"line":2973},[53,42640,42347],{"class":82},[53,42642,2665],{"class":389},[53,42644,42492],{"class":389},[53,42646,42492],{"class":389},[53,42648,42525],{"class":59},[53,42650,42651],{"class":63}," org.apache.httpcomponents:jakarta-httpcore:jar:4.0-alpha2:compile\n",[53,42653,42654,42656,42658,42660,42662,42664],{"class":55,"line":2979},[53,42655,42347],{"class":82},[53,42657,2665],{"class":389},[53,42659,42492],{"class":389},[53,42661,42492],{"class":389},[53,42663,42525],{"class":59},[53,42665,42666],{"class":63}," wsdl4j:wsdl4j:jar:1.6.1:compile\n",[53,42668,42669,42671,42673,42675,42677,42679],{"class":55,"line":2990},[53,42670,42347],{"class":82},[53,42672,2665],{"class":389},[53,42674,42492],{"class":389},[53,42676,42492],{"class":389},[53,42678,42525],{"class":59},[53,42680,42681],{"class":63}," backport-util-concurrent:backport-util-concurrent:jar:2.2:compile\n",[53,42683,42684,42686,42688,42690,42692,42694],{"class":55,"line":10443},[53,42685,42347],{"class":82},[53,42687,2665],{"class":389},[53,42689,42492],{"class":389},[53,42691,42492],{"class":389},[53,42693,42525],{"class":59},[53,42695,42696],{"class":63}," org.apache.ws.commons.neethi:neethi:jar:2.0:compile\n",[53,42698,42699,42701,42703,42705,42707,42710],{"class":55,"line":26443},[53,42700,42347],{"class":82},[53,42702,2665],{"class":389},[53,42704,42492],{"class":389},[53,42706,42492],{"class":389},[53,42708,42709],{"class":59}," \\-",[53,42711,42712],{"class":63}," org.apache.woden:woden-impl-om:jar:1.0M8:compile\n",[53,42714,42715,42717,42719,42721,42723,42726],{"class":55,"line":26448},[53,42716,42347],{"class":82},[53,42718,2665],{"class":389},[53,42720,42492],{"class":389},[53,42722,42492],{"class":389},[53,42724,42725],{"class":59}," +-",[53,42727,42728],{"class":63}," org.apache.woden:woden-api:jar:1.0M8:compile\n",[53,42730,42731,42733,42735,42737,42739,42741],{"class":55,"line":26453},[53,42732,42347],{"class":82},[53,42734,2665],{"class":389},[53,42736,42492],{"class":389},[53,42738,42492],{"class":389},[53,42740,42725],{"class":59},[53,42742,42743],{"class":63}," org.apache.ant:ant:jar:1.7.0:compile\n",[53,42745,42746,42748,42750,42752,42754,42757,42759],{"class":55,"line":26458},[53,42747,42347],{"class":82},[53,42749,2665],{"class":389},[53,42751,42492],{"class":389},[53,42753,42492],{"class":389},[53,42755,42756],{"class":389}," |",[53,42758,42352],{"class":59},[53,42760,42761],{"class":63}," org.apache.ant:ant-launcher:jar:1.7.0:compile\n",[53,42763,42764,42766,42768,42770,42772,42774],{"class":55,"line":26463},[53,42765,42347],{"class":82},[53,42767,2665],{"class":389},[53,42769,42492],{"class":389},[53,42771,42492],{"class":389},[53,42773,42725],{"class":59},[53,42775,42776],{"class":63}," xerces:xmlParserAPIs:jar:2.6.0:compile\n",[53,42778,42779,42781,42783,42785,42787,42790],{"class":55,"line":26468},[53,42780,42347],{"class":82},[53,42782,2665],{"class":389},[53,42784,42492],{"class":389},[53,42786,42492],{"class":389},[53,42788,42789],{"class":59}," \\-",[53,42791,42792],{"class":63}," org.codehaus.woodstox:wstx-asl:jar:3.2.4:runtime\n",[53,42794,42795,42797,42799,42801,42803],{"class":55,"line":26473},[53,42796,42347],{"class":82},[53,42798,2665],{"class":389},[53,42800,42492],{"class":389},[53,42802,42379],{"class":59},[53,42804,42805],{"class":63}," org.apache.ws.commons.axiom:axiom-api:jar:1.2:compile\n",[53,42807,42808,42810,42812,42814,42816,42818],{"class":55,"line":26478},[53,42809,42347],{"class":82},[53,42811,2665],{"class":389},[53,42813,42492],{"class":389},[53,42815,42492],{"class":389},[53,42817,42379],{"class":59},[53,42819,42820],{"class":63}," jaxen:jaxen:jar:1.1-beta-9:compile\n",[53,42822,42823,42825,42827,42829,42831,42833],{"class":55,"line":27490},[53,42824,42347],{"class":82},[53,42826,2665],{"class":389},[53,42828,42492],{"class":389},[53,42830,42492],{"class":389},[53,42832,42379],{"class":59},[53,42834,42835],{"class":63}," xml-apis:xml-apis:jar:1.3.03:compile\n",[53,42837,42838,42840,42842,42844,42846,42848],{"class":55,"line":27496},[53,42839,42347],{"class":82},[53,42841,2665],{"class":389},[53,42843,42492],{"class":389},[53,42845,42492],{"class":389},[53,42847,42352],{"class":59},[53,42849,42850],{"class":63}," stax:stax-api:jar:1.0.1:compile\n",[53,42852,42853,42855,42857,42859,42861],{"class":55,"line":27501},[53,42854,42347],{"class":82},[53,42856,2665],{"class":389},[53,42858,42492],{"class":389},[53,42860,42379],{"class":59},[53,42862,42863],{"class":63}," org.slf4j:jcl-over-slf4j:jar:1.6.1:compile\n",[53,42865,42866,42868,42870,42872,42874],{"class":55,"line":27507},[53,42867,42347],{"class":82},[53,42869,2665],{"class":389},[53,42871,42492],{"class":389},[53,42873,42379],{"class":59},[53,42875,42876],{"class":63}," org.apache.struts:struts-core:jar:1.3.8:compile\n",[53,42878,42879,42881,42883,42885,42887,42889],{"class":55,"line":27513},[53,42880,42347],{"class":82},[53,42882,2665],{"class":389},[53,42884,42492],{"class":389},[53,42886,42492],{"class":389},[53,42888,42352],{"class":59},[53,42890,42891],{"class":63}," commons-chain:commons-chain:jar:1.1:compile\n",[53,42893,42894,42896,42898,42900,42902],{"class":55,"line":27519},[53,42895,42347],{"class":82},[53,42897,2665],{"class":389},[53,42899,42492],{"class":389},[53,42901,42379],{"class":59},[53,42903,42904],{"class":63}," com.sun.xml:xml:jar:0.8.0:compile\n",[53,42906,42907,42909,42911,42913,42915],{"class":55,"line":27525},[53,42908,42347],{"class":82},[53,42910,2665],{"class":389},[53,42912,42492],{"class":389},[53,42914,42379],{"class":59},[53,42916,42917],{"class":63}," xmlc:xmlc-all-runtime:jar:2.2.8.1:compile\n",[53,42919,42920,42922,42924,42926,42928],{"class":55,"line":27530},[53,42921,42347],{"class":82},[53,42923,2665],{"class":389},[53,42925,42492],{"class":389},[53,42927,42379],{"class":59},[53,42929,42930],{"class":63}," xalan:xalan:jar:2.7.1:compile\n",[53,42932,42933,42935,42937,42939,42941,42943],{"class":55,"line":27536},[53,42934,42347],{"class":82},[53,42936,2665],{"class":389},[53,42938,42492],{"class":389},[53,42940,42492],{"class":389},[53,42942,42352],{"class":59},[53,42944,42945],{"class":63}," xalan:serializer:jar:2.7.1:compile\n",[53,42947,42948,42950,42952,42954,42956],{"class":55,"line":27542},[53,42949,42347],{"class":82},[53,42951,2665],{"class":389},[53,42953,42492],{"class":389},[53,42955,42352],{"class":59},[53,42957,42958],{"class":63}," com.lowagie:itext:jar:2.0.7:compile\n",[53,42960,42961,42963,42965,42967,42969],{"class":55,"line":27548},[53,42962,42347],{"class":82},[53,42964,2665],{"class":389},[53,42966,42492],{"class":389},[53,42968,42525],{"class":59},[53,42970,42971],{"class":63}," bouncycastle:bcmail-jdk14:jar:138:compile\n",[53,42973,42974,42976,42978,42980,42982],{"class":55,"line":27554},[53,42975,42347],{"class":82},[53,42977,2665],{"class":389},[53,42979,42492],{"class":389},[53,42981,42709],{"class":59},[53,42983,42984],{"class":63}," bouncycastle:bcprov-jdk14:jar:138:compile\n",[53,42986,42987,42989,42991,42993],{"class":55,"line":27559},[53,42988,42347],{"class":82},[53,42990,2665],{"class":389},[53,42992,42379],{"class":59},[53,42994,42995],{"class":63}," org.apache.xmlgraphics:fop:jar:0.95-1:compile\n",[53,42997,42998,43000,43002,43004,43006],{"class":55,"line":27565},[53,42999,42347],{"class":82},[53,43001,2665],{"class":389},[53,43003,42492],{"class":389},[53,43005,42379],{"class":59},[53,43007,43008],{"class":63}," org.apache.xmlgraphics:xmlgraphics-commons:jar:1.3.1:compile\n",[53,43010,43011,43013,43015,43017,43019],{"class":55,"line":27571},[53,43012,42347],{"class":82},[53,43014,2665],{"class":389},[53,43016,42492],{"class":389},[53,43018,42379],{"class":59},[53,43020,43021],{"class":63}," org.apache.xmlgraphics:batik-svg-dom:jar:1.7:compile\n",[53,43023,43024,43026,43028,43030,43032,43034],{"class":55,"line":27576},[53,43025,42347],{"class":82},[53,43027,2665],{"class":389},[53,43029,42492],{"class":389},[53,43031,42492],{"class":389},[53,43033,42379],{"class":59},[53,43035,43036],{"class":63}," org.apache.xmlgraphics:batik-anim:jar:1.7:compile\n",[53,43038,43039,43041,43043,43045,43047,43049],{"class":55,"line":27581},[53,43040,42347],{"class":82},[53,43042,2665],{"class":389},[53,43044,42492],{"class":389},[53,43046,42492],{"class":389},[53,43048,42379],{"class":59},[53,43050,43051],{"class":63}," org.apache.xmlgraphics:batik-css:jar:1.7:compile\n",[53,43053,43054,43056,43058,43060,43062,43064],{"class":55,"line":27586},[53,43055,42347],{"class":82},[53,43057,2665],{"class":389},[53,43059,42492],{"class":389},[53,43061,42492],{"class":389},[53,43063,42379],{"class":59},[53,43065,43066],{"class":63}," org.apache.xmlgraphics:batik-dom:jar:1.7:compile\n",[53,43068,43069,43071,43073,43075,43077,43079],{"class":55,"line":27591},[53,43070,42347],{"class":82},[53,43072,2665],{"class":389},[53,43074,42492],{"class":389},[53,43076,42492],{"class":389},[53,43078,42379],{"class":59},[53,43080,43081],{"class":63}," org.apache.xmlgraphics:batik-parser:jar:1.7:compile\n",[53,43083,43084,43086,43088,43090,43092,43094],{"class":55,"line":27596},[53,43085,42347],{"class":82},[53,43087,2665],{"class":389},[53,43089,42492],{"class":389},[53,43091,42492],{"class":389},[53,43093,42379],{"class":59},[53,43095,43096],{"class":63}," org.apache.xmlgraphics:batik-util:jar:1.7:compile\n",[53,43098,43100,43102,43104,43106,43108,43110],{"class":55,"line":43099},64,[53,43101,42347],{"class":82},[53,43103,2665],{"class":389},[53,43105,42492],{"class":389},[53,43107,42492],{"class":389},[53,43109,42352],{"class":59},[53,43111,43112],{"class":63}," xml-apis:xml-apis-ext:jar:1.3.04:compile\n",[53,43114,43116,43118,43120,43122,43124],{"class":55,"line":43115},65,[53,43117,42347],{"class":82},[53,43119,2665],{"class":389},[53,43121,42492],{"class":389},[53,43123,42379],{"class":59},[53,43125,43126],{"class":63}," org.apache.xmlgraphics:batik-bridge:jar:1.7:compile\n",[53,43128,43130,43132,43134,43136,43138,43140],{"class":55,"line":43129},66,[53,43131,42347],{"class":82},[53,43133,2665],{"class":389},[53,43135,42492],{"class":389},[53,43137,42492],{"class":389},[53,43139,42379],{"class":59},[53,43141,43142],{"class":63}," org.apache.xmlgraphics:batik-script:jar:1.7:compile\n",[53,43144,43146,43148,43150,43152,43154,43156],{"class":55,"line":43145},67,[53,43147,42347],{"class":82},[53,43149,2665],{"class":389},[53,43151,42492],{"class":389},[53,43153,42492],{"class":389},[53,43155,42352],{"class":59},[53,43157,43158],{"class":63}," org.apache.xmlgraphics:batik-xml:jar:1.7:compile\n",[53,43160,43162,43164,43166,43168,43170],{"class":55,"line":43161},68,[53,43163,42347],{"class":82},[53,43165,2665],{"class":389},[53,43167,42492],{"class":389},[53,43169,42379],{"class":59},[53,43171,43172],{"class":63}," org.apache.xmlgraphics:batik-awt-util:jar:1.7:compile\n",[53,43174,43176,43178,43180,43182,43184],{"class":55,"line":43175},69,[53,43177,42347],{"class":82},[53,43179,2665],{"class":389},[53,43181,42492],{"class":389},[53,43183,42379],{"class":59},[53,43185,43186],{"class":63}," org.apache.xmlgraphics:batik-gvt:jar:1.7:compile\n",[53,43188,43190,43192,43194,43196,43198],{"class":55,"line":43189},70,[53,43191,42347],{"class":82},[53,43193,2665],{"class":389},[53,43195,42492],{"class":389},[53,43197,42379],{"class":59},[53,43199,43200],{"class":63}," org.apache.xmlgraphics:batik-transcoder:jar:1.7:compile\n",[53,43202,43204,43206,43208,43210,43212,43214],{"class":55,"line":43203},71,[53,43205,42347],{"class":82},[53,43207,2665],{"class":389},[53,43209,42492],{"class":389},[53,43211,42492],{"class":389},[53,43213,42352],{"class":59},[53,43215,43216],{"class":63}," org.apache.xmlgraphics:batik-svggen:jar:1.7:compile\n",[53,43218,43220,43222,43224,43226,43228],{"class":55,"line":43219},72,[53,43221,42347],{"class":82},[53,43223,2665],{"class":389},[53,43225,42492],{"class":389},[53,43227,42379],{"class":59},[53,43229,43230],{"class":63}," org.apache.xmlgraphics:batik-extension:jar:1.7:compile\n",[53,43232,43234,43236,43238,43240,43242],{"class":55,"line":43233},73,[53,43235,42347],{"class":82},[53,43237,2665],{"class":389},[53,43239,42492],{"class":389},[53,43241,42379],{"class":59},[53,43243,43244],{"class":63}," org.apache.xmlgraphics:batik-ext:jar:1.7:compile\n",[53,43246,43248,43250,43252,43254,43256],{"class":55,"line":43247},74,[53,43249,42347],{"class":82},[53,43251,2665],{"class":389},[53,43253,42492],{"class":389},[53,43255,42379],{"class":59},[53,43257,43258],{"class":63}," commons-io:commons-io:jar:1.3.1:compile\n",[53,43260,43262,43264,43266,43268,43270],{"class":55,"line":43261},75,[53,43263,42347],{"class":82},[53,43265,2665],{"class":389},[53,43267,42492],{"class":389},[53,43269,42352],{"class":59},[53,43271,43272],{"class":63}," org.apache.avalon.framework:avalon-framework-impl:jar:4.3.1:compile\n",[53,43274,43276,43278,43280,43282],{"class":55,"line":43275},76,[53,43277,42347],{"class":82},[53,43279,2665],{"class":389},[53,43281,42379],{"class":59},[53,43283,43284],{"class":63}," org.apache.avalon.framework:avalon-framework-api:jar:4.3.1:compile\n",[53,43286,43288,43290,43292,43294],{"class":55,"line":43287},77,[53,43289,42347],{"class":82},[53,43291,2665],{"class":389},[53,43293,42379],{"class":59},[53,43295,43296],{"class":63}," poi:poi:jar:2.5.1-final-20040804:compile\n",[53,43298,43300,43302,43304,43306],{"class":55,"line":43299},78,[53,43301,42347],{"class":82},[53,43303,2665],{"class":389},[53,43305,42379],{"class":59},[53,43307,43308],{"class":63}," javax.servlet:servlet-api:jar:2.5:compile\n",[53,43310,43312,43314,43316,43318],{"class":55,"line":43311},79,[53,43313,42347],{"class":82},[53,43315,2665],{"class":389},[53,43317,42379],{"class":59},[53,43319,43320],{"class":63}," javax.mail:mail:jar:1.4.1:compile\n",[53,43322,43324,43326,43328,43330,43332],{"class":55,"line":43323},80,[53,43325,42347],{"class":82},[53,43327,2665],{"class":389},[53,43329,42492],{"class":389},[53,43331,42352],{"class":59},[53,43333,43334],{"class":63}," javax.activation:activation:jar:1.1.1:compile\n",[53,43336,43338,43340,43342,43344],{"class":55,"line":43337},81,[53,43339,42347],{"class":82},[53,43341,2665],{"class":389},[53,43343,42379],{"class":59},[53,43345,43346],{"class":63}," org.openoffice:jurt:jar:3.2.1:compile\n",[53,43348,43350,43352,43354,43356,43358],{"class":55,"line":43349},82,[53,43351,42347],{"class":82},[53,43353,2665],{"class":389},[53,43355,42492],{"class":389},[53,43357,42352],{"class":59},[53,43359,43360],{"class":63}," org.openoffice:ridl:jar:3.2.1:compile\n",[53,43362,43364,43366,43368,43370],{"class":55,"line":43363},83,[53,43365,42347],{"class":82},[53,43367,2665],{"class":389},[53,43369,42379],{"class":59},[53,43371,43372],{"class":63}," org.openoffice:unoil:jar:3.1.0:compile\n",[53,43374,43376,43378,43380,43382],{"class":55,"line":43375},84,[53,43377,42347],{"class":82},[53,43379,2665],{"class":389},[53,43381,42379],{"class":59},[53,43383,43384],{"class":63}," org.openoffice:juh:jar:3.1.0:compile\n",[53,43386,43388,43390,43392,43394],{"class":55,"line":43387},85,[53,43389,42347],{"class":82},[53,43391,2665],{"class":389},[53,43393,42352],{"class":59},[53,43395,43396],{"class":63}," ru.novosoft.dc:rtf2fo:jar:eval:compile\n",[53,43398,43400],{"class":55,"line":43399},86,[53,43401,43402],{"class":82},"[INFO] +- com.jgoodies:forms:jar:1.0.7:compile\n",[53,43404,43406],{"class":55,"line":43405},87,[53,43407,43408],{"class":82},"[INFO] +- xerces:xercesImpl:jar:2.4.0:compile\n",[53,43410,43412],{"class":55,"line":43411},88,[53,43413,43414],{"class":82},"[INFO] +- com.oracle:ojdbc5:jar:11.1.0.6.0:compile\n",[53,43416,43418],{"class":55,"line":43417},89,[53,43419,43420],{"class":82},"[INFO] +- javax.help:javahelp:jar:2.0.02:compile\n",[53,43422,43424],{"class":55,"line":43423},90,[53,43425,43426],{"class":82},"[INFO] +- com.example:custom-swing-framework:jar:1.1.2-SNAPSHOT:compile\n",[53,43428,43430,43432,43434,43436],{"class":55,"line":43429},91,[53,43431,42347],{"class":82},[53,43433,2665],{"class":389},[53,43435,42379],{"class":59},[53,43437,43438],{"class":63}," com.whirlycott:whirlycache:jar:0.7.1:compile\n",[53,43440,43442,43444,43446,43448,43450],{"class":55,"line":43441},92,[53,43443,42347],{"class":82},[53,43445,2665],{"class":389},[53,43447,42492],{"class":389},[53,43449,42379],{"class":59},[53,43451,43452],{"class":63}," commons-collections:commons-collections:jar:3.1:compile\n",[53,43454,43456,43458,43460,43462,43464],{"class":55,"line":43455},93,[53,43457,42347],{"class":82},[53,43459,2665],{"class":389},[53,43461,42492],{"class":389},[53,43463,42379],{"class":59},[53,43465,43466],{"class":63}," jdom:jdom:jar:1.0:compile\n",[53,43468,43470,43472,43474,43476,43478],{"class":55,"line":43469},94,[53,43471,42347],{"class":82},[53,43473,2665],{"class":389},[53,43475,42492],{"class":389},[53,43477,42352],{"class":59},[53,43479,43480],{"class":63}," concurrent:concurrent:jar:1.3.4:compile\n",[53,43482,43484,43486,43488,43490],{"class":55,"line":43483},95,[53,43485,42347],{"class":82},[53,43487,2665],{"class":389},[53,43489,42379],{"class":59},[53,43491,43492],{"class":63}," ehcache:ehcache:jar:0.9:compile\n",[53,43494,43496,43498,43500,43502],{"class":55,"line":43495},96,[53,43497,42347],{"class":82},[53,43499,2665],{"class":389},[53,43501,42379],{"class":59},[53,43503,43504],{"class":63}," org.enhydra.xmlc:xmlc:jar:2.2.7.1:compile\n",[53,43506,43508,43510,43512,43514],{"class":55,"line":43507},97,[53,43509,42347],{"class":82},[53,43511,2665],{"class":389},[53,43513,42379],{"class":59},[53,43515,43516],{"class":63}," com.jgoodies:looks:jar:2.2.2:compile\n",[53,43518,43520,43522,43524,43526],{"class":55,"line":43519},98,[53,43521,42347],{"class":82},[53,43523,2665],{"class":389},[53,43525,42379],{"class":59},[53,43527,43528],{"class":63}," org.swinglabs:swingx:jar:1.6.1:compile\n",[53,43530,43532,43534,43536,43538,43540],{"class":55,"line":43531},99,[53,43533,42347],{"class":82},[53,43535,2665],{"class":389},[53,43537,42492],{"class":389},[53,43539,42379],{"class":59},[53,43541,43542],{"class":63}," com.jhlabs:filters:jar:2.0.235:compile\n",[53,43544,43546,43548,43550,43552,43554],{"class":55,"line":43545},100,[53,43547,42347],{"class":82},[53,43549,2665],{"class":389},[53,43551,42492],{"class":389},[53,43553,42352],{"class":59},[53,43555,43556],{"class":63}," org.swinglabs:swing-worker:jar:1.1:compile\n",[53,43558,43560,43562,43564,43566],{"class":55,"line":43559},101,[53,43561,42347],{"class":82},[53,43563,2665],{"class":389},[53,43565,42352],{"class":59},[53,43567,43568],{"class":63}," struts:struts:jar:1.2.9:compile\n",[53,43570,43572,43574,43576,43578],{"class":55,"line":43571},102,[53,43573,42347],{"class":82},[53,43575,2665],{"class":389},[53,43577,42525],{"class":59},[53,43579,43580],{"class":63}," commons-beanutils:commons-beanutils:jar:1.7.0:compile\n",[53,43582,43584,43586,43588,43590],{"class":55,"line":43583},103,[53,43585,42347],{"class":82},[53,43587,2665],{"class":389},[53,43589,42525],{"class":59},[53,43591,43592],{"class":63}," commons-digester:commons-digester:jar:1.6:compile\n",[53,43594,43596,43598,43600,43602],{"class":55,"line":43595},104,[53,43597,42347],{"class":82},[53,43599,2665],{"class":389},[53,43601,42525],{"class":59},[53,43603,43604],{"class":63}," commons-fileupload:commons-fileupload:jar:1.0:compile\n",[53,43606,43608,43610,43612,43614],{"class":55,"line":43607},105,[53,43609,42347],{"class":82},[53,43611,2665],{"class":389},[53,43613,42525],{"class":59},[53,43615,43616],{"class":63}," commons-validator:commons-validator:jar:1.1.4:compile\n",[53,43618,43620,43622,43624,43626],{"class":55,"line":43619},106,[53,43621,42347],{"class":82},[53,43623,2665],{"class":389},[53,43625,42525],{"class":59},[53,43627,43628],{"class":63}," oro:oro:jar:2.0.7:compile\n",[53,43630,43632,43634,43636,43638],{"class":55,"line":43631},107,[53,43633,42347],{"class":82},[53,43635,2665],{"class":389},[53,43637,42709],{"class":59},[53,43639,43640],{"class":63}," antlr:antlr:jar:2.7.2:compile\n",[53,43642,43644],{"class":55,"line":43643},108,[53,43645,43646],{"class":82},"[INFO] +- junit:junit:jar:4.8.2:test\n",[53,43648,43650,43652,43655],{"class":55,"line":43649},109,[53,43651,42347],{"class":82},[53,43653,43654],{"class":89},"\\-",[53,43656,43657],{"class":82}," org.slf4j:slf4j-api:jar:1.6.1:compile\n",[53,43659,43661],{"class":55,"line":43660},110,[53,43662,43663],{"class":82},"[INFO] ------------------------------------------------------------------------\n",[53,43665,43667],{"class":55,"line":43666},111,[53,43668,43669],{"class":82},"[INFO] BUILD SUCCESSFUL\n",[53,43671,43673],{"class":55,"line":43672},112,[53,43674,43663],{"class":82},[53,43676,43678],{"class":55,"line":43677},113,[53,43679,43680],{"class":82},"[INFO] Total time: 2 seconds\n",[53,43682,43684],{"class":55,"line":43683},114,[53,43685,43686],{"class":82},"[INFO] Finished at: Tue Jun 28 14:10:29 CEST 2011\n",[53,43688,43690],{"class":55,"line":43689},115,[53,43691,43692],{"class":82},"[INFO] Final Memory: 22M/257M\n",[53,43694,43696],{"class":55,"line":43695},116,[53,43697,43663],{"class":82},[18,43699,43700],{},"As described before the elimination of the “rogue” logging class wasn’t a big problem. But we hadn’t reached the finish\nline yet.",[18,43702,43703,43704,43709,43710,43715,43716,43721,43722,43727,43728,43733,43734,43739],{},"The final application is being assembled into one big EAR file which contains a lot\nof ",[585,43705,43708],{"href":43706,"rel":43707},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch5.chapter.html",[589],"EJBs",", SAR\nfiles (",[585,43711,43714],{"href":43712,"rel":43713},"http://community.jboss.org/wiki/ServiceArchive",[589],"JBoss Service Archives","), and\nthree ",[585,43717,43720],{"href":43718,"rel":43719},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch9.chapt.html",[589],"WARs"," (web applications). By default JBoss uses\nits unified class loader (well described in the JBoss Admin Guide in\nsection ",[585,43723,43726],{"href":43724,"rel":43725},"http://docs.jboss.org/jbossas/jboss4guide/r4/html/ch2.chapter.html#d0e2490",[589],"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 ",[585,43729,43732],{"href":43730,"rel":43731},"http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html",[589],"Apache Tomcat 5.5 Class Loader HOW-TO",", which\nbasically isolates the deployed web applications from each other. The wiki article\non ",[585,43735,43738],{"href":43736,"rel":43737},"http://community.jboss.org/wiki/JBossClassLoadingUseCases",[589],"Advanced JBoss Class Loading"," is also very informative in\nthis context.",[18,43741,43742,43743,43748,43749,43754],{},"Since JBoss 4.0.3.SP1 is using Apache Tomcat 5.5 as servlet container it\nprovides ",[585,43744,43747],{"href":43745,"rel":43746},"http://community.jboss.org/wiki/UseJBossWebLoader",[589],"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 ",[585,43750,43753],{"href":43751,"rel":43752},"http://community.jboss.org/wiki/EnableClassloaderLogging",[589],"logging to DEBUG"," and engage!",[18,43756,43757,43758,43763,43764,43769,43770,986],{},"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. ",[585,43759,43762],{"href":43760,"rel":43761},"http://www.slf4j.org/apidocs/org/slf4j/spi/LoggerFactoryBinder.html",[589],"org.slf4j.spi.LoggerFactoryBinder",") had already\nbeen loaded from another repository. If you recall the details of Apache\nTomcat’s ",[585,43765,43768],{"href":43766,"rel":43767},"http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html#Overview",[589],"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: ",[27,43771,43772],{},"FilteredPackages",[18,43774,43775],{},"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.",[43,43777,43779],{"className":1980,"code":43778,"language":1982,"meta":48,"style":48}," \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",[50,43780,43781,43786,43791,43796,43801,43806,43811,43816,43821],{"__ignoreMap":48},[53,43782,43783],{"class":55,"line":56},[53,43784,43785],{}," \u003C!-- The list of package prefixes that should not be loaded without\n",[53,43787,43788],{"class":55,"line":86},[53,43789,43790],{}," delegating to the parent class loader before trying the web app\n",[53,43792,43793],{"class":55,"line":126},[53,43794,43795],{}," class loader. The packages listed here are those tha are used by\n",[53,43797,43798],{"class":55,"line":163},[53,43799,43800],{}," the web container implementation and cannot be overriden. The format\n",[53,43802,43803],{"class":55,"line":186},[53,43804,43805],{}," is a comma separated list of the package names. There cannot be any\n",[53,43807,43808],{"class":55,"line":221},[53,43809,43810],{}," whitespace between the package prefixes.\n",[53,43812,43813],{"class":55,"line":242},[53,43814,43815],{}," This setting only applies when UseJBossWebLoader=false.\n",[53,43817,43818],{"class":55,"line":273},[53,43819,43820],{}," -->\n",[53,43822,43823],{"class":55,"line":279},[53,43824,43825],{}," \u003Cattribute name=\"FilteredPackages\">javax.servlet,org.slf4j\u003C/attribute>\n",[2352,43827,43829],{"id":43828},"lessons-learned","Lessons learned",[577,43831,43832,43845,43848,43851,43854,43857,43884],{},[580,43833,43834,43835,43838,43839,43844],{},"If you’re starting a new project choose exactly one logging framework and stick with it. I recommend\nusing ",[585,43836,42103],{"href":42101,"rel":43837},[589]," for its stable API and ",[585,43840,43843],{"href":43841,"rel":43842},"http://logback.qos.ch/",[589],"Logback"," as backend at the\nmoment.",[580,43846,43847],{},"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.",[580,43849,43850],{},"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.",[580,43852,43853],{},"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.",[580,43855,43856],{},"Use the tools your IDE provides. It makes refactoring so much easier if you know what your IDE is capable of.",[580,43858,43859,43860,99,43865,3566,43870,3566,43875,3566,43880,12136],{},"Never underestimate the grief that class loading issues can cause\nyou. ",[585,43861,43864],{"href":43862,"rel":43863},"http://onjava.com/lpt/a/5586",[589],"Really",[585,43866,43869],{"href":43867,"rel":43868},"http://www.devx.com/Java/Article/31614/1954?pf=true",[589],"read",[585,43871,43874],{"href":43872,"rel":43873},"http://onjava.com/lpt/a/4337",[589],"up",[585,43876,43879],{"href":43877,"rel":43878},"http://onjava.com/lpt/a/5795",[589],"on",[585,43881,13490],{"href":43882,"rel":43883},"http://www.theserverside.com/news/1364680/Understanding-J2EE-Application-Server-ClassLoading-Architectures",[589],[580,43885,43886],{},"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.",[18,43888,43889,43890,43893],{},"How about you? Did you experience similar stories with large legacy applications? What did you take from it? We’re\nthrilled to hear ",[27,43891,43892],{},"your"," “Development War Stories” in the comments!",[2352,43895,43897],{"id":43896},"attributions","Attributions",[577,43899,43900],{},[580,43901,43902,41813,43907,99,43912],{},[585,43903,43906],{"href":43904,"rel":43905},"http://www.flickr.com/photos/lorenjavier/3537572809/",[589],"Seven Dwarves “Homeward Bound” statue by Jim Shore as soon from the China Closet on Main Street",[585,43908,43911],{"href":43909,"rel":43910},"http://www.flickr.com/photos/lorenjavier/",[589],"Loren Javier",[585,43913,43916],{"href":43914,"rel":43915},"http://creativecommons.org/licenses/by-nd/2.0/",[589],"CC BY-ND 2.0",[607,43918,43919],{},"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":48,"searchDepth":86,"depth":86,"links":43921},[43922,43923],{"id":43828,"depth":86,"text":43829},{"id":43896,"depth":86,"text":43897},[613],"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":42054,"description":43931},"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",[41310,43934,43935,290,34321,43936,43937,26512],"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…","ppWPHxzoN11zneoOW2aVj0eAXHnxYFD4vHMnjUtyqHs",{"id":43941,"title":43942,"author":43943,"body":43944,"category":43974,"date":43975,"description":43976,"extension":617,"link":43977,"meta":43978,"navigation":499,"path":43979,"seo":43980,"slug":43981,"stem":43982,"tags":43983,"teaser":43986,"__hash__":43987},"blog/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter.md","Eine Kochmuddi für hungrige Synyx-Mitarbeiter",[11619],{"type":11,"value":43945,"toc":43972},[43946,43949,43956,43959,43962,43965],[14,43947,43942],{"id":43948},"eine-kochmuddi-für-hungrige-synyx-mitarbeiter",[18,43950,43951,43955],{},[1773,43952],{"alt":43953,"src":43954},"\"Kochmellie beim ersten Synyx-Einsatz\"","https://media.synyx.de/uploads//2011/06/mellie.jpg","\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?“",[18,43957,43958],{},"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.",[18,43960,43961],{},"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.",[18,43963,43964],{},"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!",[18,43966,43967],{},[585,43968,43971],{"href":43969,"rel":43970},"http://www.hypersmash.com/dreamhost/",[589],"Dreamhost promotion code",{"title":48,"searchDepth":86,"depth":86,"links":43973},[],[614],"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":43942,"description":43955},"eine-kochmuddi-fur-hungrige-synyx-mitarbeiter","blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter",[43984,43985,5743],"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…","K-fyhzn_D2LovH6cZp_mKMDWsSCoE8fQIZqnRuFxyTs",{"id":43989,"title":43990,"author":43991,"body":43992,"category":44024,"date":44025,"description":44026,"extension":617,"link":44027,"meta":44028,"navigation":499,"path":44029,"seo":44030,"slug":43996,"stem":44032,"tags":44033,"teaser":44034,"__hash__":44035},"blog/blog/opencms-modul-integriert-externe-blogs.md","OpenCms Modul integriert externe Blogs",[11619],{"type":11,"value":43993,"toc":44022},[43994,43997,44009],[14,43995,43990],{"id":43996},"opencms-modul-integriert-externe-blogs",[18,43998,43999,44003,44004,44008],{},[1773,44000],{"alt":44001,"src":44002},"\"opencms-modul\"","https://media.synyx.de/uploads//2011/05/opencms-modul1.jpg","\nDie Synyx Hompage wird mittels dem Content Management System ",[585,44005,40251],{"href":44006,"rel":44007},"http://www.opencms.org/de/",[589]," 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.",[18,44010,3736,44011,44015,44016,44021],{},[585,44012,44014],{"href":5449,"rel":44013},[589],"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 ",[585,44017,44020],{"href":44018,"rel":44019},"https://synyx.de/de/",[589],"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":48,"searchDepth":86,"depth":86,"links":44023},[],[614],"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":43990,"description":44031},"\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",[37693,5743],"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…","mQZ_7ZI98DNNIvwJYSmzfQgUtfpYNf_WQ2vltd5u4Dc",{"id":44037,"title":44038,"author":44039,"body":44040,"category":44100,"date":44101,"description":44102,"extension":617,"link":44103,"meta":44104,"navigation":499,"path":44105,"seo":44106,"slug":44044,"stem":44108,"tags":44109,"teaser":44110,"__hash__":44111},"blog/blog/solr-as-search-engine-for-opencms.md","Solr as search engine for OpenCms",[41317],{"type":11,"value":44041,"toc":44098},[44042,44045,44060,44074,44077,44086,44095],[14,44043,44038],{"id":44044},"solr-as-search-engine-for-opencms",[18,44046,44047,44048,44053,44054,44059],{},"Matching the time of my talk at this years ",[585,44049,44052],{"href":44050,"rel":44051},"http://www.opencms-days.org/en/index.html",[589],"OpenCms Days"," we released our\nmodule for integrating ",[585,44055,44058],{"href":44056,"rel":44057},"https://github.com/synyx/opencms-solr-module",[589],"Solr with OpenCms",". A few days have passed now and\nwe had the time to polish the documentation and some aspects of the module.",[18,44061,44062,44067,44068,44073],{},[585,44063,44066],{"href":44064,"rel":44065},"http://lucene.apache.org/solr/",[589],"Solr"," is a search server that is based on the de facto standard for indexing in\nJava, ",[585,44069,44072],{"href":44070,"rel":44071},"http://lucene.apache.org/",[589],"Apache Lucene",". It provides an abstraction layer above the low level details of\nindexing and adds some useful features like facetting and synonyms.",[18,44075,44076],{},"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.",[18,44078,44079,44080,44085],{},"We provide two ways to try the module: You can\neither ",[585,44081,44084],{"href":44082,"rel":44083},"https://github.com/Synyx/opencms-solr-module",[589],"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.",[18,44087,44088,44089,44094],{},"Another way is\nthe ",[585,44090,44093],{"href":44091,"rel":44092},"https://github.com/synyx/opencms-solr-module/wiki/Integrating-Solr-into-an-existing-application",[589],"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.",[18,44096,44097],{},"Please feel free to post any questions or feature requests to the issue tracker.",{"title":48,"searchDepth":86,"depth":86,"links":44099},[],[613,996],"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":44038,"description":44107},"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",[12072,37693,37695],"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":44113,"title":44114,"author":44115,"body":44116,"category":44135,"date":44136,"description":44137,"extension":617,"link":44138,"meta":44139,"navigation":499,"path":44140,"seo":44141,"slug":44142,"stem":44143,"tags":44144,"teaser":44149,"__hash__":44150},"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",[40236],{"type":11,"value":44117,"toc":44133},[44118,44121,44124,44127],[14,44119,44114],{"id":44120},"being-open-source-instead-of-just-open-sourcing-or-open-source-is-not-just-about-the-license",[18,44122,44123],{},"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.",[18,44125,44126],{},"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.",[18,44128,44129],{},[1773,44130],{"alt":48,"src":44131,"title":44132},"https://media.synyx.de/uploads//2011/05/chili-300x199.jpg","chili",{"title":48,"searchDepth":86,"depth":86,"links":44134},[],[613,996],"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":44114,"description":44123},"opensource-is-not-just-about-the-license","blog/opensource-is-not-just-about-the-license",[44145,3059,44146,22829,44147,12072,38737,44148],"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":44152,"title":44153,"author":44154,"body":44155,"category":44286,"date":44287,"description":44288,"extension":617,"link":44289,"meta":44290,"navigation":499,"path":44291,"seo":44292,"slug":44159,"stem":44294,"tags":44295,"teaser":44296,"__hash__":44297},"blog/blog/opencms-days-2011-in-retrospect.md","OpenCms Days 2011 in retrospect",[41317],{"type":11,"value":44156,"toc":44284},[44157,44160,44168,44183,44186,44189,44204,44213,44234,44260,44278,44281],[14,44158,44153],{"id":44159},"opencms-days-2011-in-retrospect",[18,44161,44162,44163,44167],{},"From May 9 2011 to May 10 the ",[585,44164,44166],{"href":44050,"rel":44165},[589],"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.",[18,44169,44170,44171,44176,44177,44182],{},"The conference again took place at ",[585,44172,44175],{"href":44173,"rel":44174},"http://www.komed.de/",[589],"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 ",[585,44178,44181],{"href":44179,"rel":44180},"http://www.alkacon.com/en/",[589],"Alkacon"," employees, mainly targeting new features of OpenCms 8.",[18,44184,44185],{},"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.",[18,44187,44188],{},"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.",[18,44190,44191,44192,44197,44198,44203],{},"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 ",[585,44193,44196],{"href":44194,"rel":44195},"http://www.softwareag.com/",[589],"Software AG"," and the\nmodules they build together with ",[585,44199,44202],{"href":44200,"rel":44201},"http://www.componio.net/",[589],"Componio",". Quite impressive how much work they seem to have\ninvested into implementing a lot of modules and extensions.",[18,44205,44206,44207,44212],{},"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 (",[585,44208,44211],{"href":44209,"rel":44210},"http://www.gnu.org/philosophy/free-sw.html",[589],"as in beer",")\nbeer :).",[18,44214,44215,44216,44221,44222,44227,44228,44233],{},"On the second day I went only to talks in the showcase track. Selver Softic of ",[585,44217,44220],{"href":44218,"rel":44219},"http://www.infonova.com/",[589],"Infonova","\npresented an Open Source module that they are using for implementing\nthe ",[585,44223,44226],{"href":44224,"rel":44225},"http://telekom.at/",[589],"website of Austrian telekom",". The module makes it possible to\nuse ",[585,44229,44232],{"href":44230,"rel":44231},"http://velocity.apache.org/",[589],"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.",[18,44235,44236,44237,44242,44243,44248,44249,44254,44255,44259],{},"Another interesting Open Source module was presented by Rich Cooley of ",[585,44238,44241],{"href":44239,"rel":44240},"http://www.northps.com/",[589],"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 ",[585,44244,44247],{"href":44245,"rel":44246},"http://tika.apache.org/",[589],"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 ",[585,44250,44253],{"href":44251,"rel":44252},"http://www.chop.edu/",[589],"for the glossary on the website they implemented the module for",". This is something where\nour ",[585,44256,44258],{"href":44056,"rel":44257},[589],"Solr integration"," would probably also be a benefit.",[18,44261,44262,44263,44266,44267,44271,44272,44277],{},"Speaking of Solr, did I miss something? Oh yes, there was also my talk on our integration\nof ",[585,44264,44066],{"href":44064,"rel":44265},[589]," with OpenCms which is\navailable ",[585,44268,44270],{"href":44056,"rel":44269},[589],"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 ",[585,44273,44276],{"href":44274,"rel":44275},"https://github.com/synyx/opencms-solr-module/wiki",[589],"documentation of the module"," in\nthe next days and probably write another blog post going more into some of the details.",[18,44279,44280],{},"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.",[18,44282,44283],{},"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":48,"searchDepth":86,"depth":86,"links":44285},[],[996],"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":44153,"description":44293},"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",[3491,37693,37695],"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":44299,"title":44300,"author":44301,"body":44302,"category":44340,"date":44341,"description":48,"extension":617,"link":44342,"meta":44343,"navigation":499,"path":44344,"seo":44345,"slug":44306,"stem":44346,"tags":44347,"teaser":44348,"__hash__":44349},"blog/blog/vortrag-von-synyx-entwickler-bei-den-opencms-days.md","Vortrag von Synyx Entwickler bei den OpenCms Days",[11619],{"type":11,"value":44303,"toc":44338},[44304,44307,44313,44335],[14,44305,44300],{"id":44306},"vortrag-von-synyx-entwickler-bei-den-opencms-days",[18,44308,44309],{},[1773,44310],{"alt":44311,"src":44312},"\"Logo OpenCms Days 2011\"","https://media.synyx.de/uploads//2011/05/logo_opencmsdays_2011.jpg",[18,44314,44315,44316,44319,44320,44325,44326,44330,44331,44334],{},"Synyx wird wieder die ",[585,44317,44052],{"href":44050,"rel":44318},[589]," besuchen. Dieses Mal hält Florian Hopf\neinen ",[585,44321,44324],{"href":44322,"rel":44323},"http://www.opencms-days.org/en/program/sessions/session_s5.html",[589],"Vortrag"," über ein Open Source Modul für OpenCms.\nDieses Modul integriert Solr in OpenCms. Der Such Server ",[585,44327,44329],{"href":44064,"rel":44328},[589],"Apache Solr"," setzt auf der\nbewährten IR-Bibliothek ",[585,44332,44072],{"href":44070,"rel":44333},[589]," auf.",[18,44336,44337],{},"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":48,"searchDepth":86,"depth":86,"links":44339},[],[614],"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":44300,"description":48},"blog/vortrag-von-synyx-entwickler-bei-den-opencms-days",[37693],"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…","XADFzJU3s_yaFy0cd1Oyf9zr9cRBs_ENE9PE52g_Aio",{"id":44351,"title":44352,"author":44353,"body":44354,"category":44382,"date":44383,"description":48,"extension":617,"link":44384,"meta":44385,"navigation":499,"path":44386,"seo":44387,"slug":44388,"stem":44389,"tags":44390,"teaser":44393,"__hash__":44394},"blog/blog/open-source-anwendungen-fur-die-community.md","Open Source Anwendungen für die Community",[11619],{"type":11,"value":44355,"toc":44380},[44356,44359,44365,44368,44371,44374],[14,44357,44352],{"id":44358},"open-source-anwendungen-für-die-community",[18,44360,44361],{},[1773,44362],{"alt":44363,"src":44364},"\"Open Source Projekte\"","https://media.synyx.de/uploads//2011/05/opensourceprojekte.jpg",[18,44366,44367],{},"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.",[18,44369,44370],{},"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.",[18,44372,44373],{},"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.",[18,44375,44376],{},[585,44377,44379],{"href":11995,"rel":44378},[589],"Link zur Homepage und den Tools",{"title":48,"searchDepth":86,"depth":86,"links":44381},[],[996],"2011-05-04T11:14:29","https://synyx.de/blog/open-source-anwendungen-fur-die-community/",{},"/blog/open-source-anwendungen-fur-die-community",{"title":44352,"description":48},"open-source-anwendungen-fur-die-community","blog/open-source-anwendungen-fur-die-community",[44391,12072,44392],"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…","WON2bBfNFLIuAo91jrjKh7Z6k2_Krd3_mioNFobU70c",{"id":44396,"title":44397,"author":44398,"body":44399,"category":44463,"date":44464,"description":44465,"extension":617,"link":44466,"meta":44467,"navigation":499,"path":44468,"seo":44469,"slug":44403,"stem":44471,"tags":44472,"teaser":44473,"__hash__":44474},"blog/blog/maven-and-opencms.md","Maven and OpenCms",[41317],{"type":11,"value":44400,"toc":44461},[44401,44404,44439,44442],[14,44402,44397],{"id":44403},"maven-and-opencms",[18,44405,44406,44407,44412,44413,44417,44418,44423,44424,44429,44430,44433,44434,986],{},"I ",[585,44408,44411],{"href":44409,"rel":44410},"http://blog.synyx.de/2010/11/netbeans-and-opencms/",[589],"previously mentioned"," that setting up a development environment\nfor ",[585,44414,40251],{"href":44415,"rel":44416},"http://opencms.org/",[589]," can be quite hard. Besides\nour ",[585,44419,44422],{"href":44420,"rel":44421},"https://github.com/synyx/opencms-netbeans-module",[589],"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 ",[585,44425,44428],{"href":44426,"rel":44427},"http://www.eurelis.com",[589],"Eurelis",". Today we ",[27,44431,44432],{},"released version 1.0"," which is now available under the terms of\nthe ",[585,44435,44438],{"href":44436,"rel":44437},"http://www.fsf.org/licensing/licenses/lgpl.txt",[589],"GNU Lesser General Public License",[18,44440,44441],{},"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.",[18,44443,44444,44445,44449,44450,44455,44456,986],{},"You can find the projects homepage including documentation how to get started and how everything\nworks ",[585,44446,10905],{"href":44447,"rel":44448},"https://github.com/synyx/maven-opencms/",[589],". You should not get problems to get up and running after reading\nthe information from the projects ",[585,44451,44454],{"href":44452,"rel":44453},"https://github.com/synyx/maven-opencms/wiki",[589],"Wiki",". If you’re having any trouble or\nfeature request feel free to contact us or create a ticket in the\nprojects ",[585,44457,44460],{"href":44458,"rel":44459},"https://github.com/synyx/maven-opencms/issues",[589],"issue tracker",{"title":48,"searchDepth":86,"depth":86,"links":44462},[],[613,996],"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":44397,"description":44470},"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",[10977,37693],"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":44476,"title":44477,"author":44478,"body":44480,"category":44522,"date":44523,"description":44524,"extension":617,"link":44525,"meta":44526,"navigation":499,"path":44527,"seo":44528,"slug":44484,"stem":44530,"tags":44531,"teaser":44532,"__hash__":44533},"blog/blog/synyx-at-the-droidcon-2011.md","Synyx at the Droidcon 2011",[44479],"krupicka",{"type":11,"value":44481,"toc":44520},[44482,44485,44492,44505,44508,44511],[14,44483,44477],{"id":44484},"synyx-at-the-droidcon-2011",[18,44486,44487,44488,44491],{},"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. ",[573,44489,44490],{},"Which is absolutely a good thing!"," Nevertheless we want to show\nyou, that we are still pretty much alive.",[18,44493,44494,44495,44500,44501,44504],{},"While still working on some new articles, we are at this very moment in Berlin to visit\nthe ",[585,44496,44499],{"href":44497,"rel":44498},"https://www.droidcon.com/",[589],"Droidcon"," conference. From ",[573,44502,44503],{},"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.",[18,44506,44507],{},"We wish everyone attending a good time there!",[18,44509,44510],{},"— Florian Krupicka & Tobias Knell",[18,44512,44513,44514,44519],{},"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 ",[585,44515,44518],{"href":44516,"rel":44517},"http://www.10gen.com/conferences/mongoberlin2011",[589],"MongoBerlin"," conference on Friday, the 25th of\nMarch.",{"title":48,"searchDepth":86,"depth":86,"links":44521},[],[4516],"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":44477,"description":44529},"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",[8320,3491,4527],"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":44535,"title":44536,"author":44537,"body":44538,"category":44981,"date":44982,"description":44545,"extension":617,"link":44983,"meta":44984,"navigation":499,"path":44985,"seo":44986,"slug":44987,"stem":44988,"tags":44989,"teaser":44998,"__hash__":44999},"blog/blog/utilizing-git-to-dive-into-huge-code-bases.md","Utilizing Git to dive into huge code bases – Git SVN Tips",[40236],{"type":11,"value":44539,"toc":44979},[44540,44543,44546,44549,44552,44566,44569,44572,44575,44578,44581,44584,44587,44590,44593,44596,44599,44602,44605,44608,44611,44614,44617,44631,44661,44664,44667,44670,44684,44686,44700,44703,44706,44732,44735,44778,44781,44784,44787,44806,44834,44837,44840,44843,44846,44870,44873,44887,44914,44917,44920,44923,44926,44949,44957,44971,44974,44977],[14,44541,44536],{"id":44542},"utilizing-git-to-dive-into-huge-code-bases-git-svn-tips",[18,44544,44545],{},"Unfortunately there are still projects not on dvsc like git. That’s especially true",[18,44547,44548],{},"for enterprise customers which are at least stuck on Subversion if not worse.",[18,44550,44551],{},"So the first thing I do on new projects I join:",[43,44553,44555],{"className":13667,"code":44554,"language":13669,"meta":48,"style":48},"\n git svn clone -s svn-url\n\n",[50,44556,44557,44561],{"__ignoreMap":48},[53,44558,44559],{"class":55,"line":56},[53,44560,500],{"emptyLinePlaceholder":499},[53,44562,44563],{"class":55,"line":86},[53,44564,44565],{}," git svn clone -s svn-url\n",[18,44567,44568],{},"Or installing Git if I have to work on customer provided machines. That’s even more",[18,44570,44571],{},"important than the rest of a development environment like an IDE.",[18,44573,44574],{},"From experience I find it especially useful to experiment with new code basis",[18,44576,44577],{},"utilizing Git. Grown and big projects aren’t easy to understand architecturally and",[18,44579,44580],{},"implementation wise without digging deep. With the help of Git you can jump right",[18,44582,44583],{},"in, without fear and without messing everything up or having too much unrevertable",[18,44585,44586],{},"local changes. Just commit early and often! By doing it locally, in experimental",[18,44588,44589],{},"branches you can try and learn. Before you publish something to a wider audience",[18,44591,44592],{},"(svn) you can reorder, cherrypick and change everything or parts of it. Git is my",[18,44594,44595],{},"tool of choice to get my hands dirty with legacy code (new one too of course).",[18,44597,44598],{},"Some useful tips on how I use Git-SVN:",[18,44600,44601],{},"SVN history is linear, so you can’t use branches and merge the usual git-way without",[18,44603,44604],{},"thinking.",[18,44606,44607],{},"What often happens to me is that I implement a new feature, do some refactorings on",[18,44609,44610],{},"my way etc and an urgant bug report comes along. But I commited on master, don’t",[18,44612,44613],{},"want to push it to SVN yet since it’s not finished yet and might not be stable. What",[18,44615,44616],{},"to do? git svn dcommit would push all my local master commits to svn. The solution:",[43,44618,44620],{"className":13667,"code":44619,"language":13669,"meta":48,"style":48},"\n git branch featureA\n\n",[50,44621,44622,44626],{"__ignoreMap":48},[53,44623,44624],{"class":55,"line":56},[53,44625,500],{"emptyLinePlaceholder":499},[53,44627,44628],{"class":55,"line":86},[53,44629,44630],{}," git branch featureA\n",[43,44632,44634],{"className":13667,"code":44633,"language":13669,"meta":48,"style":48},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[50,44635,44636,44641,44646,44651,44656],{"__ignoreMap":48},[53,44637,44638],{"class":55,"line":56},[53,44639,44640],{},"\u003Ctt>\n",[53,44642,44643],{"class":55,"line":86},[53,44644,44645],{}," |svn | master\n",[53,44647,44648],{"class":55,"line":126},[53,44649,44650],{}," ---o---o---o---o---o---o---o---o---o---o\n",[53,44652,44653],{"class":55,"line":163},[53,44654,44655],{}," | featureA\n",[53,44657,44658],{"class":55,"line":186},[53,44659,44660],{},"\u003C/tt>\n",[18,44662,44663],{},"Now both branches featureA and master point to the latest commit. But we want master",[18,44665,44666],{},"to point to an earlier commit. Let’s say the last 10 commits aren’t in SVN yet and",[18,44668,44669],{},"the last 8 are experimental, so 2 could be pushed.",[43,44671,44673],{"className":13667,"code":44672,"language":13669,"meta":48,"style":48},"\n git reset --hard HEAD~8\n\n",[50,44674,44675,44679],{"__ignoreMap":48},[53,44676,44677],{"class":55,"line":56},[53,44678,500],{"emptyLinePlaceholder":499},[53,44680,44681],{"class":55,"line":86},[53,44682,44683],{}," git reset --hard HEAD~8\n",[18,44685,40364],{},[43,44687,44689],{"className":13667,"code":44688,"language":13669,"meta":48,"style":48},"\n git reset --hard sha-hash-of-commit-to-point-to\n\n",[50,44690,44691,44695],{"__ignoreMap":48},[53,44692,44693],{"class":55,"line":56},[53,44694,500],{"emptyLinePlaceholder":499},[53,44696,44697],{"class":55,"line":86},[53,44698,44699],{}," git reset --hard sha-hash-of-commit-to-point-to\n",[18,44701,44702],{},"Now my master is in the state it was in 8 commits ago and my experimental changes",[18,44704,44705],{},"are still in featureA branch.",[43,44707,44709],{"className":13667,"code":44708,"language":13669,"meta":48,"style":48},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[50,44710,44711,44715,44720,44724,44728],{"__ignoreMap":48},[53,44712,44713],{"class":55,"line":56},[53,44714,44640],{},[53,44716,44717],{"class":55,"line":86},[53,44718,44719],{}," |svn | master\n",[53,44721,44722],{"class":55,"line":126},[53,44723,44650],{},[53,44725,44726],{"class":55,"line":163},[53,44727,44655],{},[53,44729,44730],{"class":55,"line":186},[53,44731,44660],{},[18,44733,44734],{},"I can continue with fixing that critical bug, commit and svn dcommit. Have a look on how your history look with gitk\n–all.",[43,44736,44738],{"className":13667,"code":44737,"language":13669,"meta":48,"style":48},"\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",[50,44739,44740,44744,44749,44754,44759,44764,44769,44774],{"__ignoreMap":48},[53,44741,44742],{"class":55,"line":56},[53,44743,44640],{},[53,44745,44746],{"class":55,"line":86},[53,44747,44748],{}," | svn\n",[53,44750,44751],{"class":55,"line":126},[53,44752,44753],{}," ---o---o---o master\n",[53,44755,44756],{"class":55,"line":163},[53,44757,44758],{}," /\n",[53,44760,44761],{"class":55,"line":186},[53,44762,44763],{}," ---o---o---o\n",[53,44765,44766],{"class":55,"line":221},[53,44767,44768],{}," \\---o---o---o---o---o---o---o\n",[53,44770,44771],{"class":55,"line":242},[53,44772,44773],{}," | featureA\n",[53,44775,44776],{"class":55,"line":273},[53,44777,44660],{},[18,44779,44780],{},"Dcommit rebased 3 commits and especially if",[18,44782,44783],{},"there were some more upstream svn commits, I want to base my experimental stuff",[18,44785,44786],{},"ontop of this. So I do a",[43,44788,44790],{"className":13667,"code":44789,"language":13669,"meta":48,"style":48},"\n git checkout featureA\n git rebase master\n\n",[50,44791,44792,44796,44801],{"__ignoreMap":48},[53,44793,44794],{"class":55,"line":56},[53,44795,500],{"emptyLinePlaceholder":499},[53,44797,44798],{"class":55,"line":86},[53,44799,44800],{}," git checkout featureA\n",[53,44802,44803],{"class":55,"line":126},[53,44804,44805],{}," git rebase master\n",[43,44807,44809],{"className":13667,"code":44808,"language":13669,"meta":48,"style":48},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn | featureA\n\u003C/tt>\n",[50,44810,44811,44815,44820,44825,44830],{"__ignoreMap":48},[53,44812,44813],{"class":55,"line":56},[53,44814,44640],{},[53,44816,44817],{"class":55,"line":86},[53,44818,44819],{}," | master\n",[53,44821,44822],{"class":55,"line":126},[53,44823,44824],{}," ---o---o---o---o---o---o---o---o---o---o---o\n",[53,44826,44827],{"class":55,"line":163},[53,44828,44829],{}," | svn | featureA\n",[53,44831,44832],{"class":55,"line":186},[53,44833,44660],{},[18,44835,44836],{},"Even if I could live without the upstream changes on my featureA branch for now, I’d",[18,44838,44839],{},"need a rebase later anyway, so I can do it in advance. That’s because the history",[18,44841,44842],{},"wouldn’t be linear anymore by doing a three-way merge of my featureA into master without rebasing.",[18,44844,44845],{},"When I’m satisfied and with featureA and nothing changed in master I can",[43,44847,44849],{"className":13667,"code":44848,"language":13669,"meta":48,"style":48},"\n git checkout master\n git merge featureA\n git branch -d featureA\n\n",[50,44850,44851,44855,44860,44865],{"__ignoreMap":48},[53,44852,44853],{"class":55,"line":56},[53,44854,500],{"emptyLinePlaceholder":499},[53,44856,44857],{"class":55,"line":86},[53,44858,44859],{}," git checkout master\n",[53,44861,44862],{"class":55,"line":126},[53,44863,44864],{}," git merge featureA\n",[53,44866,44867],{"class":55,"line":163},[53,44868,44869],{}," git branch -d featureA\n",[18,44871,44872],{},"And since it’s a fast-forward merge can continue to push it to SVN",[43,44874,44876],{"className":13667,"code":44875,"language":13669,"meta":48,"style":48},"\n git svn dcommit\n\n",[50,44877,44878,44882],{"__ignoreMap":48},[53,44879,44880],{"class":55,"line":56},[53,44881,500],{"emptyLinePlaceholder":499},[53,44883,44884],{"class":55,"line":86},[53,44885,44886],{}," git svn dcommit\n",[43,44888,44890],{"className":13667,"code":44889,"language":13669,"meta":48,"style":48},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn\n\u003C/tt>\n",[50,44891,44892,44896,44901,44905,44910],{"__ignoreMap":48},[53,44893,44894],{"class":55,"line":56},[53,44895,44640],{},[53,44897,44898],{"class":55,"line":86},[53,44899,44900],{}," | master\n",[53,44902,44903],{"class":55,"line":126},[53,44904,44824],{},[53,44906,44907],{"class":55,"line":163},[53,44908,44909],{}," | svn\n",[53,44911,44912],{"class":55,"line":186},[53,44913,44660],{},[18,44915,44916],{},"If something did change in master I just do another rebase before the merge.",[18,44918,44919],{},"If I come to the conclusion that my experimental branch was just for learning",[18,44921,44922],{},"purpose and only one or two useful refactoring or unit-test improving commits I take",[18,44924,44925],{},"only these to master and abandon the branch.",[43,44927,44929],{"className":13667,"code":44928,"language":13669,"meta":48,"style":48},"\n git checkout master\n git cherry-pick sha-of-one-commit\n git cherry-pick sha-of-another\n\n",[50,44930,44931,44935,44939,44944],{"__ignoreMap":48},[53,44932,44933],{"class":55,"line":56},[53,44934,500],{"emptyLinePlaceholder":499},[53,44936,44937],{"class":55,"line":86},[53,44938,44859],{},[53,44940,44941],{"class":55,"line":126},[53,44942,44943],{}," git cherry-pick sha-of-one-commit\n",[53,44945,44946],{"class":55,"line":163},[53,44947,44948],{}," git cherry-pick sha-of-another\n",[18,44950,44951,44952],{},"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 ",[585,44953,44956],{"href":44954,"rel":44955},"http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode",[589],"interactive mode",[43,44958,44960],{"className":13667,"code":44959,"language":13669,"meta":48,"style":48},"\n git rebase -i sha-after-this-commit\n\n",[50,44961,44962,44966],{"__ignoreMap":48},[53,44963,44964],{"class":55,"line":56},[53,44965,500],{"emptyLinePlaceholder":499},[53,44967,44968],{"class":55,"line":86},[53,44969,44970],{}," git rebase -i sha-after-this-commit\n",[18,44972,44973],{},"reordering commits, splitting commits, editing commit messages, squashing multiple commits together.",[18,44975,44976],{},"There are endless more possibilities to get a better grip on your code-base.",[607,44978,989],{},{"title":48,"searchDepth":86,"depth":86,"links":44980},[],[613],"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":44536,"description":44545},"utilizing-git-to-dive-into-huge-code-bases","blog/utilizing-git-to-dive-into-huge-code-bases",[44990,40398,44991,6503,44992,44993,44994,44995,44996,44997],"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":45001,"title":45002,"author":45003,"body":45004,"category":45054,"date":45055,"description":45056,"extension":617,"link":45057,"meta":45058,"navigation":499,"path":45059,"seo":45060,"slug":45008,"stem":45062,"tags":45063,"teaser":45064,"__hash__":45065},"blog/blog/solr-summit-frankfurt.md","Solr Summit Frankfurt",[41317],{"type":11,"value":45005,"toc":45052},[45006,45009,45022,45031,45040,45049],[14,45007,45002],{"id":45008},"solr-summit-frankfurt",[18,45010,45011,45012,45017,45018,45021],{},"I just returned\nfrom ",[585,45013,45016],{"href":45014,"rel":45015},"http://www.lucidimagination.com/blog/2011/01/31/solr-summit-series-comes-to-germany-munich23-feb-frankfurt24-feb-2011-2/",[589],"Solr Summit","\nin Frankfurt, a half day mini conference about ",[585,45019,44066],{"href":44064,"rel":45020},[589],", 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.",[18,45023,45024,45025,45030],{},"The first half of the conference Marc Krellenstein, a Co-Founder\nof ",[585,45026,45029],{"href":45027,"rel":45028},"http://www.lucidimagination.com/",[589],"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.",[18,45032,45033,45034,45039],{},"Afterwards Oliver Schönherr and Thomas Kwiatkowski spoke on how Solr is used\nat ",[585,45035,45038],{"href":45036,"rel":45037},"http://www.immobilienscout24.de",[589],"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.",[18,45041,45042,45043,45048],{},"The last talk was held by Olaf Zschiedrich of ",[585,45044,45047],{"href":45045,"rel":45046},"https://www.ebay-kleinanzeigen.de/",[589],"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%.",[18,45050,45051],{},"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":48,"searchDepth":86,"depth":86,"links":45053},[],[613,996],"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":45002,"description":45061},"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",[3491,37695],"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":45067,"title":45068,"author":45069,"body":45070,"category":45125,"date":45126,"description":45127,"extension":617,"link":45128,"meta":45129,"navigation":499,"path":45130,"seo":45131,"slug":45074,"stem":45133,"tags":45134,"teaser":45138,"__hash__":45139},"blog/blog/synyx-messagesource-load-your-i18n-messages-from-database.md","Synyx MessageSource: Load your i18n messages from database",[11420],{"type":11,"value":45071,"toc":45123},[45072,45075,45083,45090,45105],[14,45073,45068],{"id":45074},"synyx-messagesource-load-your-i18n-messages-from-database",[18,45076,45077,45078,45082],{},"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 ",[585,45079,33855],{"href":45080,"rel":45081},"http://www.google.de/search?q=spring+i18n+database",[589]," for details).",[18,45084,45085,45086,45089],{},"So today I’d like to announce our new Open Source project ",[27,45087,45088],{},"Synyx Messagesource for Spring"," business-friendly\npublished using Apache License, Version 2.0.",[18,45091,45092,45093,45096,45097,45100,45101,45104],{},"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 ",[50,45094,45095],{},"MessageSource"," interface that is able to load and cache a set of\nmessages at once using a ",[50,45098,45099],{},"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",[50,45102,45103],{},".properties"," files.",[18,45106,44444,45107,45111,45112,45117,45118,986],{},[585,45108,10905],{"href":45109,"rel":45110},"http://messagesource.synyx.org",[589],". You should not get problems to get up and running after reading the\ninformation from the ",[585,45113,45116],{"href":45114,"rel":45115},"https://github.com/synyx/messagesource/wiki",[589],"projects Wiki",". If you’re having any trouble or\nfeature request feel free to contact us\nor ",[585,45119,45122],{"href":45120,"rel":45121},"https://github.com/synyx/messagesource/issues",[589],"create a ticket in the projects issue tracker",{"title":48,"searchDepth":86,"depth":86,"links":45124},[],[613,996],"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":45068,"description":45132},"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",[29803,45135,45136,45137,12072,1010],"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":45141,"title":45142,"author":45143,"body":45144,"category":45159,"date":45160,"description":45161,"extension":617,"link":45162,"meta":45163,"navigation":499,"path":45164,"seo":45165,"slug":45148,"stem":45167,"tags":45168,"teaser":45169,"__hash__":45170},"blog/blog/synyx-sponsort-die-opencms-days.md","Synyx sponsort die OpenCms-Days",[41317],{"type":11,"value":45145,"toc":45157},[45146,45149],[14,45147,45142],{"id":45148},"synyx-sponsort-die-opencms-days",[18,45150,45151,45152,45156],{},"Vom 09. bis zum 10. Mai finden, rechtzeitig zum Release von OpenCms 8, die 3. ",[585,45153,45155],{"href":44050,"rel":45154},[589],"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":48,"searchDepth":86,"depth":86,"links":45158},[],[614],"2011-02-02T21:31:27","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\\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":45142,"description":45166},"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\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",[37693],"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…","NeDaL5qGIwTEnwvsk4Hvl5qP2x5f_A7WfQkxOBAPM2s",{"id":45172,"title":45173,"author":45174,"body":45175,"category":45257,"date":45258,"description":45259,"extension":617,"link":45260,"meta":45261,"navigation":499,"path":45262,"seo":45263,"slug":45179,"stem":45265,"tags":45266,"teaser":45269,"__hash__":45270},"blog/blog/spring-ide-into-eclipse.md","Spring IDE into eclipse",[33954],{"type":11,"value":45176,"toc":45255},[45177,45180,45195,45210,45213,45216,45221,45236,45239,45242],[14,45178,45173],{"id":45179},"spring-ide-into-eclipse",[18,45181,45182,45183,45188,45189,45194],{},"Today, I tried to install parts of the ",[585,45184,45187],{"href":45185,"rel":45186},"http://www.springsource.com/developer/sts",[589],"SpringSource Tool Suite","\ninto ",[585,45190,45193],{"href":45191,"rel":45192},"http://www.eclipse.org/",[589],"Eclipse"," Helios SR1 via update-site.",[18,45196,45197,45198,45204,45205,45209],{},"After finding the right update-site of the STS for version 3.6 of eclipse (namely: *\n",[573,45199,45200],{},[585,45201,45202],{"href":45202,"rel":45203},"http://dist.springsource.com/release/TOOLS/update/e3.6",[589],"* and * *",[585,45206,45207],{"href":45207,"rel":45208},"http://dist.springsource.com/release/TOOLS/composite/e3.6",[589],"**) and following the installation instructions carefully, I\nhad Spring IDE 2.5.2 successfully integrated.",[18,45211,45212],{},"But after restarting eclipse I was shocked, it seemed that the update-manager broke down…",[18,45214,45215],{},"when opening the update-manager the log gasped out:",[18,45217,1038,45218,1042],{},[573,45219,45220],{},"An internal error occurred during: Contacting Software Sites. java.lang.NullPointerException…",[18,45222,45223,45224,45229,45230,45235],{},"didn’t sound good. After quite a lot of researching and try and failing, I was very glad to find\na ",[585,45225,45228],{"href":45226,"rel":45227},"https://jira.springsource.org/browse/IDE-1163?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#issue-tabs",[589],"bug report",",\nwhich mentioned that this occurs on updating Spring IDE. Thanks\nto ",[585,45231,45234],{"href":45232,"rel":45233},"http://www.springsource.com/people/cdupuis",[589],"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”",[18,45237,45238],{},"everything was working well again.",[18,45240,45241],{},"Now I can enjoy my Spring IDE and perhaps you can so, too.",[18,45243,45244,45245,45248,45249,45254],{},"edit: in the meantime ",[585,45246,45234],{"href":45232,"rel":45247},[589]," already delivered\na ",[585,45250,45253],{"href":45251,"rel":45252},"http://dist.springsource.com/release/TOOLS/update/2.5.2.SR1/e3.6/springsource-tool-suite-2.5.2.RELEASE-e3.6-updatesite.zip",[589],"quickly fixed update-archive","\nwhich makes the file manipulating superfluous… Hell these guys are quite fast!",{"title":48,"searchDepth":86,"depth":86,"links":45256},[],[613],"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":45173,"description":45264},"Today, I tried to install parts of the SpringSource Tool Suite\ninto Eclipse Helios SR1 via update-site.","blog/spring-ide-into-eclipse",[45267,45268,1010],"bug","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…","pYIVv5Z_1S__iGMQhek2t48OhdaNC3Lw0q-CD9EVE-M",{"id":45272,"title":45273,"author":45274,"body":45275,"category":46147,"date":46148,"description":46149,"extension":617,"link":46150,"meta":46151,"navigation":499,"path":46152,"seo":46153,"slug":45279,"stem":46155,"tags":46156,"teaser":46157,"__hash__":46158},"blog/blog/scripting-opencms.md","Scripting OpenCms",[41317],{"type":11,"value":45276,"toc":46145},[45277,45280,45293,45300,45312,45663,45666,45767,45770,45793,45796,45819,45822,45827,45830,46139,46142],[14,45278,45273],{"id":45279},"scripting-opencms",[18,45281,45282,45286,45287,45292],{},[585,45283,40251],{"href":45284,"rel":45285},"http://opencms.org/de/",[589]," 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. ",[585,45288,45291],{"href":45289,"rel":45290},"http://blog.codecentric.de/en/2010/04/how-to-automate-opencms-module-import/",[589],"This blogpost","\ndescribes a way to circumvent these problems by generating the script files.",[18,45294,45295,45296,45299],{},"I will present a simple extension of this mechanism that makes it possible to access the VFS using\nthe ",[585,45297,12893],{"href":40487,"rel":45298},[589]," 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.",[18,45301,45302,45303,45306,45307,45311],{},"The original script file for accessing the cms shell is located in ",[50,45304,45305],{},"WEB-INF/cmsshell.sh"," (\nSee ",[585,45308,10905],{"href":45309,"rel":45310},"http://blog.synyx.de/2010/11/using-cmsshell-on-ubuntu-systems/",[589]," 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:",[43,45313,45315],{"className":29913,"code":45314,"language":29915,"meta":48,"style":48},"\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",[50,45316,45317,45321,45325,45330,45334,45339,45343,45348,45362,45367,45386,45412,45436,45440,45450,45455,45474,45495,45499,45516,45532,45536,45541,45558,45574,45578,45587,45604,45621,45625],{"__ignoreMap":48},[53,45318,45319],{"class":55,"line":56},[53,45320,500],{"emptyLinePlaceholder":499},[53,45322,45323],{"class":55,"line":86},[53,45324,2531],{"class":2530},[53,45326,45327],{"class":55,"line":126},[53,45328,45329],{"class":2530},"# Script for evaluating groovy scripts with access to opencms.\n",[53,45331,45332],{"class":55,"line":163},[53,45333,30183],{"class":2530},[53,45335,45336],{"class":55,"line":186},[53,45337,45338],{"class":2530},"# Please make sure that \"servlet-api.jar\" and \"jsp-api.jar\" are found.\n",[53,45340,45341],{"class":55,"line":221},[53,45342,30183],{"class":2530},[53,45344,45345],{"class":55,"line":242},[53,45346,45347],{"class":2530},"# get path to opencms base directory\n",[53,45349,45350,45353,45355,45357,45360],{"class":55,"line":273},[53,45351,45352],{"class":82},"OPENCMS_BASE",[53,45354,390],{"class":389},[53,45356,4067],{"class":63},[53,45358,45359],{"class":89},"pwd",[53,45361,4095],{"class":63},[53,45363,45364],{"class":55,"line":279},[53,45365,45366],{"class":2530},"# get path to tomcat home\n",[53,45368,45369,45371,45373,45375,45377,45380,45382,45384],{"class":55,"line":496},[53,45370,2540],{"class":389},[53,45372,2307],{"class":82},[53,45374,30445],{"class":389},[53,45376,6072],{"class":63},[53,45378,45379],{"class":82},"$TOMCAT_HOME",[53,45381,2704],{"class":63},[53,45383,30455],{"class":82},[53,45385,6087],{"class":389},[53,45387,45388,45391,45394,45396,45399,45401,45404,45406,45408,45410],{"class":55,"line":503},[53,45389,45390],{"class":82}," [ ",[53,45392,45393],{"class":389},"-n",[53,45395,6072],{"class":63},[53,45397,45398],{"class":82},"$CATALINA_HOME",[53,45400,2704],{"class":63},[53,45402,45403],{"class":82}," ] && TOMCAT_HOME",[53,45405,390],{"class":389},[53,45407,2704],{"class":63},[53,45409,45398],{"class":82},[53,45411,30535],{"class":63},[53,45413,45414,45416,45418,45420,45422,45424,45426,45428,45430,45433],{"class":55,"line":509},[53,45415,45390],{"class":82},[53,45417,30445],{"class":389},[53,45419,6072],{"class":63},[53,45421,45379],{"class":82},[53,45423,2704],{"class":63},[53,45425,45403],{"class":82},[53,45427,390],{"class":389},[53,45429,2704],{"class":63},[53,45431,45432],{"class":82},"$OPENCMS_BASE",[53,45434,45435],{"class":63},"\"/../../..\n",[53,45437,45438],{"class":55,"line":515},[53,45439,2579],{"class":389},[53,45441,45442,45445,45447],{"class":55,"line":521},[53,45443,45444],{"class":82},"TOMCAT_CLASSPATH",[53,45446,390],{"class":389},[53,45448,45449],{"class":63},"\"\"\n",[53,45451,45452],{"class":55,"line":527},[53,45453,45454],{"class":2530},"# Support for tomcat 5\n",[53,45456,45457,45459,45462,45464,45467,45470,45472],{"class":55,"line":533},[53,45458,31521],{"class":389},[53,45460,45461],{"class":82}," JAR ",[53,45463,31527],{"class":389},[53,45465,45466],{"class":82}," ${TOMCAT_HOME}",[53,45468,45469],{"class":63},"/common/lib/*.jar",[53,45471,30366],{"class":82},[53,45473,30369],{"class":389},[53,45475,45476,45479,45481,45484,45486,45489,45492],{"class":55,"line":539},[53,45477,45478],{"class":82}," TOMCAT_CLASSPATH",[53,45480,390],{"class":389},[53,45482,45483],{"class":63},"\"${",[53,45485,45444],{"class":82},[53,45487,45488],{"class":63},"}:${",[53,45490,45491],{"class":82},"JAR",[53,45493,45494],{"class":63},"}\"\n",[53,45496,45497],{"class":55,"line":545},[53,45498,30415],{"class":389},[53,45500,45501,45503,45505,45507,45509,45512,45514],{"class":55,"line":2070},[53,45502,31521],{"class":389},[53,45504,45461],{"class":82},[53,45506,31527],{"class":389},[53,45508,45466],{"class":82},[53,45510,45511],{"class":63},"/shared/lib/*.jar",[53,45513,30366],{"class":82},[53,45515,30369],{"class":389},[53,45517,45518,45520,45522,45524,45526,45528,45530],{"class":55,"line":2075},[53,45519,45478],{"class":82},[53,45521,390],{"class":389},[53,45523,45483],{"class":63},[53,45525,45444],{"class":82},[53,45527,45488],{"class":63},[53,45529,45491],{"class":82},[53,45531,45494],{"class":63},[53,45533,45534],{"class":55,"line":2081},[53,45535,30415],{"class":389},[53,45537,45538],{"class":55,"line":2087},[53,45539,45540],{"class":2530},"# Support for tomcat 6\n",[53,45542,45543,45545,45547,45549,45551,45554,45556],{"class":55,"line":2092},[53,45544,31521],{"class":389},[53,45546,45461],{"class":82},[53,45548,31527],{"class":389},[53,45550,45466],{"class":82},[53,45552,45553],{"class":63},"/lib/*.jar",[53,45555,30366],{"class":82},[53,45557,30369],{"class":389},[53,45559,45560,45562,45564,45566,45568,45570,45572],{"class":55,"line":2097},[53,45561,45478],{"class":82},[53,45563,390],{"class":389},[53,45565,45483],{"class":63},[53,45567,45444],{"class":82},[53,45569,45488],{"class":63},[53,45571,45491],{"class":82},[53,45573,45494],{"class":63},[53,45575,45576],{"class":55,"line":2103},[53,45577,30415],{"class":389},[53,45579,45580,45583,45585],{"class":55,"line":2109},[53,45581,45582],{"class":82},"OPENCMS_CLASSPATH",[53,45584,390],{"class":389},[53,45586,45449],{"class":63},[53,45588,45589,45591,45593,45595,45598,45600,45602],{"class":55,"line":2115},[53,45590,31521],{"class":389},[53,45592,45461],{"class":82},[53,45594,31527],{"class":389},[53,45596,45597],{"class":82}," ${OPENCMS_BASE}",[53,45599,45553],{"class":63},[53,45601,30366],{"class":82},[53,45603,30369],{"class":389},[53,45605,45606,45609,45611,45613,45615,45617,45619],{"class":55,"line":2120},[53,45607,45608],{"class":82}," OPENCMS_CLASSPATH",[53,45610,390],{"class":389},[53,45612,45483],{"class":63},[53,45614,45582],{"class":82},[53,45616,45488],{"class":63},[53,45618,45491],{"class":82},[53,45620,45494],{"class":63},[53,45622,45623],{"class":55,"line":2946},[53,45624,30415],{"class":389},[53,45626,45627,45629,45632,45635,45637,45639,45641,45644,45647,45650,45652,45654,45656,45658,45661],{"class":55,"line":2952},[53,45628,12893],{"class":59},[53,45630,45631],{"class":89}," -classpath",[53,45633,45634],{"class":63}," \"${",[53,45636,45582],{"class":82},[53,45638,45488],{"class":63},[53,45640,45444],{"class":82},[53,45642,45643],{"class":63},"}:classes\"",[53,45645,45646],{"class":63}," evalscripts.groovy",[53,45648,45649],{"class":89}," -base=",[53,45651,45483],{"class":63},[53,45653,45352],{"class":82},[53,45655,5911],{"class":63},[53,45657,6072],{"class":63},[53,45659,45660],{"class":89},"$@",[53,45662,30535],{"class":63},[18,45664,45665],{},"As you can see, a groovy script named “evalscripts.groovy” is called and all options are passed to it. The script:",[43,45667,45669],{"className":12891,"code":45668,"language":12893,"meta":48,"style":48},"\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",[50,45670,45671,45675,45680,45685,45690,45695,45700,45705,45709,45713,45718,45723,45728,45733,45738,45743,45748,45753,45758,45762],{"__ignoreMap":48},[53,45672,45673],{"class":55,"line":56},[53,45674,500],{"emptyLinePlaceholder":499},[53,45676,45677],{"class":55,"line":86},[53,45678,45679],{},"import org.opencms.main.CmsShell;\n",[53,45681,45682],{"class":55,"line":126},[53,45683,45684],{},"import org.opencms.file.CmsObject;\n",[53,45686,45687],{"class":55,"line":163},[53,45688,45689],{},"base = args[0].substring(CmsShell.SHELL_PARAM_BASE.length());\n",[53,45691,45692],{"class":55,"line":186},[53,45693,45694],{},"shell = new CmsShell(base, null, null, \">\", null) {\n",[53,45696,45697],{"class":55,"line":221},[53,45698,45699],{}," CmsObject getCmsObject() {\n",[53,45701,45702],{"class":55,"line":242},[53,45703,45704],{}," return m_cms;\n",[53,45706,45707],{"class":55,"line":273},[53,45708,860],{},[53,45710,45711],{"class":55,"line":279},[53,45712,282],{},[53,45714,45715],{"class":55,"line":496},[53,45716,45717],{},"user = \"Admin\";\n",[53,45719,45720],{"class":55,"line":503},[53,45721,45722],{},"pass = \"admin\";\n",[53,45724,45725],{"class":55,"line":509},[53,45726,45727],{},"cms = shell.getCmsObject();\n",[53,45729,45730],{"class":55,"line":515},[53,45731,45732],{},"cms.loginUser(user, pass);\n",[53,45734,45735],{"class":55,"line":521},[53,45736,45737],{},"binding1 = new Binding();\n",[53,45739,45740],{"class":55,"line":527},[53,45741,45742],{},"binding1.setProperty('cmsObject' , cms);\n",[53,45744,45745],{"class":55,"line":533},[53,45746,45747],{},"groovyShell = new GroovyShell(binding1);\n",[53,45749,45750],{"class":55,"line":539},[53,45751,45752],{},"for (int i = 1; i \u003C args.length; i++) {\n",[53,45754,45755],{"class":55,"line":545},[53,45756,45757],{}," groovyShell.evaluate(new File(args[i]))\n",[53,45759,45760],{"class":55,"line":2070},[53,45761,282],{},[53,45763,45764],{"class":55,"line":2075},[53,45765,45766],{},"shell.exit();\n",[18,45768,45769],{},"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:",[43,45771,45773],{"className":12891,"code":45772,"language":12893,"meta":48,"style":48},"\n10.times {\n cmsObject.createUser(\"User$it\", \"Pass$it\", \"\", new HashMap());\n}\n\n",[50,45774,45775,45779,45784,45789],{"__ignoreMap":48},[53,45776,45777],{"class":55,"line":56},[53,45778,500],{"emptyLinePlaceholder":499},[53,45780,45781],{"class":55,"line":86},[53,45782,45783],{},"10.times {\n",[53,45785,45786],{"class":55,"line":126},[53,45787,45788],{}," cmsObject.createUser(\"User$it\", \"Pass$it\", \"\", new HashMap());\n",[53,45790,45791],{"class":55,"line":163},[53,45792,282],{},[18,45794,45795],{},"Or list all users:",[43,45797,45799],{"className":12891,"code":45798,"language":12893,"meta":48,"style":48},"\ncmsObject.getUsers().each {\n println it.name\n}\n\n",[50,45800,45801,45805,45810,45815],{"__ignoreMap":48},[53,45802,45803],{"class":55,"line":56},[53,45804,500],{"emptyLinePlaceholder":499},[53,45806,45807],{"class":55,"line":86},[53,45808,45809],{},"cmsObject.getUsers().each {\n",[53,45811,45812],{"class":55,"line":126},[53,45813,45814],{}," println it.name\n",[53,45816,45817],{"class":55,"line":163},[53,45818,282],{},[18,45820,45821],{},"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:",[18,45823,45824],{},[50,45825,45826],{},"./groovyshell.sh createUsers.groovy listUsers.groovy",[18,45828,45829],{},"You will see the familiar OpenCms startup sequence followed by the output of the second script:",[43,45831,45833],{"className":29913,"code":45832,"language":29915,"meta":48,"style":48},"\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",[50,45834,45835,45839,45843,45858,45870,45878,45896,45923,45940,45968,45976,45990,46015,46044,46065,46070,46075,46080,46085,46090,46095,46100,46105,46110,46115,46120,46125,46130,46135],{"__ignoreMap":48},[53,45836,45837],{"class":55,"line":56},[53,45838,500],{"emptyLinePlaceholder":499},[53,45840,45841],{"class":55,"line":86},[53,45842,5968],{"class":89},[53,45844,45845,45848,45850,45852,45855],{"class":55,"line":126},[53,45846,45847],{"class":59},"Welcome",[53,45849,378],{"class":63},[53,45851,112],{"class":63},[53,45853,45854],{"class":63}," OpenCms",[53,45856,45857],{"class":63}," shell!\n",[53,45859,45860,45863,45865,45867],{"class":55,"line":163},[53,45861,45862],{"class":59},"This",[53,45864,6924],{"class":63},[53,45866,45854],{"class":63},[53,45868,45869],{"class":63}," 7.5.x.\n",[53,45871,45872,45875],{"class":55,"line":186},[53,45873,45874],{"class":59},"Copyright",[53,45876,45877],{"class":82}," (c) 2010 Alkacon Software GmbH\n",[53,45879,45880,45882,45885,45887,45890,45893],{"class":55,"line":221},[53,45881,40251],{"class":59},[53,45883,45884],{"class":63}," comes",[53,45886,1178],{"class":63},[53,45888,45889],{"class":63}," ABSOLUTELY",[53,45891,45892],{"class":63}," NO",[53,45894,45895],{"class":63}," WARRANTY\n",[53,45897,45898,45900,45902,45905,45908,45911,45914,45917,45920],{"class":55,"line":242},[53,45899,45862],{"class":59},[53,45901,6924],{"class":63},[53,45903,45904],{"class":63}," free",[53,45906,45907],{"class":63}," software,",[53,45909,45910],{"class":63}," and",[53,45912,45913],{"class":63}," you",[53,45915,45916],{"class":63}," are",[53,45918,45919],{"class":63}," welcome",[53,45921,45922],{"class":63}," to\n",[53,45924,45925,45928,45931,45934,45937],{"class":55,"line":273},[53,45926,45927],{"class":59},"redistribute",[53,45929,45930],{"class":63}," it",[53,45932,45933],{"class":63}," under",[53,45935,45936],{"class":63}," certain",[53,45938,45939],{"class":63}," conditions.\n",[53,45941,45942,45945,45948,45950,45953,45956,45959,45962,45965],{"class":55,"line":279},[53,45943,45944],{"class":59},"Please",[53,45946,45947],{"class":63}," see",[53,45949,112],{"class":63},[53,45951,45952],{"class":63}," GNU",[53,45954,45955],{"class":63}," Lesser",[53,45957,45958],{"class":63}," General",[53,45960,45961],{"class":63}," Public",[53,45963,45964],{"class":63}," Licence",[53,45966,45967],{"class":63}," for\n",[53,45969,45970,45973],{"class":55,"line":496},[53,45971,45972],{"class":59},"further",[53,45974,45975],{"class":63}," details.\n",[53,45977,45978,45981,45984,45987],{"class":55,"line":503},[53,45979,45980],{"class":59},"help",[53,45982,45983],{"class":63}," Shows",[53,45985,45986],{"class":63}," this",[53,45988,45989],{"class":63}," text.\n",[53,45991,45992,45994,45996,45999,46001,46004,46006,46009,46012],{"class":55,"line":509},[53,45993,45980],{"class":59},[53,45995,1058],{"class":89},[53,45997,45998],{"class":63}," Shows",[53,46000,112],{"class":63},[53,46002,46003],{"class":63}," signatures",[53,46005,109],{"class":63},[53,46007,46008],{"class":63}," all",[53,46010,46011],{"class":63}," available",[53,46013,46014],{"class":63}," methods.\n",[53,46016,46017,46019,46022,46025,46027,46029,46031,46033,46036,46039,46041],{"class":55,"line":515},[53,46018,45980],{"class":59},[53,46020,46021],{"class":63}," {string}",[53,46023,46024],{"class":63}," Shows",[53,46026,112],{"class":63},[53,46028,46003],{"class":63},[53,46030,109],{"class":63},[53,46032,46008],{"class":63},[53,46034,46035],{"class":63}," methods",[53,46037,46038],{"class":63}," containing",[53,46040,45986],{"class":63},[53,46042,46043],{"class":63}," string.\n",[53,46045,46046,46049,46052,46055,46058,46060,46062],{"class":55,"line":521},[53,46047,46048],{"class":89},"exit",[53,46050,46051],{"class":63}," or",[53,46053,46054],{"class":63}," quit",[53,46056,46057],{"class":63}," Leaves",[53,46059,45986],{"class":63},[53,46061,45854],{"class":63},[53,46063,46064],{"class":63}," Shell.\n",[53,46066,46067],{"class":55,"line":527},[53,46068,46069],{"class":59},"Admin\n",[53,46071,46072],{"class":55,"line":533},[53,46073,46074],{"class":59},"Export\n",[53,46076,46077],{"class":55,"line":539},[53,46078,46079],{"class":59},"Guest\n",[53,46081,46082],{"class":55,"line":545},[53,46083,46084],{"class":59},"User0\n",[53,46086,46087],{"class":55,"line":2070},[53,46088,46089],{"class":59},"User1\n",[53,46091,46092],{"class":55,"line":2075},[53,46093,46094],{"class":59},"User2\n",[53,46096,46097],{"class":55,"line":2081},[53,46098,46099],{"class":59},"User3\n",[53,46101,46102],{"class":55,"line":2087},[53,46103,46104],{"class":59},"User4\n",[53,46106,46107],{"class":55,"line":2092},[53,46108,46109],{"class":59},"User5\n",[53,46111,46112],{"class":55,"line":2097},[53,46113,46114],{"class":59},"User6\n",[53,46116,46117],{"class":55,"line":2103},[53,46118,46119],{"class":59},"User7\n",[53,46121,46122],{"class":55,"line":2109},[53,46123,46124],{"class":59},"User8\n",[53,46126,46127],{"class":55,"line":2115},[53,46128,46129],{"class":59},"User9\n",[53,46131,46132],{"class":55,"line":2120},[53,46133,46134],{"class":59},"Goodbye!\n",[53,46136,46137],{"class":55,"line":2946},[53,46138,5968],{"class":89},[18,46140,46141],{},"I think this will be useful for us in the future, maybe also for you?",[607,46143,46144],{},"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":48,"searchDepth":86,"depth":86,"links":46146},[],[613,996],"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":45273,"description":46154},"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",[12893,37693],"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":46160,"title":46161,"author":46162,"body":46163,"category":46252,"date":46253,"description":46254,"extension":617,"link":46255,"meta":46256,"navigation":499,"path":46257,"seo":46258,"slug":46260,"stem":46261,"tags":46262,"teaser":46264,"__hash__":46265},"blog/blog/devoxx-2010-revisited.md","Devoxx 2010 – Revisited using Parleys",[11420],{"type":11,"value":46164,"toc":46250},[46165,46168,46189,46192,46195,46198,46207,46216,46224,46231,46237],[14,46166,46161],{"id":46167},"devoxx-2010-revisited-using-parleys",[18,46169,46170,46171,46175,46176,1073,46180,1073,46184,46188],{},"This year in November three of my colleagues and I were visiting the best Java conference\never – ",[585,46172,4729],{"href":46173,"rel":46174},"http://devoxx.com/display/Devoxx2K10/Home",[589]," in Antwerp (blogs\nhere: ",[585,46177,7598],{"href":46178,"rel":46179},"http://blog.synyx.de/2010/11/devoxx-2010-part-1/",[589],[585,46181,7607],{"href":46182,"rel":46183},"http://blog.synyx.de/2010/11/devoxx-university/",[589],[585,46185,7615],{"href":46186,"rel":46187},"http://blog.synyx.de/2010/11/devoxx-2010-part-2/",[589],").\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.",[18,46190,46191],{},"The fist thing is, that the conference was very very well organized and the location was almost perfect.",[18,46193,46194],{},"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.",[18,46196,46197],{},"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.",[18,46199,46200,46201,46206],{},"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 ",[585,46202,46205],{"href":46203,"rel":46204},"http://parleys.com",[589],"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.",[18,46208,46209,46210,46215],{},"Parleys is a fancy webbased application (",[585,46211,46214],{"href":46212,"rel":46213},"http://get.adobe.com/air/",[589],"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.",[18,46217,46218,46219,46223],{},"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 ",[585,46220,46221],{"href":46221,"rel":46222},"http://parleys.com/desktop/",[589]," 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.",[18,46225,46226,46227,46230],{},"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 ",[585,46228,38639],{"href":38636,"rel":46229},[589]," 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.",[18,46232,46233],{},[1773,46234],{"alt":46235,"src":46236},"\"Talk in Parleys\"","https://media.synyx.de/uploads//2010/12/devoxx_revisited.jpg",[18,46238,46239,46240,99,46243,17019,46246,46249],{},"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 ",[573,46241,46242],{},"queue to download",[573,46244,46245],{},"mark as to be watched",[573,46247,46248],{},"watch",". I really hope\nto see these features announced when we’re coming back to Devoxx 2011.",{"title":48,"searchDepth":86,"depth":86,"links":46251},[],[613],"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":46161,"description":46259},"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",[19738,290,46263,38638],"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…","2q1f9aI8s6IQ0sxryqynGHaJNhc6eD_d5-mQgdXOj5M",{"id":46267,"title":46268,"author":46269,"body":46270,"category":46308,"date":46309,"description":46310,"extension":617,"link":46311,"meta":46312,"navigation":499,"path":46313,"seo":46314,"slug":46274,"stem":46316,"tags":46317,"teaser":46319,"__hash__":46320},"blog/blog/netbeans-and-opencms.md","Netbeans and OpenCms",[41317],{"type":11,"value":46271,"toc":46306},[46272,46275,46282,46295,46298,46303],[14,46273,46268],{"id":46274},"netbeans-and-opencms",[18,46276,46277,46281],{},[585,46278,40251],{"href":46279,"rel":46280},"http://opencms.org",[589]," 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.",[18,46283,46284,46285,46290,46291,986],{},"One of the results is a custom ",[585,46286,46289],{"href":46287,"rel":46288},"http://netbeans.org",[589],"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 ",[585,46292,46294],{"href":44420,"rel":46293},[589],"open source project site",[18,46296,46297],{},"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.",[18,46299,46300],{},[1773,46301],{"alt":48,"src":46302},"https://media.synyx.de/uploads//2010/11/netbeans-dialogsmall.png",[18,46304,46305],{},"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":48,"searchDepth":86,"depth":86,"links":46307},[],[613,996],"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":46268,"description":46315},"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",[46318,37693],"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…","Q9FAJClXp5mBFEXH6TKZXjpr0KEBVv4APPwpv1WY2_8",{"id":46322,"title":46323,"author":46324,"body":46325,"category":46369,"date":46370,"description":46371,"extension":617,"link":4732,"meta":46372,"navigation":499,"path":46373,"seo":46374,"slug":46329,"stem":46375,"tags":46376,"teaser":46377,"__hash__":46378},"blog/blog/devoxx-2010-part-2.md","Devoxx 2010 – Part 2",[11420],{"type":11,"value":46326,"toc":46367},[46327,46330,46333,46346,46349,46352,46355,46361,46364],[14,46328,46323],{"id":46329},"devoxx-2010-part-2",[18,46331,46332],{},"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”.",[18,46334,46335,46336,46340,46341,46345],{},"So day three to five at Devoxx were “Conference” days which have – in opposite to the\nUniversity-Talks ",[585,46337,46339],{"href":46182,"rel":46338},[589],"Florian"," and I mentioned earlier – much shorter\ntalks. This had the trade-off ",[585,46342,46344],{"href":46178,"rel":46343},[589],"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.",[18,46347,46348],{},"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.",[18,46350,46351],{},"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.",[18,46353,46354],{},"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.",[18,46356,46357],{},[1773,46358],{"alt":46359,"src":46360},"\"A lot of people\"","https://media.synyx.de/uploads//2010/11/devoxx-bloch-crowded.jpg",[18,46362,46363],{},"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.",[18,46365,46366],{},"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":48,"searchDepth":86,"depth":86,"links":46368},[],[613],"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":46323,"description":46332},"blog/devoxx-2010-part-2",[3491,19738],"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…","bdOAG0IRzkdVC1t5uzzFu3CGKGq789X_iXcYF7kNrm0",{"id":46380,"title":46381,"author":46382,"body":46384,"category":46397,"date":46398,"description":48,"extension":617,"link":46399,"meta":46400,"navigation":499,"path":46401,"seo":46402,"slug":46388,"stem":46403,"tags":46404,"teaser":46408,"__hash__":46409},"blog/blog/game-center-presentation-at-cocoa-heads-karlsruhe.md","Game Center Presentation at Cocoa Heads Karlsruhe",[46383],"linsin",{"type":11,"value":46385,"toc":46395},[46386,46389],[14,46387,46381],{"id":46388},"game-center-presentation-at-cocoa-heads-karlsruhe",[18,46390,46391],{},[1773,46392],{"alt":46393,"src":46394},"hero-gamecenter.png","https://media.synyx.de/uploads//2010/11/hero-gamecenter.png",{"title":48,"searchDepth":86,"depth":86,"links":46396},[],[4516],"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":46381,"description":48},"blog/game-center-presentation-at-cocoa-heads-karlsruhe",[37119,46405,46406,46407,37123],"ipad","iphone","ipod","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":46411,"title":46412,"author":46413,"body":46414,"category":46488,"date":46489,"description":46490,"extension":617,"link":46491,"meta":46492,"navigation":499,"path":46493,"seo":46494,"slug":46418,"stem":46496,"tags":46497,"teaser":46500,"__hash__":46501},"blog/blog/devoxx-university.md","Devoxx University",[41317],{"type":11,"value":46415,"toc":46486},[46416,46419,46427,46440,46465,46480,46483],[14,46417,46412],{"id":46418},"devoxx-university",[18,46420,46421,46422,46426],{},"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 ",[585,46423,46425],{"href":46178,"rel":46424},[589],"some words on the conference itself"," so I will focus on the\ntalks.",[18,46428,46429,46430,46433,46434,46439],{},"Monday started off with ",[585,46431,40784],{"href":40782,"rel":46432},[589]," 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 ",[585,46435,46438],{"href":46436,"rel":46437},"http://www.mousefeed.com/",[589],"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.",[18,46441,46442,46443,46446,46447,46452,46453,46458,46459,46464],{},"For the afternoon session I chose ",[585,46444,40174],{"href":40172,"rel":46445},[589],", the document oriented database. Being already\nfamiliar with the basic concepts from ",[585,46448,46451],{"href":46449,"rel":46450},"http://blog.synyx.de/2010/08/froscon-2010/",[589],"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 ",[585,46454,46457],{"href":46455,"rel":46456},"http://code.google.com/p/morphia/",[589],"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 ",[585,46460,46463],{"href":46461,"rel":46462},"http://www.springsource.org/spring-data",[589],"Spring Data"," can\noffer something similar in the near future.",[18,46466,46467,46468,46473,46474,46479],{},"On tuesday I had to get up extra early: the Scala lab offered by Dick Wall of the ",[585,46469,46472],{"href":46470,"rel":46471},"http://www.javaposse.com/",[589],"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, ",[585,46475,46478],{"href":46476,"rel":46477},"http://www.scalatest.org/",[589],"ScalaTest"," is supposed to be a good starting\npoint for learning the language without having to integrate it in a production system.",[18,46481,46482],{},"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.",[18,46484,46485],{},"Looking forward to the rest of the conference which will surely bring more interesting talks during the rest of the\nweek!",{"title":48,"searchDepth":86,"depth":86,"links":46487},[],[613],"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":46412,"description":46495},"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",[3491,19738,290,46498,46499],"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":46503,"title":46504,"author":46505,"body":46506,"category":46553,"date":46554,"description":46555,"extension":617,"link":4727,"meta":46556,"navigation":499,"path":46557,"seo":46558,"slug":46510,"stem":46560,"tags":46561,"teaser":46562,"__hash__":46563},"blog/blog/devoxx-2010-part-1.md","Devoxx 2010 – Part 1",[11420],{"type":11,"value":46507,"toc":46551},[46508,46511,46519,46522,46525,46548],[14,46509,46504],{"id":46510},"devoxx-2010-part-1",[18,46512,46513,46514,46518],{},"Employees of Synyx are going to ",[585,46515,4729],{"href":46516,"rel":46517},"http://devoxx.com",[589]," 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!",[18,46520,46521],{},"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.",[18,46523,46524],{},"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…).",[18,46526,46527,46528,99,46533,99,46538,99,46542,46547],{},"By the way, if someone is interested in what University-Talks I’ve been\nvisiting: ",[585,46529,46532],{"href":46530,"rel":46531},"http://devoxx.com/display/Devoxx2K10/Hadoop+Fundamentals++HDFS%2C+MapReduce%2C+Pig%2C+and+Hive",[589],"Hadoop Fundamentals",[585,46534,46537],{"href":46535,"rel":46536},"http://devoxx.com/display/Devoxx2K10/Extreme+Productivity+with+Spring+Roo",[589],"Spring Roo",[585,46539,38639],{"href":46540,"rel":46541},"http://devoxx.com/display/Devoxx2K10/Introducing+Wicket",[589],[585,46543,46546],{"href":46544,"rel":46545},"http://devoxx.com/display/Devoxx2K10/What%27s+new+in+Hibernate++a+JPA+2+perspective",[589],"Hibernate / JPA2"," (\nclick on the links for details and full names of the talks).",[18,46549,46550],{},"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":48,"searchDepth":86,"depth":86,"links":46552},[],[613],"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":46504,"description":46559},"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",[3491,19738],"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":46565,"title":46566,"author":46567,"body":46568,"category":46678,"date":46679,"description":46575,"extension":617,"link":46680,"meta":46681,"navigation":499,"path":46682,"seo":46683,"slug":46684,"stem":46685,"tags":46686,"teaser":46687,"__hash__":46688},"blog/blog/negating-matches-in-apache-location.md","Negating matches in Apache locations",[41317],{"type":11,"value":46569,"toc":46676},[46570,46573,46576,46590,46625,46634,46668,46671,46674],[14,46571,46566],{"id":46572},"negating-matches-in-apache-locations",[18,46574,46575],{},"It took me some time to figure it out so why not sharing it with the world?",[18,46577,46578,46583,46584,46589],{},[585,46579,46582],{"href":46580,"rel":46581},"http://httpd.apache.org/",[589],"Apache"," allows you to add basic auth to parts of your site using\nthe ",[585,46585,46588],{"href":46586,"rel":46587},"http://httpd.apache.org/docs/2.2/mod/core.html#location",[589],"Location"," directive. When restricting access to all\nresources you might add a section like this to your VirtualHost:",[43,46591,46593],{"className":13667,"code":46592,"language":13669,"meta":48,"style":48},"\u003CLocation />\n AuthUserFile /path/to/.htpasswd\n AuthName \"geschuetzter Bereich\"\n AuthType Basic\n require valid-user\n\u003C/Location>\n\n",[50,46594,46595,46600,46605,46610,46615,46620],{"__ignoreMap":48},[53,46596,46597],{"class":55,"line":56},[53,46598,46599],{},"\u003CLocation />\n",[53,46601,46602],{"class":55,"line":86},[53,46603,46604],{}," AuthUserFile /path/to/.htpasswd\n",[53,46606,46607],{"class":55,"line":126},[53,46608,46609],{}," AuthName \"geschuetzter Bereich\"\n",[53,46611,46612],{"class":55,"line":163},[53,46613,46614],{}," AuthType Basic\n",[53,46616,46617],{"class":55,"line":186},[53,46618,46619],{}," require valid-user\n",[53,46621,46622],{"class":55,"line":221},[53,46623,46624],{},"\u003C/Location>\n",[18,46626,46627,46628,46633],{},"/ 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 ",[585,46629,46632],{"href":46630,"rel":46631},"http://www.pcre.org/",[589],"Perl compatible regular expressions"," you can use lookahead assertions to negate an\nexpressions:",[43,46635,46637],{"className":13667,"code":46636,"language":13669,"meta":48,"style":48},"\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",[50,46638,46639,46644,46649,46654,46659,46664],{"__ignoreMap":48},[53,46640,46641],{"class":55,"line":56},[53,46642,46643],{},"\u003CLocation ~ \"^/(?!path/that/doesnt/need/auth)\">\n",[53,46645,46646],{"class":55,"line":86},[53,46647,46648],{}," AuthUserFile /path/to/.htpasswd\n",[53,46650,46651],{"class":55,"line":126},[53,46652,46653],{}," AuthName \"geschuetzter Bereich\"\n",[53,46655,46656],{"class":55,"line":163},[53,46657,46658],{}," AuthType Basic\n",[53,46660,46661],{"class":55,"line":186},[53,46662,46663],{}," require valid-user\n",[53,46665,46666],{"class":55,"line":221},[53,46667,46624],{},[18,46669,46670],{},"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.",[18,46672,46673],{},"Big thank you to our administrators who’ve been kind enough to share a lot of their wisdom with me.",[607,46675,989],{},{"title":48,"searchDepth":86,"depth":86,"links":46677},[],[4122],"2010-11-10T20:41:53","https://synyx.de/blog/negating-matches-in-apache-location/",{},"/blog/negating-matches-in-apache-location",{"title":46566,"description":46575},"negating-matches-in-apache-location","blog/negating-matches-in-apache-location",[41310],"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":46690,"title":46691,"author":46692,"body":46693,"category":46857,"date":46858,"description":46859,"extension":617,"link":46860,"meta":46861,"navigation":499,"path":46862,"seo":46863,"slug":46697,"stem":46865,"tags":46866,"teaser":46867,"__hash__":46868},"blog/blog/using-cmsshell-on-ubuntu-systems.md","Using CmsShell on Ubuntu systems",[41317],{"type":11,"value":46694,"toc":46855},[46695,46698,46707,46710,46716,46723,46737,46740,46745,46748,46753,46759,46833,46836,46842,46845,46850,46853],[14,46696,46691],{"id":46697},"using-cmsshell-on-ubuntu-systems",[18,46699,46700,46701,46704,46705,986],{},"CmsShell is a neat tool to access the ",[585,46702,40251],{"href":46279,"rel":46703},[589]," 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 ",[50,46706,45305],{},[18,46708,46709],{},"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.",[18,46711,46712,46713],{},"First be sure that it’s executable: ",[50,46714,46715],{},"chmod +x cmsshell.sh",[18,46717,46718,46719,46722],{},"When trying to run it using ",[50,46720,46721],{},"./cmsshell.sh"," you will very likely see an error:",[43,46724,46726],{"className":13667,"code":46725,"language":13669,"meta":48,"style":48},"\nbash: ./cmsshell.sh: /bin/sh^M: bad interpreter: No such file or directory\n\n",[50,46727,46728,46732],{"__ignoreMap":48},[53,46729,46730],{"class":55,"line":56},[53,46731,500],{"emptyLinePlaceholder":499},[53,46733,46734],{"class":55,"line":86},[53,46735,46736],{},"bash: ./cmsshell.sh: /bin/sh^M: bad interpreter: No such file or directory\n",[18,46738,46739],{},"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):",[18,46741,46742],{},[50,46743,46744],{},"sudo apt-get install tofrodos",[18,46746,46747],{},"Run it on the file by issuing",[18,46749,46750],{},[50,46751,46752],{},"dos2unix cmsshell.sh",[18,46754,46755,46756,46758],{},"Try to run the file again: ",[50,46757,46721],{},", which will result in another error:",[43,46760,46762],{"className":13667,"code":46761,"language":13669,"meta":48,"style":48},"\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",[50,46763,46764,46768,46773,46778,46783,46788,46793,46798,46803,46808,46813,46818,46823,46828],{"__ignoreMap":48},[53,46765,46766],{"class":55,"line":56},[53,46767,500],{"emptyLinePlaceholder":499},[53,46769,46770],{"class":55,"line":86},[53,46771,46772],{},"/cmsshell.sh: 8: pushd: not found\n",[53,46774,46775],{"class":55,"line":126},[53,46776,46777],{},"./cmsshell.sh: 9: dirs: not found\n",[53,46779,46780],{"class":55,"line":163},[53,46781,46782],{},"./cmsshell.sh: 10: popd: not found\n",[53,46784,46785],{"class":55,"line":186},[53,46786,46787],{},"Exception in thread \"main\" java.lang.NoClassDefFoundError: org/opencms/main/CmsShell\n",[53,46789,46790],{"class":55,"line":221},[53,46791,46792],{},"Caused by: java.lang.ClassNotFoundException: org.opencms.main.CmsShell\n",[53,46794,46795],{"class":55,"line":242},[53,46796,46797],{}," at java.net.URLClassLoader$1.run(URLClassLoader.java:200)\n",[53,46799,46800],{"class":55,"line":273},[53,46801,46802],{}," at java.security.AccessController.doPrivileged(Native Method)\n",[53,46804,46805],{"class":55,"line":279},[53,46806,46807],{}," at java.net.URLClassLoader.findClass(URLClassLoader.java:188)\n",[53,46809,46810],{"class":55,"line":496},[53,46811,46812],{}," at java.lang.ClassLoader.loadClass(ClassLoader.java:307)\n",[53,46814,46815],{"class":55,"line":503},[53,46816,46817],{}," at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)\n",[53,46819,46820],{"class":55,"line":509},[53,46821,46822],{}," at java.lang.ClassLoader.loadClass(ClassLoader.java:252)\n",[53,46824,46825],{"class":55,"line":515},[53,46826,46827],{}," at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)\n",[53,46829,46830],{"class":55,"line":521},[53,46831,46832],{},"Could not find the main class: org.opencms.main.CmsShell. Program will exit.\n",[18,46834,46835],{},"To make it work you need to open the script and change the line",[18,46837,46838,46841],{},[50,46839,46840],{},"OPENCMS_BASE=","dirs +0``",[18,46843,46844],{},"to",[18,46846,46847,46849],{},[50,46848,46840],{},"pwd``",[18,46851,46852],{},"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.",[607,46854,989],{},{"title":48,"searchDepth":86,"depth":86,"links":46856},[],[4122,996],"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":46691,"description":46864},"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",[34779,37693],"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":46870,"title":46871,"author":46872,"body":46873,"category":46994,"date":46995,"description":46996,"extension":617,"link":46997,"meta":46998,"navigation":499,"path":46999,"seo":47000,"slug":46877,"stem":47001,"tags":47002,"teaser":47006,"__hash__":47007},"blog/blog/maven-2-inheritance-before-interpolation.md","Maven 2: Inheritance before Interpolation",[11420],{"type":11,"value":46874,"toc":46992},[46875,46878,46881,46888,46897,46904,46908,46911,46915,46919,46930,46933,46942,46945,46948,46955,46961,46964,46969,46974,46980,46983,46986,46989],[14,46876,46871],{"id":46877},"maven-2-inheritance-before-interpolation",[18,46879,46880],{},"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.",[18,46882,46883,46887],{},[585,46884,46886],{"href":25317,"rel":46885},[589],"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.",[18,46889,46890,46891,46896],{},"It is\na ",[585,46892,46895],{"href":46893,"rel":46894},"http://www.sonatype.com/people/2010/08/how-to-migrate-from-ant-to-maven-project-structure/",[589],"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”.",[18,46898,46899,46900,986],{},"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 ",[585,46901,29857],{"href":46902,"rel":46903},"http://nexus.sonatype.org/",[589],[18,46905,4067,46906],{},[651,46907],{},[6175,46909,46910],{},"\ninternal.nexus.example.org\n",[46912,46913,46914],"name",{},"\nInternal Repo\n",[46916,46917,46918],"url",{},"\n${infrastructure.nexus.host}/content/groups/internal\n",[18,46920,46921,46922,46925,46926,46929],{},"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 ",[27,46923,46924],{},"Interpolation",". At some other place (within a pom or within global\nconfiguration) you are able to set the property ",[50,46927,46928],{},"infrastructure.nexus.host",". We did this since the hostnames are subject\nto change, as soon as the projects infrastructure is moved to our customers.",[18,46931,46932],{},"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.",[18,46934,46935,46936,46941],{},"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 ",[585,46937,46940],{"href":46938,"rel":46939},"https://web.archive.org/web/20220313074201/https://repo1.maven.org/maven2/",[589],"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.",[18,46943,46944],{},"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).",[18,46946,46947],{},"But as soon as you try to build your project maven fails:",[18,46949,46950,46951],{},"`$ mvn validate -Dinfrastructure.nexus.host=",[585,46952,46953],{"href":46953,"rel":46954},"http://example.org",[589],[18,46956,46957,46960],{},[53,46958,46959],{},"INFO"," Scanning for projects...",[18,46962,46963],{},"Downloading: $\n{infrastructure.nexus.host}/content/groups/internal/org/example/parent/1.0.0-SNAPSHOT/parent-1.0.0-SNAPSHOT.pom",[18,46965,46966,46968],{},[53,46967,46959],{}," 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)",[18,46970,46971,46973],{},[53,46972,46959],{}," ------------------------------------------------------------------------",[18,46975,46976,46979],{},[53,46977,46978],{},"ERROR"," FATAL ERROR",[18,46981,46982],{},"...`",[18,46984,46985],{},"As you could see, in the URL where maven tried to download the parent-project it did not resolve the property.",[18,46987,46988],{},"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?",[18,46990,46991],{},"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":48,"searchDepth":86,"depth":86,"links":46993},[],[613],"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":46871,"description":46880},"blog/maven-2-inheritance-before-interpolation",[47003,47004,10977,47005],"build","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":47009,"title":47010,"author":47011,"body":47012,"category":47098,"date":47099,"description":47019,"extension":617,"link":47100,"meta":47101,"navigation":499,"path":47102,"seo":47103,"slug":47104,"stem":47105,"tags":47106,"teaser":47108,"__hash__":47109},"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",[37639],{"type":11,"value":47013,"toc":47096},[47014,47017,47020,47023,47026,47029,47036,47039,47047,47050,47053,47056,47059,47061,47064,47069,47072,47075,47078,47081,47084,47087,47090,47093],[14,47015,47010],{"id":47016},"simple-shell-script-to-use-dictleoorg-in-your-shell",[18,47018,47019],{},"Just create a new file like “vim leo”.",[18,47021,47022],{},"Insert the following script code:",[18,47024,47025],{},"`#!/bin/sh",[18,47027,47028],{},"t(){",[18,47030,47031,47032,47035],{},"while ",[53,47033,47034],{}," -n \"$1\"","; do",[18,47037,47038],{},"T=/tmp/$$.html",[18,47040,47041,47042],{},"lynx -source \"",[585,47043,47046],{"href":47044,"rel":47045},"http://dict.leo.org/?search=$1%22%7C",[589],"http://dict.leo.org/?search=$1\"|",[18,47048,47049],{},"grep results >$T",[18,47051,47052],{},"w3m -dump $T",[18,47054,47055],{},"rm $T",[18,47057,47058],{},"shift",[18,47060,13332],{},[18,47062,47063],{},"}",[18,47065,47066,47068],{},[53,47067,47034],{},"&&\\",[18,47070,47071],{},"t \"$@\"||\\",[18,47073,47074],{},"while read -ep dict2> W; do",[18,47076,47077],{},"t $W|more",[18,47079,47080],{},"done`",[18,47082,47083],{},"You need lynx and w3m.",[18,47085,47086],{},"Make the file executable (chmod +x leo)",[18,47088,47089],{},"enjoy 😉",[18,47091,47092],{},"e.g. ./leo Übersetzung",[18,47094,47095],{},"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":48,"searchDepth":86,"depth":86,"links":47097},[],[613],"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":47010,"description":47019},"simple-shell-script-to-use-dict-leo-org-in-your-shell","blog/simple-shell-script-to-use-dict-leo-org-in-your-shell",[47107,31941],"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…","9eBgoxgIENVKQIAOSW3KP4NaM4V9eUcPjxoOoIXTX9E",{"id":47111,"title":47112,"author":47113,"body":47114,"category":47150,"date":47151,"description":47152,"extension":617,"link":47153,"meta":47154,"navigation":499,"path":47155,"seo":47156,"slug":47118,"stem":47157,"tags":47158,"teaser":47159,"__hash__":47160},"blog/blog/context-reload-with-tomcat.md","Context reload with Tomcat",[41317],{"type":11,"value":47115,"toc":47148},[47116,47119,47126,47129,47134,47137,47140],[14,47117,47112],{"id":47118},"context-reload-with-tomcat",[18,47120,47121,47122,47125],{},"Ever wondered why ",[585,47123,29848],{"href":29846,"rel":47124},[589]," reloads the context when editing web.xml?",[18,47127,47128],{},"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",[18,47130,47131],{},[50,47132,47133],{},"\u003CWatchedResource>WEB-INF/web.xml\u003C/WatchedResource>",[18,47135,47136],{},"which triggers the reload for any web.xml.",[18,47138,47139],{},"You can either add more resources here or, preferably, add your own context configuration with your resources.",[18,47141,47142,47143,986],{},"Find out more about context configuration in\nthe ",[585,47144,47147],{"href":47145,"rel":47146},"http://tomcat.apache.org/tomcat-6.0-doc/config/context.html",[589],"Tomcat documentation",{"title":48,"searchDepth":86,"depth":86,"links":47149},[],[613,996],"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":47112,"description":47152},"blog/context-reload-with-tomcat",[26512],"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":47162,"title":47163,"author":47164,"body":47165,"category":47240,"date":47241,"description":47242,"extension":617,"link":47243,"meta":47244,"navigation":499,"path":47245,"seo":47246,"slug":47169,"stem":47247,"tags":47248,"teaser":47249,"__hash__":47250},"blog/blog/console-logging-with-opencms.md","Console logging with OpenCms",[41317],{"type":11,"value":47166,"toc":47238},[47167,47170,47175,47178,47204,47206,47233,47236],[14,47168,47163],{"id":47169},"console-logging-with-opencms",[18,47171,47172],{},[573,47173,47174],{},"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.",[18,47176,47177],{},"The log4j ConsoleAppender is already configured in OpenCms log4j.properties but not enabled by default. To enable it\nchange the configuration",[43,47179,47183],{"className":47180,"code":47181,"language":47182,"meta":48,"style":48},"language-plain shiki shiki-themes github-light github-dark","\nlog4j.rootLogger=\\\n ERROR,\\\n OC\n\n","plain",[50,47184,47185,47189,47194,47199],{"__ignoreMap":48},[53,47186,47187],{"class":55,"line":56},[53,47188,500],{"emptyLinePlaceholder":499},[53,47190,47191],{"class":55,"line":86},[53,47192,47193],{},"log4j.rootLogger=\\\n",[53,47195,47196],{"class":55,"line":126},[53,47197,47198],{}," ERROR,\\\n",[53,47200,47201],{"class":55,"line":163},[53,47202,47203],{}," OC\n",[18,47205,46844],{},[43,47207,47209],{"className":47180,"code":47208,"language":47182,"meta":48,"style":48},"\nlog4j.rootLogger=\\\n ERROR,\\\n OC,\\\n CONSOLE\n\n",[50,47210,47211,47215,47219,47223,47228],{"__ignoreMap":48},[53,47212,47213],{"class":55,"line":56},[53,47214,500],{"emptyLinePlaceholder":499},[53,47216,47217],{"class":55,"line":86},[53,47218,47193],{},[53,47220,47221],{"class":55,"line":126},[53,47222,47198],{},[53,47224,47225],{"class":55,"line":163},[53,47226,47227],{}," OC,\\\n",[53,47229,47230],{"class":55,"line":186},[53,47231,47232],{}," CONSOLE\n",[18,47234,47235],{},"This is useful when starting OpenCms from within an IDE like Netbeans or Eclipse which display the Console window by\ndefault.",[607,47237,989],{},{"title":48,"searchDepth":86,"depth":86,"links":47239},[],[613,996],"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":47163,"description":47174},"blog/console-logging-with-opencms",[37693],"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":47252,"title":47253,"author":47254,"body":47255,"category":47325,"date":47326,"description":47327,"extension":617,"link":47328,"meta":47329,"navigation":499,"path":47330,"seo":47331,"slug":47333,"stem":47334,"tags":47335,"teaser":47338,"__hash__":47339},"blog/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann.md","'Fragile Agile' von Pavlo Baron und Michael Hüttermann",[26052],{"type":11,"value":47256,"toc":47323},[47257,47261,47279,47284,47287,47290,47295,47298,47303,47306,47309,47312,47315,47318],[14,47258,47260],{"id":47259},"fragile-agile-von-pavlo-baron-und-michael-hüttermann","\"Fragile Agile\" von Pavlo Baron und Michael Hüttermann",[18,47262,47263,47267,47268,1628,47273,47278],{},[1773,47264],{"alt":47265,"src":47266},"\"fragile_agile\"","https://media.synyx.de/uploads//2010/09/fragile_agile.png","\nGespannt habe ich auf das neue Buch der Kollegen ",[585,47269,47272],{"href":47270,"rel":47271},"http://www.pbit.org",[589],"Baron",[585,47274,47277],{"href":47275,"rel":47276},"http://huettermann.net/",[589],"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.",[18,47280,47281],{},[573,47282,47283],{},"“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”",[18,47285,47286],{},"Heutzutage wird Agilität fast immer direkt gleichgesetzt mit Scrum und/oder Kanban.",[18,47288,47289],{},"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.",[18,47291,47292],{},[573,47293,47294],{},"“Truckfaktor: Die Anzahl von Menschen im Projektteam, die von einem Truck erfasst werden müssen, bevor das Projekt in\nernsthafte Schwierigkeiten gerät”",[18,47296,47297],{},"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.",[18,47299,47300],{},[573,47301,47302],{},"“Sie arbeiten schnell und somit agil? Falsch! Agil heißt, den Kunden zufriedenzustellen, nicht schnell etwas\nherunterzuklopfen”",[18,47304,47305],{},"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 😉",[18,47307,47308],{},"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.",[18,47310,47311],{},"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!",[18,47313,47314],{},"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.",[18,47316,47317],{},"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 🙂 )",[18,47319,47320],{},[573,47321,47322],{},"“Design is not what it looks like and feels like. Design is how it works!” (Steve Jobs)",{"title":48,"searchDepth":86,"depth":86,"links":47324},[],[614],"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":47253,"description":47332},"\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",[15667,47336,47337,19860,44991,25655,4232],"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…","i8BRfnraw8pkykCcNeMsM3fcbvrjInhdaciQUi0tPj8",{"id":47341,"title":47342,"author":47343,"body":47344,"category":47811,"date":47812,"description":47813,"extension":617,"link":47814,"meta":47815,"navigation":499,"path":47816,"seo":47817,"slug":47348,"stem":47819,"tags":47820,"teaser":47823,"__hash__":47824},"blog/blog/android-roboguice-against-oncreate-boilerplate.md","Android: RoboGuice against onCreate boilerplate",[44479],{"type":11,"value":47345,"toc":47805},[47346,47349,47360,47364,47392,47396,47399,47584,47590,47736,47742,47746,47784,47788,47800,47803],[14,47347,47342],{"id":47348},"android-roboguice-against-oncreate-boilerplate",[18,47350,47351,47352,47355,47356,47359],{},"When prototyping Android activities with a lot of view elements, the ",[50,47353,47354],{},"onCreate"," method can quickly become cluttered.\nSetup code that simply retrieves the views from the declarative layout (by using ",[50,47357,47358],{},"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!",[2352,47361,47363],{"id":47362},"enter-dependency-injection","Enter dependency injection",[18,47365,47366,47367,47372,47373,47376,47377,47382,47383,47388,47389,47391],{},"A popular design pattern — usually accompanied by a framework — to get rid of object setup clutter\nis ",[585,47368,47371],{"href":47369,"rel":47370},"http://en.wikipedia.org/wiki/Dependency_injection",[589],"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 ",[573,47374,47375],{},"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 ",[585,47378,47381],{"href":47379,"rel":47380},"http://code.google.com/p/google-guice/",[589],"Google Guice",". For the latter a young but promising add-on\nlibrary has come to fruition: ",[585,47384,47387],{"href":47385,"rel":47386},"http://code.google.com/p/roboguice/",[589],"RoboGuice"," makes DI available for your Android\nclasses. We will walk you through a small example, showing how it simplifies your ",[50,47390,47354],{}," 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.",[2352,47393,47395],{"id":47394},"before-and-after","Before and after",[18,47397,47398],{},"We start right away with a small code snippet from an Android activity:",[43,47400,47402],{"className":288,"code":47401,"language":290,"meta":48,"style":48},"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",[50,47403,47404,47409,47414,47419,47424,47429,47434,47439,47444,47449,47454,47459,47464,47469,47473,47477,47482,47487,47492,47497,47502,47507,47512,47517,47522,47527,47532,47537,47542,47547,47552,47556,47561,47566,47571,47576,47580],{"__ignoreMap":48},[53,47405,47406],{"class":55,"line":56},[53,47407,47408],{},"public class SpiderActivity extends Activity {\n",[53,47410,47411],{"class":55,"line":86},[53,47412,47413],{}," // [..snip..] lots of other members not needed for the example\n",[53,47415,47416],{"class":55,"line":126},[53,47417,47418],{}," private RelativeLayout rating;\n",[53,47420,47421],{"class":55,"line":163},[53,47422,47423],{}," private ImageView ratingStars;\n",[53,47425,47426],{"class":55,"line":186},[53,47427,47428],{}," private Button infoButton;\n",[53,47430,47431],{"class":55,"line":221},[53,47432,47433],{}," private TextView type;\n",[53,47435,47436],{"class":55,"line":242},[53,47437,47438],{}," private TextView language;\n",[53,47440,47441],{"class":55,"line":273},[53,47442,47443],{}," private TextView spider;\n",[53,47445,47446],{"class":55,"line":279},[53,47447,47448],{}," private TextView translation;\n",[53,47450,47451],{"class":55,"line":496},[53,47452,47453],{}," private TextView author;\n",[53,47455,47456],{"class":55,"line":503},[53,47457,47458],{}," private TextView page;\n",[53,47460,47461],{"class":55,"line":509},[53,47462,47463],{}," private RatingBar ratingBar;\n",[53,47465,47466],{"class":55,"line":515},[53,47467,47468],{}," private Handler handler;\n",[53,47470,47471],{"class":55,"line":521},[53,47472,13038],{},[53,47474,47475],{"class":55,"line":527},[53,47476,13043],{},[53,47478,47479],{"class":55,"line":533},[53,47480,47481],{}," setContentView(R.layout.main);\n",[53,47483,47484],{"class":55,"line":539},[53,47485,47486],{}," infoButton = (Button) findViewById(R.id.InfoButton);\n",[53,47488,47489],{"class":55,"line":545},[53,47490,47491],{}," infoButton.setOnClickListener(new InfosOnClickListener());\n",[53,47493,47494],{"class":55,"line":2070},[53,47495,47496],{}," rating = (RelativeLayout) findViewById(R.id.ratingDialog);\n",[53,47498,47499],{"class":55,"line":2075},[53,47500,47501],{}," ratingStars = (ImageView) findViewById(R.id.ratingStars);\n",[53,47503,47504],{"class":55,"line":2081},[53,47505,47506],{}," ratingStars.setOnClickListener(new MenuOnClickListener());\n",[53,47508,47509],{"class":55,"line":2087},[53,47510,47511],{}," type = (TextView) findViewById(R.id.TypeText);\n",[53,47513,47514],{"class":55,"line":2092},[53,47515,47516],{}," language = (TextView) findViewById(R.id.LanguageText);\n",[53,47518,47519],{"class":55,"line":2097},[53,47520,47521],{}," spider = (TextView) findViewById(R.id.SpiderText);\n",[53,47523,47524],{"class":55,"line":2103},[53,47525,47526],{}," translation = (TextView) findViewById(R.id.TranslationText);\n",[53,47528,47529],{"class":55,"line":2109},[53,47530,47531],{}," author = (TextView) findViewById(R.id.AuthorText);\n",[53,47533,47534],{"class":55,"line":2115},[53,47535,47536],{}," page = (TextView) findViewById(R.id.PageText);\n",[53,47538,47539],{"class":55,"line":2120},[53,47540,47541],{}," ratingBar = (RatingBar) findViewById(R.id.ratingBar);\n",[53,47543,47544],{"class":55,"line":2946},[53,47545,47546],{}," if (hasUnusualDeviceDimensions()) {\n",[53,47548,47549],{"class":55,"line":2952},[53,47550,47551],{}," adjustLayout();\n",[53,47553,47554],{"class":55,"line":2964},[53,47555,12712],{},[53,47557,47558],{"class":55,"line":2973},[53,47559,47560],{}," if (!isLayoutSupported()) {\n",[53,47562,47563],{"class":55,"line":2979},[53,47564,47565],{}," Toast.makeText(this.getApplicationContext(),\n",[53,47567,47568],{"class":55,"line":2990},[53,47569,47570],{}," \"Sorry, this phone resolution is not supported.\",\n",[53,47572,47573],{"class":55,"line":10443},[53,47574,47575],{}," Toast.LENGTH_LONG).show();\n",[53,47577,47578],{"class":55,"line":26443},[53,47579,12712],{},[53,47581,47582],{"class":55,"line":26448},[53,47583,860],{},[18,47585,47586,47587,47589],{},"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 ",[50,47588,47354],{}," method. Let’s see how this would look like with RoboGuice\napplied to your project:",[43,47591,47593],{"className":288,"code":47592,"language":290,"meta":48,"style":48},"// 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",[50,47594,47595,47600,47605,47610,47615,47620,47625,47630,47635,47640,47645,47650,47655,47660,47665,47670,47674,47678,47683,47687,47692,47696,47700,47704,47708,47712,47716,47720,47724,47728,47732],{"__ignoreMap":48},[53,47596,47597],{"class":55,"line":56},[53,47598,47599],{},"// these are the needed classes from the RoboGuice framework\n",[53,47601,47602],{"class":55,"line":86},[53,47603,47604],{},"import roboguice.activity.RoboActivity;\n",[53,47606,47607],{"class":55,"line":126},[53,47608,47609],{},"import roboguice.inject.InjectView;\n",[53,47611,47612],{"class":55,"line":163},[53,47613,47614],{},"// [..snip..]\n",[53,47616,47617],{"class":55,"line":186},[53,47618,47619],{},"public class SpiderActivity extends RoboActivity {\n",[53,47621,47622],{"class":55,"line":221},[53,47623,47624],{}," @InjectView(R.id.ratingDialog ) private RelativeLayout rating;\n",[53,47626,47627],{"class":55,"line":242},[53,47628,47629],{}," @InjectView(R.id.ratingStars) private ImageView ratingStars;\n",[53,47631,47632],{"class":55,"line":273},[53,47633,47634],{}," @InjectView(R.id.InfoButton) private Button infoButton;\n",[53,47636,47637],{"class":55,"line":279},[53,47638,47639],{}," @InjectView(R.id.TypeText) private TextView type;\n",[53,47641,47642],{"class":55,"line":496},[53,47643,47644],{}," @InjectView(R.id.LanguageText) private TextView language;\n",[53,47646,47647],{"class":55,"line":503},[53,47648,47649],{}," @InjectView(R.id.SpiderText) private TextView spider;\n",[53,47651,47652],{"class":55,"line":509},[53,47653,47654],{}," @InjectView(R.id.TranslationText) private TextView translation;\n",[53,47656,47657],{"class":55,"line":515},[53,47658,47659],{}," @InjectView(R.id.AuthorText) private TextView author;\n",[53,47661,47662],{"class":55,"line":521},[53,47663,47664],{}," @InjectView(R.id.PageText) private TextView page;\n",[53,47666,47667],{"class":55,"line":527},[53,47668,47669],{}," @InjectView(R.id.ratingBar) private RatingBar ratingBar;\n",[53,47671,47672],{"class":55,"line":533},[53,47673,13038],{},[53,47675,47676],{"class":55,"line":539},[53,47677,13043],{},[53,47679,47680],{"class":55,"line":545},[53,47681,47682],{}," // the following call actually injects your views ...\n",[53,47684,47685],{"class":55,"line":2070},[53,47686,47481],{},[53,47688,47689],{"class":55,"line":2075},[53,47690,47691],{}," // they are available afterwards\n",[53,47693,47694],{"class":55,"line":2081},[53,47695,47491],{},[53,47697,47698],{"class":55,"line":2087},[53,47699,47506],{},[53,47701,47702],{"class":55,"line":2092},[53,47703,47546],{},[53,47705,47706],{"class":55,"line":2097},[53,47707,47551],{},[53,47709,47710],{"class":55,"line":2103},[53,47711,12712],{},[53,47713,47714],{"class":55,"line":2109},[53,47715,47560],{},[53,47717,47718],{"class":55,"line":2115},[53,47719,47565],{},[53,47721,47722],{"class":55,"line":2120},[53,47723,47570],{},[53,47725,47726],{"class":55,"line":2946},[53,47727,47575],{},[53,47729,47730],{"class":55,"line":2952},[53,47731,12712],{},[53,47733,47734],{"class":55,"line":2964},[53,47735,860],{},[18,47737,47738,47739,47741],{},"See how the ",[50,47740,47354],{}," 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.",[2352,47743,47745],{"id":47744},"setting-up-roboguice","Setting up RoboGuice",[18,47747,47748,47749,47754,47755,47759,47760,47763,47764,47767,47768,47771,47772,47775,47776,47779,47780,47783],{},"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 ",[585,47750,47753],{"href":47751,"rel":47752},"https://web.archive.org/web/20160713065637/http://google-guice.googlecode.com:80/files/guice-2.0-no_aop.jar",[589],"Guice 2.0"," (\nwhich is the base for RoboGuice)\nand ",[585,47756,47387],{"href":47757,"rel":47758},"http://download.java.net/maven/2/roboguice/roboguice/1.1-SNAPSHOT/roboguice-1.1-20100408.222944-3.jar",[589]," (\nin our example we used the development version 1.1) into a ",[50,47761,47762],{},"lib"," subdirectory of your project. Add both the libraries\nto your build path by selecting ",[27,47765,47766],{},"Build Path > Add to Build Path"," from the context menu of each of the libraries.\nFinally a small change to your ",[50,47769,47770],{},"AndroidManifest.xml"," will be needed, to make RoboGuice work. In the ",[27,47773,47774],{},"Application"," tab\nset the ",[27,47777,47778],{},"Name"," attribute to ",[50,47781,47782],{},"roboguice.application.RoboApplication"," (or make your own Application class inherit from\nit).",[2352,47785,47787],{"id":47786},"finishing-words","Finishing words",[18,47789,47790,47791,47795,47796,47799],{},"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 ",[585,47792,47794],{"href":47379,"rel":47793},[589],"Guice"," pages introduce the general idioms of the framework, while\nthe ",[585,47797,47387],{"href":47385,"rel":47798},[589]," pages contain some more examples and the documentation.",[18,47801,47802],{},"I hope this short introduction proves useful and helps you making your applications code more readable and testable in\nthe end.",[607,47804,989],{},{"title":48,"searchDepth":86,"depth":86,"links":47806},[47807,47808,47809,47810],{"id":47362,"depth":86,"text":47363},{"id":47394,"depth":86,"text":47395},{"id":47744,"depth":86,"text":47745},{"id":47786,"depth":86,"text":47787},[4516,997],"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":47342,"description":47818},"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",[4526,47821,47822,997],"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":47826,"title":47827,"author":47828,"body":47829,"category":47842,"date":47843,"description":48,"extension":617,"link":47844,"meta":47845,"navigation":499,"path":47846,"seo":47847,"slug":47833,"stem":47848,"tags":47849,"teaser":47852,"__hash__":47853},"blog/blog/apn-device-tokens.md","APN Device Tokens",[46383],{"type":11,"value":47830,"toc":47840},[47831,47834],[14,47832,47827],{"id":47833},"apn-device-tokens",[18,47835,47836],{},[1773,47837],{"alt":47838,"src":47839},"I Think I Spider","https://media.synyx.de/uploads//2010/07/512px.png",{"title":48,"searchDepth":86,"depth":86,"links":47841},[],[4516,4650,997],"2010-09-14T06:46:26","https://synyx.de/blog/apn-device-tokens/",{},"/blog/apn-device-tokens",{"title":47827,"description":48},"blog/apn-device-tokens",[47850,37119,46405,46406,47851],"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":47855,"title":47856,"author":47857,"body":47858,"category":47904,"date":47905,"description":47906,"extension":617,"link":47907,"meta":47908,"navigation":499,"path":47909,"seo":47910,"slug":47911,"stem":47912,"tags":47913,"teaser":47915,"__hash__":47916},"blog/blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse.md","Wer zu spät kommt, den bestraft die Scrum-Fail-Tasse.",[11619],{"type":11,"value":47859,"toc":47902},[47860,47863,47870,47878,47887,47899],[14,47861,47856],{"id":47862},"wer-zu-spät-kommt-den-bestraft-die-scrum-fail-tasse",[18,47864,47865,47869],{},[1773,47866],{"alt":47867,"src":47868},"\"scrum_02\"","https://media.synyx.de/uploads//2010/09/scrum_02.jpg","\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.",[18,47871,47872,47873,47877],{},"Für die Daily ",[585,47874,47876],{"href":5455,"rel":47875},[589],"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.",[18,47879,47880,47881,47886],{},"Und was ist daran jetzt sinnvoll? Ganz einfach: ",[585,47882,47885],{"href":47883,"rel":47884},"http://www.kiva.org/",[589],"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.",[18,47888,47889,47892,47893,47898],{},[585,47890,41094],{"href":41092,"rel":47891},[589],", 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 ",[585,47894,47897],{"href":47895,"rel":47896},"http://www.kiva.org/team/synyx",[589],"Kiva Lending Team Seite"," von Synyx nachzulesen\nist.",[18,47900,47901],{},"Auch wenn sich der Scrum Master stets über Pünktlichkeit freut, kann er mit einem zwinkernden Auge, das Positive daran\nsehen 😉",{"title":48,"searchDepth":86,"depth":86,"links":47903},[],[614],"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":47856,"description":47869},"wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse","blog/wer-zu-spat-kommt-den-bestraft-die-scrum-fail-tasse",[47914,4232],"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…","-82TgZjoSmCTpAZH7I8veHScnfFHO9hzN_rscjR8G2c",{"id":47918,"title":47919,"author":47920,"body":47921,"category":48181,"date":48182,"description":48183,"extension":617,"link":48184,"meta":48185,"navigation":499,"path":48186,"seo":48187,"slug":47925,"stem":48189,"tags":48190,"teaser":48191,"__hash__":48192},"blog/blog/on-cross-device-mobile-development-part-2.md","On cross-device mobile development – Part 2",[44479],{"type":11,"value":47922,"toc":48174},[47923,47926,47938,47942,47965,47969,48001,48005,48029,48033,48040,48044,48047,48051,48055,48079,48083,48102,48106,48121,48125,48156,48160,48171],[14,47924,47919],{"id":47925},"on-cross-device-mobile-development-part-2",[18,47927,19757,47928,47933,47934,47937],{},[585,47929,47932],{"href":47930,"rel":47931},"http://mobile.synyx.de/2010/08/on-cross-device-mobile-development-part-1/",[589],"previous part"," of this series we took\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as ",[573,47935,47936],{},"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 …",[2352,47939,47941],{"id":47940},"what-does-native-have-what-web-stack-hasnt","What does native have, what web stack hasn’t?",[18,47943,47944,47945,47948,47949,47952,47953,47956,47957,47960,47961,47964],{},"If you want the answer to this question boiled down to one simple word: ",[27,47946,47947],{},"abstraction",". In an application you don’t\nwant to think in terms of ",[50,47950,47951],{},"\u003Cul>"," elements, to which you append ",[50,47954,47955],{},"\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 ",[50,47958,47959],{},"ListController"," and an\nattached ",[50,47962,47963],{},"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.",[2352,47966,47968],{"id":47967},"competing-with-native-sdks","Competing with native SDKs",[18,47970,47971,47972,47977,47978,47983,47984,47986,47987,47992,47993,47996,47997,48000],{},"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. ",[585,47973,47976],{"href":47974,"rel":47975},"https://web.archive.org/web/20210302121558/https://phonegap.com/",[589],"PhoneGap"," — which we refered to already in\nthe first part of the series — is\nnow ",[585,47979,47982],{"href":47980,"rel":47981},"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/",[589],"fully embraced","\nby Nokia for their Symbian smartphone operating system. Palm has his whole mobile user experience built around ",[573,47985,47936],{},", accordingly naming their operating system ",[585,47988,47991],{"href":47989,"rel":47990},"http://developer.palm.com/",[589],"WebOS",". The need for mobile ",[573,47994,47995],{},"web stack","\nframeworks is growing. The rest of this article will introduce some of those frameworks, but first a short recap of what\n",[27,47998,47999],{},"abstractions"," we would expect from such a framework, compared to their native siblings:",[1217,48002,48004],{"id":48003},"models","Models",[18,48006,48007,48008,48013,48014,48021,48022,986],{},"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 ",[585,48009,48012],{"href":48010,"rel":48011},"http://developer.apple.com/iphone/library/documentation/DataManagement/Conceptual/iPhoneCoreData01/",[589],"CoreData",".\nIt provides modelling tools and stub generation to easily integrate with ",[585,48015,48018],{"href":48016,"rel":48017},"http://developer.apple.com/iphone/library/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html",[589],[50,48019,48020],{},"UITableView","\ncontrols. Android, while staying more on the lower SQLite level, also provides similar integration to their native ",[585,48023,48026],{"href":48024,"rel":48025},"http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html",[589],[50,48027,48028],{},"ListView",[1217,48030,48032],{"id":48031},"views-user-interface-ui-elements","Views & user interface (UI) elements",[18,48034,48035,48039],{},[1773,48036],{"alt":48037,"src":48038},"\"iPhone UI Controls\"","https://media.synyx.de/uploads//2010/07/iPhone-UI-Controls-e1280510112988.png","\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.",[1217,48041,48043],{"id":48042},"controllers","Controllers",[18,48045,48046],{},"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.",[649,48048,48050],{"id":48049},"javascript-frameworks-the-small-the-huge-and-the-familiar","JavaScript frameworks … the small, the huge and the familiar",[1217,48052,48054],{"id":48053},"jqtouch","jQTouch",[18,48056,48057,48058,48062,48063,48067,48068,48075,48076,48078],{},"Our first framework is perhaps the one which contradicts our demands the most. ",[585,48059,48054],{"href":48060,"rel":48061},"http://jqtouch.com",[589]," is a\nlightweight layer on the ever-present ",[585,48064,25753],{"href":48065,"rel":48066},"http://jquery.com",[589]," 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 ",[585,48069,48072],{"href":48070,"rel":48071},"http://building-iphone-apps.labs.oreilly.com/",[589],[573,48073,48074],{},"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 ",[573,48077,47995],{}," development on mobile devices.",[1217,48080,48082],{"id":48081},"sencha-touch","Sencha Touch",[18,48084,48085,48090,48091,48095,48096,48101],{},[585,48086,48089],{"href":48087,"rel":48088},"http://www.sencha.com/",[589],"Sencha Labs"," (formerly ExtJS) are already well-known for their desktop browser frameworks.\nWith ",[585,48092,48082],{"href":48093,"rel":48094},"http://www.sencha.com/products/touch/",[589]," 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 ",[585,48097,48100],{"href":48098,"rel":48099},"http://www.sencha.com/products/touch/demos.php",[589],"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.",[1217,48103,48105],{"id":48104},"jquery-mobile","jQuery mobile",[18,48107,48108,48109,48114,48115,48120],{},"For people, who in the past have worked with ",[585,48110,48113],{"href":48111,"rel":48112},"http://jqueryui.com",[589],"jQuery UI"," for web applications, John Resig recently\nannounced ",[585,48116,48119],{"href":48117,"rel":48118},"http://jquerymobile.com/",[589],"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.",[1217,48122,48124],{"id":48123},"google-web-toolkit","Google Web Toolkit",[18,48126,48127,48128,48132,48133,48138,48139,10260,48144,48149,48150,48155],{},"Last but definitely not least, we have to include an old familiar\nfriend: ",[585,48129,48124],{"href":48130,"rel":48131},"http://code.google.com/webtoolkit/",[589]," (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. ",[585,48134,48137],{"href":48135,"rel":48136},"http://code.google.com/p/gwt-mobile-webkit/",[589],"GWT Mobile WebKit"," extends GWT with support for touch\ninterfaces and also bundles libraries to\nabstract ",[585,48140,48143],{"href":48141,"rel":48142},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Geolocation",[589],"geolocation services",[585,48145,48148],{"href":48146,"rel":48147},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Database",[589],"SQLite"," database — something even\nmissing from the original Android SDK. Views can be created programmatically\nor ",[585,48151,48154],{"href":48152,"rel":48153},"http://code.google.com/webtoolkit/doc/latest/DevGuideUiBinder.html",[589],"declaratively"," and provide a rich API for\ncallbacks on user input.",[2352,48157,48159],{"id":48158},"what-is-left-to-be-said","What is left to be said",[18,48161,10933,48162,48164,48165,23086,48167,48170],{},[573,48163,47995],{}," mobile development community is certainly on the move to sophisticated frameworks. From small\nmicroframeworks to full-stack frameworks like ",[573,48166,48082],{},[573,48168,48169],{},"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.",[18,48172,48173],{},"To be continued some time …",{"title":48,"searchDepth":86,"depth":86,"links":48175},[48176,48177,48180],{"id":47940,"depth":86,"text":47941},{"id":47967,"depth":86,"text":47968,"children":48178},[48179],{"id":48049,"depth":126,"text":48050},{"id":48158,"depth":86,"text":48159},[4516],"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":47919,"description":48188},"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",[4526,10147,6831,46406,6991],"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…","YieTuihm_5oZzpoNHWVn3JDuz9uBqr4EfoBq26hjIl0",{"id":48194,"title":48195,"author":48196,"body":48197,"category":48300,"date":48301,"description":48302,"extension":617,"link":48303,"meta":48304,"navigation":499,"path":48305,"seo":48306,"slug":48201,"stem":48308,"tags":48309,"teaser":48311,"__hash__":48312},"blog/blog/froscon-2010.md","FrOSCon 2010",[41317],{"type":11,"value":48198,"toc":48298},[48199,48202,48211,48214,48229,48248,48257,48272,48286,48289],[14,48200,48195],{"id":48201},"froscon-2010",[18,48203,48204,48205,48210],{},"Am 21. und 22.08. fand für mich die 2. ",[585,48206,48209],{"href":48207,"rel":48208},"http://froscon.de/",[589],"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.",[18,48212,48213],{},"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.",[18,48215,48216,48217,48222,48223,48228],{},"Ein recht verbreitetes Thema, das unter anderem gerne im leider\neingestellten ",[585,48218,48221],{"href":48219,"rel":48220},"http://blog.stackoverflow.com/category/podcasts/",[589],"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. ",[585,48224,48227],{"href":48225,"rel":48226},"http://www.lornajane.net/",[589],"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.",[18,48230,48231,48232,48235,48236,48241,48242,48247],{},"Obwohl ich mit den Grundlagen schon vertraut war, waren zwei Vorträge zu ",[585,48233,40174],{"href":40172,"rel":48234},[589]," interessant:\nEiner über MongoDB im generellen von einem Mitarbeiter von ",[585,48237,48240],{"href":48238,"rel":48239},"http://www.10gen.com/",[589],"10gen",", der Firma hinter der\nDatenbank, und einen zur Integration in Ruby on Rails von ",[585,48243,48246],{"href":48244,"rel":48245},"http://jan.krutisch.de/",[589],"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.",[18,48249,48250,48251,48256],{},"“Mobile Linux Development” von Christian Küster ging mit einem für mich neuen Blickwinkel auf\nein ",[585,48252,48255],{"href":48253,"rel":48254},"http://mobile.synyx.de",[589],"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.",[18,48258,48259,48260,48265,48266,48271],{},"Der Vortrag, wegen dem ich mich eigentlich zum Besuch der FrOSCon entschlossen hatte, Ruby on Rails 3\nvon ",[585,48261,48264],{"href":48262,"rel":48263},"http://yehudakatz.com/",[589],"Yehuda Katz",", war leider nicht so technisch, wie ich ihn mir gewünscht hatte. Gerade da er\nvon einem Mann aus dem ",[585,48267,48270],{"href":48268,"rel":48269},"http://rubyonrails.org/merb",[589],"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.",[18,48273,48274,48275,48280,48281,48285],{},"Kurzfristig eingeschoben wurde ein Talk von ",[585,48276,48279],{"href":48277,"rel":48278},"http://kippdata.de/",[589],"Rainer Jung"," zu den Neuerungen\nin ",[585,48282,48284],{"href":46580,"rel":48283},[589],"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 :).",[18,48287,48288],{},"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€.",[18,48290,48291,48292,48297],{},"Und noch ein praktischer Tipp am Rande: Wer kein Problem mit einem kleinen Fußmarsch hat, ist beim sehr freundlichen\nWirt im ",[585,48293,48296],{"href":48294,"rel":48295},"http://www.zum-laternchen.de/",[589],"Laternchen"," bestens aufgehoben.",{"title":48,"searchDepth":86,"depth":86,"links":48299},[],[996],"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":48195,"description":48307},"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",[41310,38233,4231,21435,46498,12072,48310],"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":48314,"title":48315,"author":48316,"body":48317,"category":49827,"date":49828,"description":49829,"extension":617,"link":49830,"meta":49831,"navigation":499,"path":49832,"seo":49833,"slug":48321,"stem":49835,"tags":49836,"teaser":49837,"__hash__":49838},"blog/blog/on-cross-device-mobile-development-part-1.md","On cross-device mobile development – Part 1",[44479],{"type":11,"value":48318,"toc":49821},[48319,48322,48329,48333,48336,48340,48348,48652,48655,49342,49345,49545,49556,49603,49606,49773,49775,49784,49787,49794,49798,49801,49815,49818],[14,48320,48315],{"id":48321},"on-cross-device-mobile-development-part-1",[18,48323,48324,48325,48328],{},"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. ",[573,48326,48327],{},"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.",[2352,48330,48332],{"id":48331},"its-not-in-the-browser","It’s (not) in the browser",[18,48334,48335],{},"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.",[2352,48337,48339],{"id":48338},"of-models-views-controllers","Of models, views & controllers",[18,48341,22349,48342,48347],{},[585,48343,48346],{"href":48344,"rel":48345},"http://building-iphone-apps.labs.oreilly.com/ch04.html",[589],"“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:",[43,48349,48351],{"className":6829,"code":48350,"language":6831,"meta":48,"style":48},"\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",[50,48352,48353,48368,48405,48433,48461,48489,48517,48525,48552,48577,48602,48627],{"__ignoreMap":48},[53,48354,48355,48357,48359,48361,48363,48366],{"class":55,"line":56},[53,48356,6838],{"class":82},[53,48358,577],{"class":6841},[53,48360,6975],{"class":59},[53,48362,390],{"class":82},[53,48364,48365],{"class":63},"\"navigation\"",[53,48367,6860],{"class":82},[53,48369,48370,48372,48374,48377,48379,48381,48383,48386,48389,48391,48394,48397,48399,48401,48403],{"class":55,"line":86},[53,48371,6865],{"class":82},[53,48373,580],{"class":6841},[53,48375,48376],{"class":82},">\u003C",[53,48378,585],{"class":6841},[53,48380,36193],{"class":59},[53,48382,390],{"class":82},[53,48384,48385],{"class":63},"\"current\"",[53,48387,48388],{"class":59}," href",[53,48390,390],{"class":82},[53,48392,48393],{"class":63},"\"#home\"",[53,48395,48396],{"class":82},">Home\u003C/",[53,48398,585],{"class":6841},[53,48400,6982],{"class":82},[53,48402,580],{"class":6841},[53,48404,6860],{"class":82},[53,48406,48407,48409,48411,48413,48415,48417,48419,48422,48425,48427,48429,48431],{"class":55,"line":126},[53,48408,6865],{"class":82},[53,48410,580],{"class":6841},[53,48412,48376],{"class":82},[53,48414,585],{"class":6841},[53,48416,48388],{"class":59},[53,48418,390],{"class":82},[53,48420,48421],{"class":63},"\"#new\"",[53,48423,48424],{"class":82},">New things\u003C/",[53,48426,585],{"class":6841},[53,48428,6982],{"class":82},[53,48430,580],{"class":6841},[53,48432,6860],{"class":82},[53,48434,48435,48437,48439,48441,48443,48445,48447,48450,48453,48455,48457,48459],{"class":55,"line":163},[53,48436,6865],{"class":82},[53,48438,580],{"class":6841},[53,48440,48376],{"class":82},[53,48442,585],{"class":6841},[53,48444,48388],{"class":59},[53,48446,390],{"class":82},[53,48448,48449],{"class":63},"\"#favorites\"",[53,48451,48452],{"class":82},">I like those\u003C/",[53,48454,585],{"class":6841},[53,48456,6982],{"class":82},[53,48458,580],{"class":6841},[53,48460,6860],{"class":82},[53,48462,48463,48465,48467,48469,48471,48473,48475,48478,48481,48483,48485,48487],{"class":55,"line":186},[53,48464,6865],{"class":82},[53,48466,580],{"class":6841},[53,48468,48376],{"class":82},[53,48470,585],{"class":6841},[53,48472,48388],{"class":59},[53,48474,390],{"class":82},[53,48476,48477],{"class":63},"\"#more\"",[53,48479,48480],{"class":82},">More content\u003C/",[53,48482,585],{"class":6841},[53,48484,6982],{"class":82},[53,48486,580],{"class":6841},[53,48488,6860],{"class":82},[53,48490,48491,48493,48495,48497,48499,48501,48503,48506,48509,48511,48513,48515],{"class":55,"line":221},[53,48492,6865],{"class":82},[53,48494,580],{"class":6841},[53,48496,48376],{"class":82},[53,48498,585],{"class":6841},[53,48500,48388],{"class":59},[53,48502,390],{"class":82},[53,48504,48505],{"class":63},"\"#info\"",[53,48507,48508],{"class":82},">About\u003C/",[53,48510,585],{"class":6841},[53,48512,6982],{"class":82},[53,48514,580],{"class":6841},[53,48516,6860],{"class":82},[53,48518,48519,48521,48523],{"class":55,"line":242},[53,48520,6958],{"class":82},[53,48522,577],{"class":6841},[53,48524,6860],{"class":82},[53,48526,48527,48529,48531,48533,48535,48538,48540,48542,48545,48548,48550],{"class":55,"line":273},[53,48528,6838],{"class":82},[53,48530,6817],{"class":6841},[53,48532,36193],{"class":59},[53,48534,390],{"class":82},[53,48536,48537],{"class":63},"\"view\"",[53,48539,6975],{"class":59},[53,48541,390],{"class":82},[53,48543,48544],{"class":63},"\"home\"",[53,48546,48547],{"class":82},">...\u003C/",[53,48549,6817],{"class":6841},[53,48551,6860],{"class":82},[53,48553,48554,48556,48558,48560,48562,48564,48566,48568,48571,48573,48575],{"class":55,"line":279},[53,48555,6838],{"class":82},[53,48557,6817],{"class":6841},[53,48559,36193],{"class":59},[53,48561,390],{"class":82},[53,48563,48537],{"class":63},[53,48565,6975],{"class":59},[53,48567,390],{"class":82},[53,48569,48570],{"class":63},"\"new\"",[53,48572,48547],{"class":82},[53,48574,6817],{"class":6841},[53,48576,6860],{"class":82},[53,48578,48579,48581,48583,48585,48587,48589,48591,48593,48596,48598,48600],{"class":55,"line":496},[53,48580,6838],{"class":82},[53,48582,6817],{"class":6841},[53,48584,36193],{"class":59},[53,48586,390],{"class":82},[53,48588,48537],{"class":63},[53,48590,6975],{"class":59},[53,48592,390],{"class":82},[53,48594,48595],{"class":63},"\"favorites\"",[53,48597,48547],{"class":82},[53,48599,6817],{"class":6841},[53,48601,6860],{"class":82},[53,48603,48604,48606,48608,48610,48612,48614,48616,48618,48621,48623,48625],{"class":55,"line":503},[53,48605,6838],{"class":82},[53,48607,6817],{"class":6841},[53,48609,36193],{"class":59},[53,48611,390],{"class":82},[53,48613,48537],{"class":63},[53,48615,6975],{"class":59},[53,48617,390],{"class":82},[53,48619,48620],{"class":63},"\"more\"",[53,48622,48547],{"class":82},[53,48624,6817],{"class":6841},[53,48626,6860],{"class":82},[53,48628,48629,48631,48633,48635,48637,48639,48641,48643,48646,48648,48650],{"class":55,"line":509},[53,48630,6838],{"class":82},[53,48632,6817],{"class":6841},[53,48634,36193],{"class":59},[53,48636,390],{"class":82},[53,48638,48537],{"class":63},[53,48640,6975],{"class":59},[53,48642,390],{"class":82},[53,48644,48645],{"class":63},"\"info\"",[53,48647,48547],{"class":82},[53,48649,6817],{"class":6841},[53,48651,6860],{"class":82},[18,48653,48654],{},"The navigational items are then replaced with nice button graphics via CSS and positioned on the page.",[43,48656,48659],{"className":48657,"code":48658,"language":10147,"meta":48,"style":48},"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",[50,48660,48661,48666,48673,48685,48698,48711,48725,48736,48747,48751,48760,48772,48783,48787,48797,48807,48817,48831,48844,48856,48870,48874,48891,48907,48919,48923,48942,48957,48961,48975,48990,49002,49006,49024,49039,49043,49057,49072,49085,49089,49107,49122,49126,49140,49155,49168,49172,49186,49201,49211,49223,49235,49248,49261,49265,49270,49279,49289,49301,49313,49325,49338],{"__ignoreMap":48},[53,48662,48663],{"class":55,"line":56},[53,48664,48665],{"class":2530},"/* navigation is a fixed block */\n",[53,48667,48668,48671],{"class":55,"line":86},[53,48669,48670],{"class":59},"#navigation",[53,48672,5795],{"class":82},[53,48674,48675,48678,48680,48683],{"class":55,"line":126},[53,48676,48677],{"class":89}," position",[53,48679,5859],{"class":82},[53,48681,48682],{"class":89},"fixed",[53,48684,1727],{"class":82},[53,48686,48687,48690,48692,48694,48696],{"class":55,"line":163},[53,48688,48689],{"class":89}," top",[53,48691,5859],{"class":82},[53,48693,37264],{"class":89},[53,48695,32859],{"class":389},[53,48697,1727],{"class":82},[53,48699,48700,48703,48705,48707,48709],{"class":55,"line":186},[53,48701,48702],{"class":89}," left",[53,48704,5859],{"class":82},[53,48706,37264],{"class":89},[53,48708,32859],{"class":389},[53,48710,1727],{"class":82},[53,48712,48713,48716,48718,48721,48723],{"class":55,"line":221},[53,48714,48715],{"class":89}," width",[53,48717,5859],{"class":82},[53,48719,48720],{"class":89},"320",[53,48722,32859],{"class":389},[53,48724,1727],{"class":82},[53,48726,48727,48730,48732,48734],{"class":55,"line":242},[53,48728,48729],{"class":89}," margin",[53,48731,5859],{"class":82},[53,48733,37264],{"class":89},[53,48735,1727],{"class":82},[53,48737,48738,48741,48743,48745],{"class":55,"line":273},[53,48739,48740],{"class":89}," padding",[53,48742,5859],{"class":82},[53,48744,37264],{"class":89},[53,48746,1727],{"class":82},[53,48748,48749],{"class":55,"line":279},[53,48750,282],{"class":82},[53,48752,48753,48755,48758],{"class":55,"line":496},[53,48754,48670],{"class":59},[53,48756,48757],{"class":6841}," li",[53,48759,5795],{"class":82},[53,48761,48762,48765,48767,48770],{"class":55,"line":503},[53,48763,48764],{"class":89}," display",[53,48766,5859],{"class":82},[53,48768,48769],{"class":89},"block",[53,48771,1727],{"class":82},[53,48773,48774,48776,48778,48781],{"class":55,"line":509},[53,48775,48677],{"class":89},[53,48777,5859],{"class":82},[53,48779,48780],{"class":89},"absolute",[53,48782,1727],{"class":82},[53,48784,48785],{"class":55,"line":515},[53,48786,282],{"class":82},[53,48788,48789,48791,48793,48795],{"class":55,"line":521},[53,48790,48670],{"class":59},[53,48792,48757],{"class":6841},[53,48794,34442],{"class":6841},[53,48796,5795],{"class":82},[53,48798,48799,48801,48803,48805],{"class":55,"line":527},[53,48800,48764],{"class":89},[53,48802,5859],{"class":82},[53,48804,48769],{"class":89},[53,48806,1727],{"class":82},[53,48808,48809,48811,48813,48815],{"class":55,"line":533},[53,48810,48677],{"class":89},[53,48812,5859],{"class":82},[53,48814,48780],{"class":89},[53,48816,1727],{"class":82},[53,48818,48819,48822,48824,48827,48829],{"class":55,"line":539},[53,48820,48821],{"class":89}," height",[53,48823,5859],{"class":82},[53,48825,48826],{"class":89},"48",[53,48828,32859],{"class":389},[53,48830,1727],{"class":82},[53,48832,48833,48835,48837,48840,48842],{"class":55,"line":545},[53,48834,48715],{"class":89},[53,48836,5859],{"class":82},[53,48838,48839],{"class":89},"80",[53,48841,32859],{"class":389},[53,48843,1727],{"class":82},[53,48845,48846,48848,48850,48852,48854],{"class":55,"line":2070},[53,48847,48689],{"class":89},[53,48849,5859],{"class":82},[53,48851,37264],{"class":89},[53,48853,32859],{"class":389},[53,48855,1727],{"class":82},[53,48857,48858,48861,48863,48866,48868],{"class":55,"line":2075},[53,48859,48860],{"class":89}," text-indent",[53,48862,5859],{"class":82},[53,48864,48865],{"class":89},"-9999",[53,48867,32859],{"class":389},[53,48869,1727],{"class":82},[53,48871,48872],{"class":55,"line":2081},[53,48873,282],{"class":82},[53,48875,48876,48878,48881,48884,48886,48888],{"class":55,"line":2087},[53,48877,585],{"class":6841},[53,48879,48880],{"class":82},"[",[53,48882,48883],{"class":59},"href",[53,48885,390],{"class":389},[53,48887,48393],{"class":63},[53,48889,48890],{"class":82},"] {\n",[53,48892,48893,48896,48898,48900,48902,48905],{"class":55,"line":2092},[53,48894,48895],{"class":89}," background",[53,48897,5859],{"class":82},[53,48899,46916],{"class":89},[53,48901,1067],{"class":82},[53,48903,48904],{"class":5805},"home.png",[53,48906,1079],{"class":82},[53,48908,48909,48911,48913,48915,48917],{"class":55,"line":2097},[53,48910,48702],{"class":89},[53,48912,5859],{"class":82},[53,48914,37264],{"class":89},[53,48916,32859],{"class":389},[53,48918,1727],{"class":82},[53,48920,48921],{"class":55,"line":2103},[53,48922,282],{"class":82},[53,48924,48925,48927,48929,48931,48933,48935,48937,48940],{"class":55,"line":2109},[53,48926,585],{"class":6841},[53,48928,48880],{"class":82},[53,48930,48883],{"class":59},[53,48932,390],{"class":389},[53,48934,48393],{"class":63},[53,48936,7599],{"class":82},[53,48938,48939],{"class":59},".current",[53,48941,5795],{"class":82},[53,48943,48944,48946,48948,48950,48952,48955],{"class":55,"line":2115},[53,48945,48895],{"class":89},[53,48947,5859],{"class":82},[53,48949,46916],{"class":89},[53,48951,1067],{"class":82},[53,48953,48954],{"class":5805},"home-current.png",[53,48956,1079],{"class":82},[53,48958,48959],{"class":55,"line":2120},[53,48960,282],{"class":82},[53,48962,48963,48965,48967,48969,48971,48973],{"class":55,"line":2946},[53,48964,585],{"class":6841},[53,48966,48880],{"class":82},[53,48968,48883],{"class":59},[53,48970,390],{"class":389},[53,48972,48421],{"class":63},[53,48974,48890],{"class":82},[53,48976,48977,48979,48981,48983,48985,48988],{"class":55,"line":2952},[53,48978,48895],{"class":89},[53,48980,5859],{"class":82},[53,48982,46916],{"class":89},[53,48984,1067],{"class":82},[53,48986,48987],{"class":5805},"new.png",[53,48989,1079],{"class":82},[53,48991,48992,48994,48996,48998,49000],{"class":55,"line":2964},[53,48993,48702],{"class":89},[53,48995,5859],{"class":82},[53,48997,48839],{"class":89},[53,48999,32859],{"class":389},[53,49001,1727],{"class":82},[53,49003,49004],{"class":55,"line":2973},[53,49005,282],{"class":82},[53,49007,49008,49010,49012,49014,49016,49018,49020,49022],{"class":55,"line":2979},[53,49009,585],{"class":6841},[53,49011,48880],{"class":82},[53,49013,48883],{"class":59},[53,49015,390],{"class":389},[53,49017,48421],{"class":63},[53,49019,7599],{"class":82},[53,49021,48939],{"class":59},[53,49023,5795],{"class":82},[53,49025,49026,49028,49030,49032,49034,49037],{"class":55,"line":2990},[53,49027,48895],{"class":89},[53,49029,5859],{"class":82},[53,49031,46916],{"class":89},[53,49033,1067],{"class":82},[53,49035,49036],{"class":5805},"new-current.png",[53,49038,1079],{"class":82},[53,49040,49041],{"class":55,"line":10443},[53,49042,282],{"class":82},[53,49044,49045,49047,49049,49051,49053,49055],{"class":55,"line":26443},[53,49046,585],{"class":6841},[53,49048,48880],{"class":82},[53,49050,48883],{"class":59},[53,49052,390],{"class":389},[53,49054,48449],{"class":63},[53,49056,48890],{"class":82},[53,49058,49059,49061,49063,49065,49067,49070],{"class":55,"line":26448},[53,49060,48895],{"class":89},[53,49062,5859],{"class":82},[53,49064,46916],{"class":89},[53,49066,1067],{"class":82},[53,49068,49069],{"class":5805},"favorites.png",[53,49071,1079],{"class":82},[53,49073,49074,49076,49078,49081,49083],{"class":55,"line":26453},[53,49075,48702],{"class":89},[53,49077,5859],{"class":82},[53,49079,49080],{"class":89},"160",[53,49082,32859],{"class":389},[53,49084,1727],{"class":82},[53,49086,49087],{"class":55,"line":26458},[53,49088,282],{"class":82},[53,49090,49091,49093,49095,49097,49099,49101,49103,49105],{"class":55,"line":26463},[53,49092,585],{"class":6841},[53,49094,48880],{"class":82},[53,49096,48883],{"class":59},[53,49098,390],{"class":389},[53,49100,48449],{"class":63},[53,49102,7599],{"class":82},[53,49104,48939],{"class":59},[53,49106,5795],{"class":82},[53,49108,49109,49111,49113,49115,49117,49120],{"class":55,"line":26468},[53,49110,48895],{"class":89},[53,49112,5859],{"class":82},[53,49114,46916],{"class":89},[53,49116,1067],{"class":82},[53,49118,49119],{"class":5805},"favorites-current.png",[53,49121,1079],{"class":82},[53,49123,49124],{"class":55,"line":26473},[53,49125,282],{"class":82},[53,49127,49128,49130,49132,49134,49136,49138],{"class":55,"line":26478},[53,49129,585],{"class":6841},[53,49131,48880],{"class":82},[53,49133,48883],{"class":59},[53,49135,390],{"class":389},[53,49137,48477],{"class":63},[53,49139,48890],{"class":82},[53,49141,49142,49144,49146,49148,49150,49153],{"class":55,"line":27490},[53,49143,48895],{"class":89},[53,49145,5859],{"class":82},[53,49147,46916],{"class":89},[53,49149,1067],{"class":82},[53,49151,49152],{"class":5805},"more.png",[53,49154,1079],{"class":82},[53,49156,49157,49159,49161,49164,49166],{"class":55,"line":27496},[53,49158,48702],{"class":89},[53,49160,5859],{"class":82},[53,49162,49163],{"class":89},"240",[53,49165,32859],{"class":389},[53,49167,1727],{"class":82},[53,49169,49170],{"class":55,"line":27501},[53,49171,282],{"class":82},[53,49173,49174,49176,49178,49180,49182,49184],{"class":55,"line":27507},[53,49175,585],{"class":6841},[53,49177,48880],{"class":82},[53,49179,48883],{"class":59},[53,49181,390],{"class":389},[53,49183,48505],{"class":63},[53,49185,48890],{"class":82},[53,49187,49188,49190,49192,49194,49196,49199],{"class":55,"line":27513},[53,49189,48895],{"class":89},[53,49191,5859],{"class":82},[53,49193,46916],{"class":89},[53,49195,1067],{"class":82},[53,49197,49198],{"class":5805},"info.png",[53,49200,1079],{"class":82},[53,49202,49203,49205,49207,49209],{"class":55,"line":27519},[53,49204,48677],{"class":89},[53,49206,5859],{"class":82},[53,49208,48682],{"class":89},[53,49210,1727],{"class":82},[53,49212,49213,49215,49217,49219,49221],{"class":55,"line":27525},[53,49214,48715],{"class":89},[53,49216,5859],{"class":82},[53,49218,48826],{"class":89},[53,49220,32859],{"class":389},[53,49222,1727],{"class":82},[53,49224,49225,49227,49229,49231,49233],{"class":55,"line":27530},[53,49226,48821],{"class":89},[53,49228,5859],{"class":82},[53,49230,48826],{"class":89},[53,49232,32859],{"class":389},[53,49234,1727],{"class":82},[53,49236,49237,49240,49242,49244,49246],{"class":55,"line":27536},[53,49238,49239],{"class":89}," bottom",[53,49241,5859],{"class":82},[53,49243,23508],{"class":89},[53,49245,32859],{"class":389},[53,49247,1727],{"class":82},[53,49249,49250,49253,49255,49257,49259],{"class":55,"line":27542},[53,49251,49252],{"class":89}," right",[53,49254,5859],{"class":82},[53,49256,23508],{"class":89},[53,49258,32859],{"class":389},[53,49260,1727],{"class":82},[53,49262,49263],{"class":55,"line":27548},[53,49264,282],{"class":82},[53,49266,49267],{"class":55,"line":27554},[53,49268,49269],{"class":2530},"/* finally position the views themselves */\n",[53,49271,49272,49274,49277],{"class":55,"line":27559},[53,49273,6817],{"class":6841},[53,49275,49276],{"class":59},".view",[53,49278,5795],{"class":82},[53,49280,49281,49283,49285,49287],{"class":55,"line":27565},[53,49282,48677],{"class":89},[53,49284,5859],{"class":82},[53,49286,48780],{"class":89},[53,49288,1727],{"class":82},[53,49290,49291,49293,49295,49297,49299],{"class":55,"line":27571},[53,49292,48689],{"class":89},[53,49294,5859],{"class":82},[53,49296,48826],{"class":89},[53,49298,32859],{"class":389},[53,49300,1727],{"class":82},[53,49302,49303,49305,49307,49309,49311],{"class":55,"line":27576},[53,49304,48702],{"class":89},[53,49306,5859],{"class":82},[53,49308,37264],{"class":89},[53,49310,32859],{"class":389},[53,49312,1727],{"class":82},[53,49314,49315,49317,49319,49321,49323],{"class":55,"line":27581},[53,49316,48715],{"class":89},[53,49318,5859],{"class":82},[53,49320,48720],{"class":89},[53,49322,32859],{"class":389},[53,49324,1727],{"class":82},[53,49326,49327,49329,49331,49334,49336],{"class":55,"line":27586},[53,49328,48821],{"class":89},[53,49330,5859],{"class":82},[53,49332,49333],{"class":89},"396",[53,49335,32859],{"class":389},[53,49337,1727],{"class":82},[53,49339,49340],{"class":55,"line":27591},[53,49341,282],{"class":82},[18,49343,49344],{},"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:",[43,49346,49348],{"className":6989,"code":49347,"language":6991,"meta":48,"style":48},"$(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",[50,49349,49350,49366,49385,49399,49413,49449,49454,49459,49476,49499,49503,49518,49541],{"__ignoreMap":48},[53,49351,49352,49354,49357,49360,49362,49364],{"class":55,"line":56},[53,49353,34393],{"class":59},[53,49355,49356],{"class":82},"(document).",[53,49358,49359],{"class":59},"ready",[53,49361,1067],{"class":82},[53,49363,5789],{"class":389},[53,49365,13502],{"class":82},[53,49367,49368,49370,49373,49375,49378,49380,49383],{"class":55,"line":86},[53,49369,13507],{"class":389},[53,49371,49372],{"class":82}," navitems ",[53,49374,390],{"class":389},[53,49376,49377],{"class":59}," $",[53,49379,1067],{"class":82},[53,49381,49382],{"class":63},"\"#navigation li a\"",[53,49384,1079],{"class":82},[53,49386,49387,49390,49393,49395,49397],{"class":55,"line":126},[53,49388,49389],{"class":82}," navitems.",[53,49391,49392],{"class":59},"click",[53,49394,1067],{"class":82},[53,49396,5789],{"class":389},[53,49398,13502],{"class":82},[53,49400,49401,49404,49407,49409,49411],{"class":55,"line":163},[53,49402,49403],{"class":82}," navitems.",[53,49405,49406],{"class":59},"removeClass",[53,49408,1067],{"class":82},[53,49410,48385],{"class":63},[53,49412,1079],{"class":82},[53,49414,49415,49417,49420,49422,49424,49426,49428,49430,49433,49435,49437,49439,49442,49444,49447],{"class":55,"line":186},[53,49416,24522],{"class":389},[53,49418,49419],{"class":82}," ref ",[53,49421,390],{"class":389},[53,49423,49377],{"class":59},[53,49425,1067],{"class":82},[53,49427,3609],{"class":89},[53,49429,4562],{"class":82},[53,49431,49432],{"class":59},"addClass",[53,49434,1067],{"class":82},[53,49436,48385],{"class":63},[53,49438,4562],{"class":82},[53,49440,49441],{"class":59},"attr",[53,49443,1067],{"class":82},[53,49445,49446],{"class":63},"\"href\"",[53,49448,1079],{"class":82},[53,49450,49451],{"class":55,"line":221},[53,49452,49453],{"class":2530}," /* hide the other views, show the one navigated to\n",[53,49455,49456],{"class":55,"line":242},[53,49457,49458],{"class":2530}," and trigger a custom event */\n",[53,49460,49461,49464,49466,49469,49471,49474],{"class":55,"line":273},[53,49462,49463],{"class":59}," $",[53,49465,1067],{"class":82},[53,49467,49468],{"class":63},"\"div.view\"",[53,49470,4562],{"class":82},[53,49472,49473],{"class":59},"hide",[53,49475,7061],{"class":82},[53,49477,49478,49480,49483,49486,49489,49492,49494,49497],{"class":55,"line":279},[53,49479,49463],{"class":59},[53,49481,49482],{"class":82},"(ref).",[53,49484,49485],{"class":59},"show",[53,49487,49488],{"class":82},"().",[53,49490,49491],{"class":59},"trigger",[53,49493,1067],{"class":82},[53,49495,49496],{"class":63},"\"becameActive\"",[53,49498,1079],{"class":82},[53,49500,49501],{"class":55,"line":496},[53,49502,13545],{"class":82},[53,49504,49505,49508,49510,49512,49514,49516],{"class":55,"line":503},[53,49506,49507],{"class":59}," $",[53,49509,1067],{"class":82},[53,49511,49468],{"class":63},[53,49513,4562],{"class":82},[53,49515,49473],{"class":59},[53,49517,7061],{"class":82},[53,49519,49520,49522,49524,49527,49529,49531,49533,49535,49537,49539],{"class":55,"line":509},[53,49521,49507],{"class":59},[53,49523,1067],{"class":82},[53,49525,49526],{"class":63},"\"div.view.current\"",[53,49528,4562],{"class":82},[53,49530,49485],{"class":59},[53,49532,49488],{"class":82},[53,49534,49491],{"class":59},[53,49536,1067],{"class":82},[53,49538,49496],{"class":63},[53,49540,1079],{"class":82},[53,49542,49543],{"class":55,"line":515},[53,49544,7148],{"class":82},[18,49546,49547,49548,49551,49552,49555],{},"With custom events like ",[50,49549,49550],{},"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 ",[50,49553,49554],{},"#new"," view, you could simply\nwrite:",[43,49557,49559],{"className":6989,"code":49558,"language":6991,"meta":48,"style":48},"$(\"#new\").bind(\"becameActive\", function (event) {\n $(event.target).doSomething();\n});\n",[50,49560,49561,49587,49599],{"__ignoreMap":48},[53,49562,49563,49565,49567,49569,49571,49573,49575,49577,49579,49581,49583,49585],{"class":55,"line":56},[53,49564,34393],{"class":59},[53,49566,1067],{"class":82},[53,49568,48421],{"class":63},[53,49570,4562],{"class":82},[53,49572,24281],{"class":59},[53,49574,1067],{"class":82},[53,49576,49496],{"class":63},[53,49578,99],{"class":82},[53,49580,5789],{"class":389},[53,49582,7040],{"class":82},[53,49584,7043],{"class":5805},[53,49586,13628],{"class":82},[53,49588,49589,49591,49594,49597],{"class":55,"line":86},[53,49590,49507],{"class":59},[53,49592,49593],{"class":82},"(event.target).",[53,49595,49596],{"class":59},"doSomething",[53,49598,7061],{"class":82},[53,49600,49601],{"class":55,"line":126},[53,49602,7148],{"class":82},[18,49604,49605],{},"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:",[43,49607,49609],{"className":6989,"code":49608,"language":6991,"meta":48,"style":48},"// 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",[50,49610,49611,49616,49621,49655,49673,49683,49690,49697,49704,49709,49713,49718,49734,49760,49765,49769],{"__ignoreMap":48},[53,49612,49613],{"class":55,"line":56},[53,49614,49615],{"class":2530},"// open a database, providing it's name, version,\n",[53,49617,49618],{"class":55,"line":86},[53,49619,49620],{"class":2530},"// maximum size and display name\n",[53,49622,49623,49625,49628,49630,49633,49635,49638,49640,49643,49645,49648,49650,49653],{"class":55,"line":126},[53,49624,24813],{"class":389},[53,49626,49627],{"class":82}," database ",[53,49629,390],{"class":389},[53,49631,49632],{"class":59}," openDatabase",[53,49634,1067],{"class":82},[53,49636,49637],{"class":63},"\"Example\"",[53,49639,99],{"class":82},[53,49641,49642],{"class":63},"\"1.0\"",[53,49644,99],{"class":82},[53,49646,49647],{"class":89},"1048576",[53,49649,99],{"class":82},[53,49651,49652],{"class":63},"\"Example Database\"",[53,49654,1079],{"class":82},[53,49656,49657,49660,49663,49665,49667,49669,49671],{"class":55,"line":163},[53,49658,49659],{"class":82},"database.",[53,49661,49662],{"class":59},"transaction",[53,49664,1067],{"class":82},[53,49666,5789],{"class":389},[53,49668,7040],{"class":82},[53,49670,49662],{"class":5805},[53,49672,13628],{"class":82},[53,49674,49675,49678,49681],{"class":55,"line":186},[53,49676,49677],{"class":82}," transaction.",[53,49679,49680],{"class":59},"executeSQL",[53,49682,1139],{"class":82},[53,49684,49685,49688],{"class":55,"line":221},[53,49686,49687],{"class":63}," \"CREATE TABLE IF NOT EXISTS data\"",[53,49689,33473],{"class":389},[53,49691,49692,49695],{"class":55,"line":242},[53,49693,49694],{"class":63}," \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"",[53,49696,33473],{"class":389},[53,49698,49699,49702],{"class":55,"line":273},[53,49700,49701],{"class":63}," \"... more declarations ...);\"",[53,49703,7143],{"class":82},[53,49705,49706],{"class":55,"line":279},[53,49707,49708],{"class":82}," );\n",[53,49710,49711],{"class":55,"line":496},[53,49712,7148],{"class":82},[53,49714,49715],{"class":55,"line":503},[53,49716,49717],{"class":2530},"// other statements are issued the same way\n",[53,49719,49720,49722,49724,49726,49728,49730,49732],{"class":55,"line":509},[53,49721,49659],{"class":82},[53,49723,49662],{"class":59},[53,49725,1067],{"class":82},[53,49727,5789],{"class":389},[53,49729,7040],{"class":82},[53,49731,49662],{"class":5805},[53,49733,13628],{"class":82},[53,49735,49736,49738,49740,49742,49745,49747,49749,49751,49753,49755,49758],{"class":55,"line":515},[53,49737,49677],{"class":82},[53,49739,49680],{"class":59},[53,49741,1067],{"class":82},[53,49743,49744],{"class":63},"\"SELECT * FROM data;\"",[53,49746,99],{"class":82},[53,49748,5789],{"class":389},[53,49750,7040],{"class":82},[53,49752,49662],{"class":5805},[53,49754,99],{"class":82},[53,49756,49757],{"class":5805},"result",[53,49759,13628],{"class":82},[53,49761,49762],{"class":55,"line":521},[53,49763,49764],{"class":2530}," // do something with 'result'\n",[53,49766,49767],{"class":55,"line":527},[53,49768,13545],{"class":82},[53,49770,49771],{"class":55,"line":533},[53,49772,7148],{"class":82},[2352,49774,10515],{"id":10514},[18,49776,49777,49778,3566,49781],{},"A question remains: ",[573,49779,49780],{},"when this is the basic skeleton of an application, how do i put this on an actual\ndevice?",[1773,49782],{"alt":48,"src":49783},"https://media.synyx.de/uploads//2010/08/jquery-html-css-example-e1281692547570.png",[18,49785,49786],{},"Example in Simulator",[18,49788,49789,49790,49793],{},"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 ",[585,49791,47976],{"href":47974,"rel":49792},[589],". 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.",[2352,49795,49797],{"id":49796},"whats-next","What’s next?",[18,49799,49800],{},"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:",[577,49802,49803,49806,49809,49812],{},[580,49804,49805],{},"What problems might arise with JavaScript as a development language? Is there enough tool support for ease of\ndevelopment?",[580,49807,49808],{},"What do i have to program from scratch and which frameworks are available, that erase my need of boilerplate code?",[580,49810,49811],{},"What about the performance, for example when sophisticated animations are desired?",[580,49813,49814],{},"Is my application really cross-platform, when using these technologies, and does it behave exactly the same on every\ndevice?",[18,49816,49817],{},"Stay tuned for more.",[607,49819,49820],{},"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":48,"searchDepth":86,"depth":86,"links":49822},[49823,49824,49825,49826],{"id":48331,"depth":86,"text":48332},{"id":48338,"depth":86,"text":48339},{"id":10514,"depth":86,"text":10515},{"id":49796,"depth":86,"text":49797},[4516,997],"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":48315,"description":49834},"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",[4526,10147,6831,46406,6991],"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…","yJGRhWop8a6UwlupSTSgkogMBabnFF7yVveZDcNWM8Y",{"id":49840,"title":49841,"author":49842,"body":49843,"category":50153,"date":50154,"description":50155,"extension":617,"link":50156,"meta":50157,"navigation":499,"path":50158,"seo":50159,"slug":49847,"stem":50161,"tags":50162,"teaser":50163,"__hash__":50164},"blog/blog/sending-apple-push-notifications-with-notnoops-java-apns-library.md","Sending Apple Push Notifications with notnoop's java-apns library",[12861],{"type":11,"value":49844,"toc":50151},[49845,49848,49863,49871,49874,49881,49884,50146,50149],[14,49846,49841],{"id":49847},"sending-apple-push-notifications-with-notnoops-java-apns-library",[18,49849,49850,49851,49856,49857,49862],{},"If you need to send apple push notifications to your users, like we do in\na ",[585,49852,49855],{"href":49853,"rel":49854},"http://mobile.synyx.de/2010/07/i-think-i-spider/",[589],"secret project"," mentioned earlier this\nweek, ",[585,49858,49861],{"href":49859,"rel":49860},"http://github.com/notnoop/java-apns",[589],"notnoop’s java-apns library"," is a good choice, because its really simple to\nuse and saves you a lot of work.",[18,49864,49865,49866,8526],{},"(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: ",[585,49867,49870],{"href":49868,"rel":49869},"http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/ApplePushService/ApplePushService.html",[589],"Apple Push Service",[18,49872,49873],{},"First you have to download the library (or add it in your maven dependencies):",[18,49875,49876],{},[585,49877,49880],{"href":49878,"rel":49879},"http://github.com/notnoop/java-apns/tree/",[589],"java-apns",[18,49882,49883],{},"The programming part isn’t that much so here’s how you do it:",[43,49885,49887],{"className":288,"code":49886,"language":290,"meta":48,"style":48},"\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",[50,49888,49889,49893,49898,49903,49907,49912,49917,49922,49927,49932,49937,49942,49947,49952,49957,49962,49967,49972,49977,49982,49987,49992,49997,50002,50007,50012,50017,50022,50027,50032,50037,50042,50047,50052,50057,50062,50066,50071,50076,50081,50086,50091,50096,50100,50104,50108,50113,50118,50123,50128,50133,50138,50142],{"__ignoreMap":48},[53,49890,49891],{"class":55,"line":56},[53,49892,500],{"emptyLinePlaceholder":499},[53,49894,49895],{"class":55,"line":86},[53,49896,49897],{},"public void pushMessage() {\n",[53,49899,49900],{"class":55,"line":126},[53,49901,49902],{}," ApnsService service = null;\n",[53,49904,49905],{"class":55,"line":163},[53,49906,36470],{},[53,49908,49909],{"class":55,"line":186},[53,49910,49911],{}," // get the certificate\n",[53,49913,49914],{"class":55,"line":221},[53,49915,49916],{}," InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(\"your_certificate.p12\");\n",[53,49918,49919],{"class":55,"line":242},[53,49920,49921],{}," service = APNS.newService().withCert(certStream, \"your_cert_password\").withSandboxDestination().build();\n",[53,49923,49924],{"class":55,"line":273},[53,49925,49926],{}," // or\n",[53,49928,49929],{"class":55,"line":279},[53,49930,49931],{}," // service = APNS.newService().withCert(certStream,\n",[53,49933,49934],{"class":55,"line":496},[53,49935,49936],{}," // \"your_cert_password\").withProductionDestination().build();\n",[53,49938,49939],{"class":55,"line":503},[53,49940,49941],{}," service.start();\n",[53,49943,49944],{"class":55,"line":509},[53,49945,49946],{}," // You have to delete the devices from you list that no longer\n",[53,49948,49949],{"class":55,"line":515},[53,49950,49951],{}," //have the app installed, see method below\n",[53,49953,49954],{"class":55,"line":521},[53,49955,49956],{}," deleteInactiveDevices(service);\n",[53,49958,49959],{"class":55,"line":527},[53,49960,49961],{}," // read your user list\n",[53,49963,49964],{"class":55,"line":533},[53,49965,49966],{}," List\u003CUser> userList = userDao.readUsers();\n",[53,49968,49969],{"class":55,"line":539},[53,49970,49971],{}," for (User user : userList) {\n",[53,49973,49974],{"class":55,"line":545},[53,49975,49976],{}," try {\n",[53,49978,49979],{"class":55,"line":2070},[53,49980,49981],{}," // we had a daily update here, so we need to know how many\n",[53,49983,49984],{"class":55,"line":2075},[53,49985,49986],{}," //days the user hasn't started the app\n",[53,49988,49989],{"class":55,"line":2081},[53,49990,49991],{}," // so that we get the number of updates to display it as the badge.\n",[53,49993,49994],{"class":55,"line":2087},[53,49995,49996],{}," int days = (int) ((System.currentTimeMillis() - user.getLastUpdate()) / 1000 / 60 / 60 / 24);\n",[53,49998,49999],{"class":55,"line":2092},[53,50000,50001],{}," PayloadBuilder payloadBuilder = APNS.newPayload();\n",[53,50003,50004],{"class":55,"line":2097},[53,50005,50006],{}," payloadBuilder = payloadBuilder.badge(days).alertBody(\"some message you want to send here\");\n",[53,50008,50009],{"class":55,"line":2103},[53,50010,50011],{}," // check if the message is too long (it won't be sent if it is)\n",[53,50013,50014],{"class":55,"line":2109},[53,50015,50016],{}," //and trim it if it is.\n",[53,50018,50019],{"class":55,"line":2115},[53,50020,50021],{}," if (payloadBuilder.isTooLong()) {\n",[53,50023,50024],{"class":55,"line":2120},[53,50025,50026],{}," payloadBuilder = payloadBuilder.shrinkBody();\n",[53,50028,50029],{"class":55,"line":2946},[53,50030,50031],{}," }\n",[53,50033,50034],{"class":55,"line":2952},[53,50035,50036],{}," String payload = payloadBuilder.build();\n",[53,50038,50039],{"class":55,"line":2964},[53,50040,50041],{}," String token = user.getToken();\n",[53,50043,50044],{"class":55,"line":2973},[53,50045,50046],{}," service.push(token, payload);\n",[53,50048,50049],{"class":55,"line":2979},[53,50050,50051],{}," } catch (Exception ex) {\n",[53,50053,50054],{"class":55,"line":2990},[53,50055,50056],{}," // some logging stuff\n",[53,50058,50059],{"class":55,"line":10443},[53,50060,50061],{}," }\n",[53,50063,50064],{"class":55,"line":26443},[53,50065,21600],{},[53,50067,50068],{"class":55,"line":26448},[53,50069,50070],{}," } catch (Exception ex) {\n",[53,50072,50073],{"class":55,"line":26453},[53,50074,50075],{}," // more logging\n",[53,50077,50078],{"class":55,"line":26458},[53,50079,50080],{}," } finally {\n",[53,50082,50083],{"class":55,"line":26463},[53,50084,50085],{}," // check if the service was successfull initialized and stop it here, if it was\n",[53,50087,50088],{"class":55,"line":26468},[53,50089,50090],{}," if (service != null) {\n",[53,50092,50093],{"class":55,"line":26473},[53,50094,50095],{}," service.stop();\n",[53,50097,50098],{"class":55,"line":26478},[53,50099,21600],{},[53,50101,50102],{"class":55,"line":27490},[53,50103,12712],{},[53,50105,50106],{"class":55,"line":27496},[53,50107,860],{},[53,50109,50110],{"class":55,"line":27501},[53,50111,50112],{}," private void deleteInactiveDevices(ApnsService service) {\n",[53,50114,50115],{"class":55,"line":27507},[53,50116,50117],{}," // get the list of the devices that no longer have your app installed from apple\n",[53,50119,50120],{"class":55,"line":27513},[53,50121,50122],{}," //ignore the =\"\" after Date here, it's a bug...\n",[53,50124,50125],{"class":55,"line":27519},[53,50126,50127],{}," Map\u003CString, Date> inactiveDevices = service.getInactiveDevices();\n",[53,50129,50130],{"class":55,"line":27525},[53,50131,50132],{}," for (String deviceToken : inactiveDevices.keySet()) {\n",[53,50134,50135],{"class":55,"line":27530},[53,50136,50137],{}," userDao.deleteByDeviceId(deviceToken);\n",[53,50139,50140],{"class":55,"line":27536},[53,50141,12712],{},[53,50143,50144],{"class":55,"line":27542},[53,50145,860],{},[18,50147,50148],{},"Now wasn’t that an easy one this time?",[607,50150,989],{},{"title":48,"searchDepth":86,"depth":86,"links":50152},[],[4516],"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":49841,"description":50160},"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",[47850,37119,46406,36576],"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":50166,"title":50167,"author":50168,"body":50169,"category":50373,"date":50374,"description":50375,"extension":617,"link":50376,"meta":50377,"navigation":499,"path":50378,"seo":50379,"slug":50173,"stem":50381,"tags":50382,"teaser":50386,"__hash__":50387},"blog/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app.md","Dependency Hell or Including JSR303 into a hibernated JavaEE App",[26052],{"type":11,"value":50170,"toc":50371},[50171,50174,50181,50188,50191,50334,50341,50344,50355,50366,50369],[14,50172,50167],{"id":50173},"dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[18,50175,50176,50177,8526],{},"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. (",[585,50178,50179],{"href":50179,"rel":50180},"http://redmine.synyx.org/projects/show/hades",[589],[18,50182,50183,50184,8526],{},"First I just followed the Spring tutorial which is quite simple and straight\nforward. (",[585,50185,50186],{"href":50186,"rel":50187},"http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#validation-beanvalidation-spring",[589],[18,50189,50190],{},"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.",[43,50192,50194],{"className":13667,"code":50193,"language":13669,"meta":48,"style":48},"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",[50,50195,50196,50201,50206,50211,50216,50221,50226,50231,50236,50241,50246,50251,50256,50261,50266,50271,50276,50281,50286,50291,50296,50301,50306,50311,50316,50320,50324,50329],{"__ignoreMap":48},[53,50197,50198],{"class":55,"line":56},[53,50199,50200],{},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\n",[53,50202,50203],{"class":55,"line":86},[53,50204,50205],{},"java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\n",[53,50207,50208],{"class":55,"line":126},[53,50209,50210],{},"at org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\n",[53,50212,50213],{"class":55,"line":163},[53,50214,50215],{},"at org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\n",[53,50217,50218],{"class":55,"line":186},[53,50219,50220],{},"at org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\n",[53,50222,50223],{"class":55,"line":221},[53,50224,50225],{},"at org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\n",[53,50227,50228],{"class":55,"line":242},[53,50229,50230],{},"at org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\n",[53,50232,50233],{"class":55,"line":273},[53,50234,50235],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\n",[53,50237,50238],{"class":55,"line":279},[53,50239,50240],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\n",[53,50242,50243],{"class":55,"line":496},[53,50244,50245],{},"at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\n",[53,50247,50248],{"class":55,"line":503},[53,50249,50250],{},"at org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\n",[53,50252,50253],{"class":55,"line":509},[53,50254,50255],{},"at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n",[53,50257,50258],{"class":55,"line":515},[53,50259,50260],{},"at java.lang.reflect.Method.invoke(Method.java:597)\n",[53,50262,50263],{"class":55,"line":521},[53,50264,50265],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\n",[53,50267,50268],{"class":55,"line":527},[53,50269,50270],{},"at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\n",[53,50272,50273],{"class":55,"line":533},[53,50274,50275],{},"at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\n",[53,50277,50278],{"class":55,"line":539},[53,50279,50280],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\n",[53,50282,50283],{"class":55,"line":545},[53,50284,50285],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\n",[53,50287,50288],{"class":55,"line":2070},[53,50289,50290],{},"at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\n",[53,50292,50293],{"class":55,"line":2075},[53,50294,50295],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\n",[53,50297,50298],{"class":55,"line":2081},[53,50299,50300],{},"at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\n",[53,50302,50303],{"class":55,"line":2087},[53,50304,50305],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\n",[53,50307,50308],{"class":55,"line":2092},[53,50309,50310],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\n",[53,50312,50313],{"class":55,"line":2097},[53,50314,50315],{},"at org.apache.maven.surefire.Surefire.run(Surefire.java:177)\n",[53,50317,50318],{"class":55,"line":2103},[53,50319,50255],{},[53,50321,50322],{"class":55,"line":2109},[53,50323,50260],{},[53,50325,50326],{"class":55,"line":2115},[53,50327,50328],{},"at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\n",[53,50330,50331],{"class":55,"line":2120},[53,50332,50333],{},"at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n",[18,50335,50336,50337,50340],{},"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 ",[573,50338,50339],{},"getPersistenceUtil()"," is getting called, its obvious that this method is\nnot available due to the fact that it is just a simple name matching.",[18,50342,50343],{},"Now I had THREE questions instead of one 🙂:",[577,50345,50346,50349,50352],{},[580,50347,50348],{},"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” ????",[580,50350,50351],{},"Why the specification manifests that if a class called PersistenceUtil is in classpath, there must be also a JPA\nvalidation?",[580,50353,50354],{},"Why they do not additionally check against the needed method getPersistenceUtil() as well?",[18,50356,50357,50358,50361,50362,50365],{},"Anyway, as I needed a workaround, I checked the projects classpath… and I found my class ",[573,50359,50360],{},"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 ",[573,50363,50364],{},"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.",[18,50367,50368],{},"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 🙁",[607,50370,989],{},{"title":48,"searchDepth":86,"depth":86,"links":50372},[],[614],"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":50167,"description":50380},"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",[50383,50384,50385,290,10977,5743],"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…","jaMqXSr9rHWkAW8ml4UhHnhi_9ybaohJ2bmPFJ0BOnA",{"id":50389,"title":50390,"author":50391,"body":50392,"category":50476,"date":50477,"description":50478,"extension":617,"link":50479,"meta":50480,"navigation":499,"path":50481,"seo":50482,"slug":50396,"stem":50484,"tags":50485,"teaser":50486,"__hash__":50487},"blog/blog/split-nsstring-by-characters.md","Split NSString by characters",[46383],{"type":11,"value":50393,"toc":50474},[50394,50397,50419,50428,50469,50472],[14,50395,50390],{"id":50396},"split-nsstring-by-characters",[18,50398,50399,50400,50407,50408,50410,50411,50418],{},"Have you ever pondered on the ",[585,50401,50404],{"href":50402,"rel":50403},"http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html",[589],[573,50405,50406],{},"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 ",[573,50409,50406],{}," by characters and create a ",[585,50412,50415],{"href":50413,"rel":50414},"http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html",[589],[573,50416,50417],{},"NSArray","\nof those characters.",[18,50420,50421,50422,50427],{},"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 ",[585,50423,50426],{"href":50424,"rel":50425},"http://www.idev101.com/code/Objective-C/Strings/split.html",[589],"this site",", which shows you how to do it:",[43,50429,50433],{"className":50430,"code":50431,"language":50432,"meta":48,"style":48},"language-objc shiki shiki-themes github-light github-dark","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","objc",[50,50434,50435,50440,50445,50450,50455,50460,50465],{"__ignoreMap":48},[53,50436,50437],{"class":55,"line":56},[53,50438,50439],{},"NSMutableArray *characters =\n",[53,50441,50442],{"class":55,"line":86},[53,50443,50444],{},"[[NSMutableArray alloc] initWithCapacity:[myString length]];\n",[53,50446,50447],{"class":55,"line":126},[53,50448,50449],{},"for (int i=0; i \u003C [myString length]; i++) {\n",[53,50451,50452],{"class":55,"line":163},[53,50453,50454],{}," NSString *ichar =\n",[53,50456,50457],{"class":55,"line":186},[53,50458,50459],{},"[NSString stringWithFormat:@\"%c\", [myString characterAtIndex:i]];\n",[53,50461,50462],{"class":55,"line":221},[53,50463,50464],{}," [characters addObject:ichar];\n",[53,50466,50467],{"class":55,"line":242},[53,50468,282],{},[18,50470,50471],{},"Although Apple’s documentation is extensive, you sometimes need a little hint or guidance to achieve your goal.",[607,50473,989],{},{"title":48,"searchDepth":86,"depth":86,"links":50475},[],[4516,997],"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":50390,"description":50483},"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",[37123],"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":50489,"title":50490,"author":50491,"body":50492,"category":50576,"date":50577,"description":50578,"extension":617,"link":50579,"meta":50580,"navigation":499,"path":50581,"seo":50582,"slug":50496,"stem":50584,"tags":50585,"teaser":50587,"__hash__":50588},"blog/blog/creating-a-mysql-dump-for-jdbc.md","Creating a MySQL dump for JDBC",[41317],{"type":11,"value":50493,"toc":50574},[50494,50497,50506,50515,50518,50527,50555,50559,50562,50571],[14,50495,50490],{"id":50496},"creating-a-mysql-dump-for-jdbc",[18,50498,50499,50500,50505],{},"I have just been fighting with\nthe ",[585,50501,50504],{"href":50502,"rel":50503},"https://web.archive.org/web/20150526005826/http://mojo.codehaus.org:80/sql-maven-plugin/",[589],"sql-maven-plugin"," while\ntrying to import an OpenCms MySQL dump automatically. The module wasn’t really verbose with an error message:",[18,50507,50508,50511,50512],{},[50,50509,50510],{},"[ERROR] Failed to execute: INSERT INTO ","CMS_CONTENTS",[50,50513,50514],{}," VALUES",[18,50516,50517],{},"followed by printing the rest of the dump to the standard output.",[18,50519,50520,50521,50526],{},"Fortunately my colleague ",[585,50522,50525],{"href":50523,"rel":50524},"http://blog.synyx.de/autoren/?uid=16",[589],"Marc"," 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:",[43,50528,50530],{"className":29913,"code":50529,"language":29915,"meta":48,"style":48},"mysqldump --hex-blob -u root -p opencms752 > install/opencms752.sql\n\n",[50,50531,50532],{"__ignoreMap":48},[53,50533,50534,50536,50539,50542,50544,50547,50550,50552],{"class":55,"line":56},[53,50535,29895],{"class":59},[53,50537,50538],{"class":89}," --hex-blob",[53,50540,50541],{"class":89}," -u",[53,50543,13779],{"class":63},[53,50545,50546],{"class":89}," -p",[53,50548,50549],{"class":63}," opencms752",[53,50551,31572],{"class":389},[53,50553,50554],{"class":63}," install/opencms752.sql\n",[18,50556,50557],{},[27,50558,37447],{},[18,50560,50561],{},"To prevent that some characters are encoded incorrectly, add the following to the configuration options:",[43,50563,50565],{"className":1980,"code":50564,"language":1982,"meta":48,"style":48},"\u003CescapeProcessing>false\u003C/escapeProcessing>\n",[50,50566,50567],{"__ignoreMap":48},[53,50568,50569],{"class":55,"line":56},[53,50570,50564],{},[607,50572,50573],{},"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":48,"searchDepth":86,"depth":86,"links":50575},[],[613],"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":50490,"description":50583},"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",[41780,10977,50586,37693],"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":50590,"title":50591,"author":50592,"body":50593,"category":50680,"date":50681,"description":50682,"extension":617,"link":50683,"meta":50684,"navigation":499,"path":50685,"seo":50686,"slug":50597,"stem":50688,"tags":50689,"teaser":50690,"__hash__":50691},"blog/blog/servlet-container-options-for-maven.md","Servlet container options for Maven",[41317],{"type":11,"value":50594,"toc":50678},[50595,50598,50611,50618,50626,50633,50640,50643,50670,50673,50676],[14,50596,50591],{"id":50597},"servlet-container-options-for-maven",[18,50599,50600,50601,50604,50605,50610],{},"When developing web apps with ",[585,50602,41071],{"href":25317,"rel":50603},[589]," the de facto standard for running the app is to use the\nexcellent ",[585,50606,50609],{"href":50607,"rel":50608},"https://web.archive.org/web/20150520205353/https://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin",[589],"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.",[18,50612,50613,50614,50617],{},"But if the production system does not run on Jetty but on ",[585,50615,29848],{"href":29846,"rel":50616},[589]," you might run into some\nproblems:",[577,50619,50620,50623],{},[580,50621,50622],{},"Some redirects from AJAX calls do work on Jetty but do not work on Tomcat",[580,50624,50625],{},"When submitting some forms on Jetty the parameters get lost",[18,50627,50628,50629,50632],{},"The latter can be noticed when running ",[585,50630,40251],{"href":46279,"rel":50631},[589]," on Jetty: Saving from the editor causes an error\nbecause the parameters are not available to OpenCms.",[18,50634,50635,50636,50639],{},"Fortunately there is a viable alternative:\nThe ",[585,50637,41340],{"href":41338,"rel":50638},[589],".\nBesides providing several options to deploy artifacts to an external server it can also be run in an embedded mode.",[18,50641,50642],{},"This is how the plugin is configured:",[43,50644,50646],{"className":1980,"code":50645,"language":1982,"meta":48,"style":48},"\n\u003Cplugin>\n \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n\u003C/plugin>\n\n",[50,50647,50648,50652,50656,50661,50666],{"__ignoreMap":48},[53,50649,50650],{"class":55,"line":56},[53,50651,500],{"emptyLinePlaceholder":499},[53,50653,50654],{"class":55,"line":86},[53,50655,34968],{},[53,50657,50658],{"class":55,"line":126},[53,50659,50660],{}," \u003CgroupId>org.codehaus.mojo\u003C/groupId>\n",[53,50662,50663],{"class":55,"line":163},[53,50664,50665],{}," \u003CartifactId>tomcat-maven-plugin\u003C/artifactId>\n",[53,50667,50668],{"class":55,"line":186},[53,50669,35103],{},[18,50671,50672],{},"It can be run using mvn tomcat:run and mvn tomcat:run-war for running in unpacked war mode.",[18,50674,50675],{},"Using this plugin you can be sure that the features your servlet container provides in production are the same during\ndevelopment.",[607,50677,989],{},{"title":48,"searchDepth":86,"depth":86,"links":50679},[],[613],"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":50591,"description":50687},"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",[7337,290,26510,10977,26512],"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":50693,"title":50694,"author":50695,"body":50696,"category":50936,"date":50937,"description":50938,"extension":617,"link":50939,"meta":50940,"navigation":499,"path":50941,"seo":50942,"slug":50700,"stem":50944,"tags":50945,"teaser":50947,"__hash__":50948},"blog/blog/ui-prototyping-iphone-apps.md","UI Prototyping iPhone Apps",[46383],{"type":11,"value":50697,"toc":50934},[50698,50701,50710,50713,50722,50733,50786,50797,50919,50922,50925,50932],[14,50699,50694],{"id":50700},"ui-prototyping-iphone-apps",[18,50702,50703,50704,50709],{},"Before ",[585,50705,50708],{"href":50706,"rel":50707},"http://mobile.synyx.de/2010/06/wwdc-2010/",[589],"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!",[18,50711,50712],{},"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.",[18,50714,50715,50716,50721],{},"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 ",[585,50717,50720],{"href":50718,"rel":50719},"https://web.archive.org/web/20130723100234/http://www.fruitstandsoftware.com:80/blog/2009/07/uiview-manipulation-made-easier-with-a-category/",[589],"Michael Fey’s blog",",\nwho was able to successfully reverse engineer the missing parts, which were not shown in the presentation.",[18,50723,50724,50725,50728,50729,50732],{},"Michael’s ",[573,50726,50727],{},"UIViewAdditions"," basically allow easy access to frame properties and give you a neat init method, which adds\nthe passed ",[573,50730,50731],{},"UIView"," as a parent:",[43,50734,50736],{"className":50430,"code":50735,"language":50432,"meta":48,"style":48},"- (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",[50,50737,50738,50743,50748,50753,50758,50763,50768,50772,50777,50782],{"__ignoreMap":48},[53,50739,50740],{"class":55,"line":56},[53,50741,50742],{},"- (id)initWithParent:(UIView *)parent {\n",[53,50744,50745],{"class":55,"line":86},[53,50746,50747],{}," self = [self initWithFrame:CGRectZero];\n",[53,50749,50750],{"class":55,"line":126},[53,50751,50752],{}," if (!self)\n",[53,50754,50755],{"class":55,"line":163},[53,50756,50757],{}," return nil;\n",[53,50759,50760],{"class":55,"line":186},[53,50761,50762],{}," [parent addSubview:self];\n",[53,50764,50765],{"class":55,"line":221},[53,50766,50767],{}," return self;\n",[53,50769,50770],{"class":55,"line":242},[53,50771,282],{},[53,50773,50774],{"class":55,"line":273},[53,50775,50776],{},"+ (id) viewWithParent:(UIView *)parent {\n",[53,50778,50779],{"class":55,"line":279},[53,50780,50781],{}," return [[[self alloc] initWithParent:parent] autorelease];\n",[53,50783,50784],{"class":55,"line":496},[53,50785,282],{},[18,50787,50788,50789,50792,50793,50796],{},"There wasn’t much left to do for me. I only coded the class ",[573,50790,50791],{},"Root",", which is the parent of all ",[573,50794,50795],{},"UIImageView"," instances\nused in the prototype. It provides a couple of methods to slide images back and forth:",[43,50798,50800],{"className":50430,"code":50799,"language":50432,"meta":48,"style":48},"@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",[50,50801,50802,50807,50812,50817,50822,50826,50830,50835,50840,50845,50850,50854,50858,50863,50868,50873,50877,50882,50887,50892,50897,50901,50905,50910,50915],{"__ignoreMap":48},[53,50803,50804],{"class":55,"line":56},[53,50805,50806],{},"@synthesize pageIndex = _pageIndex;\n",[53,50808,50809],{"class":55,"line":86},[53,50810,50811],{},"- (id) initWithParent:(UIView *)parent {\n",[53,50813,50814],{"class":55,"line":126},[53,50815,50816],{}," self = [super initWithParent:parent];\n",[53,50818,50819],{"class":55,"line":163},[53,50820,50821],{}," if (self == nil) {\n",[53,50823,50824],{"class":55,"line":186},[53,50825,50757],{},[53,50827,50828],{"class":55,"line":221},[53,50829,7109],{},[53,50831,50832],{"class":55,"line":242},[53,50833,50834],{}," self.userInteractionEnabled = YES;\n",[53,50836,50837],{"class":55,"line":273},[53,50838,50839],{}," self.size = self.window.size;\n",[53,50841,50842],{"class":55,"line":279},[53,50843,50844],{}," [[UIImageView viewWithParent:self] setImageWithName:@\"dailies\"];\n",[53,50846,50847],{"class":55,"line":496},[53,50848,50849],{}," self.pageIndex = 0;\n",[53,50851,50852],{"class":55,"line":503},[53,50853,50767],{},[53,50855,50856],{"class":55,"line":509},[53,50857,282],{},[53,50859,50860],{"class":55,"line":515},[53,50861,50862],{},"- (void)setPageIndex:(int)index {\n",[53,50864,50865],{"class":55,"line":521},[53,50866,50867],{}," if (index \u003C 0 || index >= [self.subviews count]) {\n",[53,50869,50870],{"class":55,"line":527},[53,50871,50872],{}," return;\n",[53,50874,50875],{"class":55,"line":533},[53,50876,7109],{},[53,50878,50879],{"class":55,"line":539},[53,50880,50881],{}," _pageIndex = index;\n",[53,50883,50884],{"class":55,"line":545},[53,50885,50886],{}," for (int i = 0; i \u003C [self.subviews count]; i++) {\n",[53,50888,50889],{"class":55,"line":2070},[53,50890,50891],{}," UIImageView *page = [self.subviews objectAtIndex:i];\n",[53,50893,50894],{"class":55,"line":2075},[53,50895,50896],{}," page.x = (i \u003C _pageIndex) ? -self.width : (i > _pageIndex) ? self.width : 0;\n",[53,50898,50899],{"class":55,"line":2081},[53,50900,7109],{},[53,50902,50903],{"class":55,"line":2087},[53,50904,282],{},[53,50906,50907],{"class":55,"line":2092},[53,50908,50909],{},"- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {\n",[53,50911,50912],{"class":55,"line":2097},[53,50913,50914],{}," self.pageIndex++;\n",[53,50916,50917],{"class":55,"line":2103},[53,50918,282],{},[18,50920,50921],{},"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:",[18,50923,50924],{},"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.",[18,50926,50927,50928,986],{},"You can download the source code for the two classes, along with a sample project\nfrom ",[585,50929,10976],{"href":50930,"rel":50931},"http://github.com/dlinsin/district9/tree/master/UIPrototyping/",[589],[607,50933,989],{},{"title":48,"searchDepth":86,"depth":86,"links":50935},[],[4516,997],"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":50694,"description":50943},"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",[37119,50946,46406,37123,9686],"design","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":50950,"title":50951,"author":50952,"body":50953,"category":50978,"date":50979,"description":50960,"extension":617,"link":50980,"meta":50981,"navigation":499,"path":50982,"seo":50983,"slug":50957,"stem":50984,"tags":50985,"teaser":50986,"__hash__":50987},"blog/blog/jetzt-ist-es-nicht-mehr-weit.md","Jetzt ist es nicht mehr weit",[33954],{"type":11,"value":50954,"toc":50976},[50955,50958,50961,50967,50970,50973],[14,50956,50951],{"id":50957},"jetzt-ist-es-nicht-mehr-weit",[18,50959,50960],{},"… bis zum Ende meiner dreijährigen Ausbildung zum Fachinformatiker Systemintegration.",[18,50962,50963,50964],{},"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. ",[573,50965,50966],{},"freu",[18,50968,50969],{},"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.",[18,50971,50972],{},"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.",[18,50974,50975],{},"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":48,"searchDepth":86,"depth":86,"links":50977},[],[5568],"2010-06-24T17:59:37","https://synyx.de/blog/jetzt-ist-es-nicht-mehr-weit/",{},"/blog/jetzt-ist-es-nicht-mehr-weit",{"title":50951,"description":50960},"blog/jetzt-ist-es-nicht-mehr-weit",[5579,40229,5743],"… 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…","eqs59GmC_MqY6ddSa-ppoDdT4Ai0pnS5G8wvpTsg-cA",{"id":50989,"title":50990,"author":50991,"body":50992,"category":52083,"date":52084,"description":52085,"extension":617,"link":52086,"meta":52087,"navigation":499,"path":52088,"seo":52089,"slug":50996,"stem":52090,"tags":52091,"teaser":52097,"__hash__":52098},"blog/blog/android-and-self-signed-ssl-certificates.md","Android and self-signed ssl certificates",[12861],{"type":11,"value":50993,"toc":52081},[50994,50997,51000,51003,51006,51532,51535,51850,51859,51862,51891,52033,52076,52079],[14,50995,50990],{"id":50996},"android-and-self-signed-ssl-certificates",[18,50998,50999],{},"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.",[18,51001,51002],{},"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.",[18,51004,51005],{},"First you have to create the SSLFactory:",[43,51007,51009],{"className":288,"code":51008,"language":290,"meta":48,"style":48},"\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",[50,51010,51011,51015,51020,51025,51030,51035,51040,51045,51050,51055,51060,51065,51069,51074,51079,51084,51089,51094,51099,51103,51108,51113,51118,51123,51128,51133,51138,51143,51148,51153,51158,51163,51168,51172,51177,51181,51186,51191,51196,51200,51205,51210,51215,51219,51224,51229,51234,51239,51244,51248,51252,51257,51262,51267,51271,51276,51280,51284,51289,51294,51299,51304,51309,51314,51319,51324,51329,51334,51339,51344,51349,51353,51358,51363,51367,51372,51377,51382,51386,51390,51395,51399,51404,51409,51413,51417,51422,51426,51431,51435,51439,51443,51448,51453,51457,51462,51467,51472,51476,51481,51486,51491,51496,51500,51505,51510,51514,51519,51524,51528],{"__ignoreMap":48},[53,51012,51013],{"class":55,"line":56},[53,51014,500],{"emptyLinePlaceholder":499},[53,51016,51017],{"class":55,"line":86},[53,51018,51019],{},"/*\n",[53,51021,51022],{"class":55,"line":126},[53,51023,51024],{}," * Licensed to the Apache Software Foundation (ASF) under one\n",[53,51026,51027],{"class":55,"line":163},[53,51028,51029],{}," * or more contributor license agreements. See the NOTICE file\n",[53,51031,51032],{"class":55,"line":186},[53,51033,51034],{}," * distributed with this work for additional information\n",[53,51036,51037],{"class":55,"line":221},[53,51038,51039],{}," * regarding copyright ownership. The ASF licenses this file\n",[53,51041,51042],{"class":55,"line":242},[53,51043,51044],{}," * to you under the Apache License, Version 2.0 (the\n",[53,51046,51047],{"class":55,"line":273},[53,51048,51049],{}," * \"License\"); you may not use this file except in compliance\n",[53,51051,51052],{"class":55,"line":279},[53,51053,51054],{}," * with the License. You may obtain a copy of the License at\n",[53,51056,51057],{"class":55,"line":496},[53,51058,51059],{}," *\n",[53,51061,51062],{"class":55,"line":503},[53,51063,51064],{}," * http://www.apache.org/licenses/LICENSE-2.0\n",[53,51066,51067],{"class":55,"line":509},[53,51068,51059],{},[53,51070,51071],{"class":55,"line":515},[53,51072,51073],{}," * Unless required by applicable law or agreed to in writing,\n",[53,51075,51076],{"class":55,"line":521},[53,51077,51078],{}," * software distributed under the License is distributed on an\n",[53,51080,51081],{"class":55,"line":527},[53,51082,51083],{}," * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n",[53,51085,51086],{"class":55,"line":533},[53,51087,51088],{}," * KIND, either express or implied. See the License for the\n",[53,51090,51091],{"class":55,"line":539},[53,51092,51093],{}," * specific language governing permissions and limitations\n",[53,51095,51096],{"class":55,"line":545},[53,51097,51098],{}," * under the License.\n",[53,51100,51101],{"class":55,"line":2070},[53,51102,41470],{},[53,51104,51105],{"class":55,"line":2075},[53,51106,51107],{},"import java.io.IOException;\n",[53,51109,51110],{"class":55,"line":2081},[53,51111,51112],{},"import java.net.InetAddress;\n",[53,51114,51115],{"class":55,"line":2087},[53,51116,51117],{},"import java.net.InetSocketAddress;\n",[53,51119,51120],{"class":55,"line":2092},[53,51121,51122],{},"import java.net.Socket;\n",[53,51124,51125],{"class":55,"line":2097},[53,51126,51127],{},"import java.net.UnknownHostException;\n",[53,51129,51130],{"class":55,"line":2103},[53,51131,51132],{},"import javax.net.ssl.SSLContext;\n",[53,51134,51135],{"class":55,"line":2109},[53,51136,51137],{},"import javax.net.ssl.SSLSocket;\n",[53,51139,51140],{"class":55,"line":2115},[53,51141,51142],{},"import javax.net.ssl.TrustManager;\n",[53,51144,51145],{"class":55,"line":2120},[53,51146,51147],{},"import org.apache.http.conn.ConnectTimeoutException;\n",[53,51149,51150],{"class":55,"line":2946},[53,51151,51152],{},"import org.apache.http.conn.scheme.LayeredSocketFactory;\n",[53,51154,51155],{"class":55,"line":2952},[53,51156,51157],{},"import org.apache.http.conn.scheme.SocketFactory;\n",[53,51159,51160],{"class":55,"line":2964},[53,51161,51162],{},"import org.apache.http.params.HttpConnectionParams;\n",[53,51164,51165],{"class":55,"line":2973},[53,51166,51167],{},"import org.apache.http.params.HttpParams;\n",[53,51169,51170],{"class":55,"line":2979},[53,51171,41455],{},[53,51173,51174],{"class":55,"line":2990},[53,51175,51176],{}," * This socket factory will create ssl socket that accepts self signed certificate\n",[53,51178,51179],{"class":55,"line":10443},[53,51180,51059],{},[53,51182,51183],{"class":55,"line":26443},[53,51184,51185],{}," * @author olamy\n",[53,51187,51188],{"class":55,"line":26448},[53,51189,51190],{}," * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $\n",[53,51192,51193],{"class":55,"line":26453},[53,51194,51195],{}," * @since 1.2.3\n",[53,51197,51198],{"class":55,"line":26458},[53,51199,41470],{},[53,51201,51202],{"class":55,"line":26463},[53,51203,51204],{},"public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory {\n",[53,51206,51207],{"class":55,"line":26468},[53,51208,51209],{}," private SSLContext sslcontext = null;\n",[53,51211,51212],{"class":55,"line":26473},[53,51213,51214],{}," private static SSLContext createEasySSLContext() throws IOException {\n",[53,51216,51217],{"class":55,"line":26478},[53,51218,36470],{},[53,51220,51221],{"class":55,"line":27490},[53,51222,51223],{}," SSLContext context = SSLContext.getInstance(\"TLS\");\n",[53,51225,51226],{"class":55,"line":27496},[53,51227,51228],{}," context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);\n",[53,51230,51231],{"class":55,"line":27501},[53,51232,51233],{}," return context;\n",[53,51235,51236],{"class":55,"line":27507},[53,51237,51238],{}," } catch (Exception e) {\n",[53,51240,51241],{"class":55,"line":27513},[53,51242,51243],{}," throw new IOException(e.getMessage());\n",[53,51245,51246],{"class":55,"line":27519},[53,51247,12712],{},[53,51249,51250],{"class":55,"line":27525},[53,51251,860],{},[53,51253,51254],{"class":55,"line":27530},[53,51255,51256],{}," private SSLContext getSSLContext() throws IOException {\n",[53,51258,51259],{"class":55,"line":27536},[53,51260,51261],{}," if (this.sslcontext == null) {\n",[53,51263,51264],{"class":55,"line":27542},[53,51265,51266],{}," this.sslcontext = createEasySSLContext();\n",[53,51268,51269],{"class":55,"line":27548},[53,51270,12712],{},[53,51272,51273],{"class":55,"line":27554},[53,51274,51275],{}," return this.sslcontext;\n",[53,51277,51278],{"class":55,"line":27559},[53,51279,860],{},[53,51281,51282],{"class":55,"line":27565},[53,51283,15330],{},[53,51285,51286],{"class":55,"line":27571},[53,51287,51288],{}," * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,\n",[53,51290,51291],{"class":55,"line":27576},[53,51292,51293],{}," * java.net.InetAddress, int, org.apache.http.params.HttpParams)\n",[53,51295,51296],{"class":55,"line":27581},[53,51297,51298],{}," */\n",[53,51300,51301],{"class":55,"line":27586},[53,51302,51303],{}," public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort,\n",[53,51305,51306],{"class":55,"line":27591},[53,51307,51308],{}," HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {\n",[53,51310,51311],{"class":55,"line":27596},[53,51312,51313],{}," int connTimeout = HttpConnectionParams.getConnectionTimeout(params);\n",[53,51315,51316],{"class":55,"line":43099},[53,51317,51318],{}," int soTimeout = HttpConnectionParams.getSoTimeout(params);\n",[53,51320,51321],{"class":55,"line":43115},[53,51322,51323],{}," InetSocketAddress remoteAddress = new InetSocketAddress(host, port);\n",[53,51325,51326],{"class":55,"line":43129},[53,51327,51328],{}," SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());\n",[53,51330,51331],{"class":55,"line":43145},[53,51332,51333],{}," if ((localAddress != null) || (localPort > 0)) {\n",[53,51335,51336],{"class":55,"line":43161},[53,51337,51338],{}," // we need to bind explicitly\n",[53,51340,51341],{"class":55,"line":43175},[53,51342,51343],{}," if (localPort \u003C 0) {\n",[53,51345,51346],{"class":55,"line":43189},[53,51347,51348],{}," localPort = 0; // indicates \"any\"\n",[53,51350,51351],{"class":55,"line":43203},[53,51352,21600],{},[53,51354,51355],{"class":55,"line":43219},[53,51356,51357],{}," InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);\n",[53,51359,51360],{"class":55,"line":43233},[53,51361,51362],{}," sslsock.bind(isa);\n",[53,51364,51365],{"class":55,"line":43247},[53,51366,12712],{},[53,51368,51369],{"class":55,"line":43261},[53,51370,51371],{}," sslsock.connect(remoteAddress, connTimeout);\n",[53,51373,51374],{"class":55,"line":43275},[53,51375,51376],{}," sslsock.setSoTimeout(soTimeout);\n",[53,51378,51379],{"class":55,"line":43287},[53,51380,51381],{}," return sslsock;\n",[53,51383,51384],{"class":55,"line":43299},[53,51385,860],{},[53,51387,51388],{"class":55,"line":43311},[53,51389,15330],{},[53,51391,51392],{"class":55,"line":43323},[53,51393,51394],{}," * @see org.apache.http.conn.scheme.SocketFactory#createSocket()\n",[53,51396,51397],{"class":55,"line":43337},[53,51398,51298],{},[53,51400,51401],{"class":55,"line":43349},[53,51402,51403],{}," public Socket createSocket() throws IOException {\n",[53,51405,51406],{"class":55,"line":43363},[53,51407,51408],{}," return getSSLContext().getSocketFactory().createSocket();\n",[53,51410,51411],{"class":55,"line":43375},[53,51412,860],{},[53,51414,51415],{"class":55,"line":43387},[53,51416,15330],{},[53,51418,51419],{"class":55,"line":43399},[53,51420,51421],{}," * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)\n",[53,51423,51424],{"class":55,"line":43405},[53,51425,51298],{},[53,51427,51428],{"class":55,"line":43411},[53,51429,51430],{}," public boolean isSecure(Socket socket) throws IllegalArgumentException {\n",[53,51432,51433],{"class":55,"line":43417},[53,51434,21609],{},[53,51436,51437],{"class":55,"line":43423},[53,51438,860],{},[53,51440,51441],{"class":55,"line":43429},[53,51442,15330],{},[53,51444,51445],{"class":55,"line":43441},[53,51446,51447],{}," * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,\n",[53,51449,51450],{"class":55,"line":43455},[53,51451,51452],{}," * boolean)\n",[53,51454,51455],{"class":55,"line":43469},[53,51456,51298],{},[53,51458,51459],{"class":55,"line":43483},[53,51460,51461],{}," public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,\n",[53,51463,51464],{"class":55,"line":43495},[53,51465,51466],{}," UnknownHostException {\n",[53,51468,51469],{"class":55,"line":43507},[53,51470,51471],{}," return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);\n",[53,51473,51474],{"class":55,"line":43519},[53,51475,860],{},[53,51477,51478],{"class":55,"line":43531},[53,51479,51480],{}," // -------------------------------------------------------------------\n",[53,51482,51483],{"class":55,"line":43545},[53,51484,51485],{}," // javadoc in org.apache.http.conn.scheme.SocketFactory says :\n",[53,51487,51488],{"class":55,"line":43559},[53,51489,51490],{}," // Both Object.equals() and Object.hashCode() must be overridden\n",[53,51492,51493],{"class":55,"line":43571},[53,51494,51495],{}," // for the correct operation of some connection managers\n",[53,51497,51498],{"class":55,"line":43583},[53,51499,51480],{},[53,51501,51502],{"class":55,"line":43595},[53,51503,51504],{}," public boolean equals(Object obj) {\n",[53,51506,51507],{"class":55,"line":43607},[53,51508,51509],{}," return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));\n",[53,51511,51512],{"class":55,"line":43619},[53,51513,860],{},[53,51515,51516],{"class":55,"line":43631},[53,51517,51518],{}," public int hashCode() {\n",[53,51520,51521],{"class":55,"line":43643},[53,51522,51523],{}," return EasySSLSocketFactory.class.hashCode();\n",[53,51525,51526],{"class":55,"line":43649},[53,51527,860],{},[53,51529,51530],{"class":55,"line":43660},[53,51531,282],{},[18,51533,51534],{},"And the TrustManager:",[43,51536,51538],{"className":288,"code":51537,"language":290,"meta":48,"style":48},"\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",[50,51539,51540,51544,51548,51552,51556,51560,51564,51568,51572,51576,51580,51584,51588,51592,51596,51600,51604,51608,51612,51616,51621,51626,51631,51636,51641,51645,51650,51655,51659,51663,51668,51672,51676,51681,51686,51690,51695,51699,51704,51709,51714,51719,51724,51729,51734,51738,51743,51747,51751,51756,51760,51765,51770,51774,51778,51783,51787,51792,51797,51802,51806,51811,51815,51819,51823,51828,51832,51837,51842,51846],{"__ignoreMap":48},[53,51541,51542],{"class":55,"line":56},[53,51543,500],{"emptyLinePlaceholder":499},[53,51545,51546],{"class":55,"line":86},[53,51547,51019],{},[53,51549,51550],{"class":55,"line":126},[53,51551,51024],{},[53,51553,51554],{"class":55,"line":163},[53,51555,51029],{},[53,51557,51558],{"class":55,"line":186},[53,51559,51034],{},[53,51561,51562],{"class":55,"line":221},[53,51563,51039],{},[53,51565,51566],{"class":55,"line":242},[53,51567,51044],{},[53,51569,51570],{"class":55,"line":273},[53,51571,51049],{},[53,51573,51574],{"class":55,"line":279},[53,51575,51054],{},[53,51577,51578],{"class":55,"line":496},[53,51579,51059],{},[53,51581,51582],{"class":55,"line":503},[53,51583,51064],{},[53,51585,51586],{"class":55,"line":509},[53,51587,51059],{},[53,51589,51590],{"class":55,"line":515},[53,51591,51073],{},[53,51593,51594],{"class":55,"line":521},[53,51595,51078],{},[53,51597,51598],{"class":55,"line":527},[53,51599,51083],{},[53,51601,51602],{"class":55,"line":533},[53,51603,51088],{},[53,51605,51606],{"class":55,"line":539},[53,51607,51093],{},[53,51609,51610],{"class":55,"line":545},[53,51611,51098],{},[53,51613,51614],{"class":55,"line":2070},[53,51615,41470],{},[53,51617,51618],{"class":55,"line":2075},[53,51619,51620],{},"import java.security.KeyStore;\n",[53,51622,51623],{"class":55,"line":2081},[53,51624,51625],{},"import java.security.KeyStoreException;\n",[53,51627,51628],{"class":55,"line":2087},[53,51629,51630],{},"import java.security.NoSuchAlgorithmException;\n",[53,51632,51633],{"class":55,"line":2092},[53,51634,51635],{},"import java.security.cert.CertificateException;\n",[53,51637,51638],{"class":55,"line":2097},[53,51639,51640],{},"import java.security.cert.X509Certificate;\n",[53,51642,51643],{"class":55,"line":2103},[53,51644,51142],{},[53,51646,51647],{"class":55,"line":2109},[53,51648,51649],{},"import javax.net.ssl.TrustManagerFactory;\n",[53,51651,51652],{"class":55,"line":2115},[53,51653,51654],{},"import javax.net.ssl.X509TrustManager;\n",[53,51656,51657],{"class":55,"line":2120},[53,51658,41455],{},[53,51660,51661],{"class":55,"line":2946},[53,51662,51185],{},[53,51664,51665],{"class":55,"line":2952},[53,51666,51667],{}," * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $\n",[53,51669,51670],{"class":55,"line":2964},[53,51671,51195],{},[53,51673,51674],{"class":55,"line":2973},[53,51675,41470],{},[53,51677,51678],{"class":55,"line":2979},[53,51679,51680],{},"public class EasyX509TrustManager implements X509TrustManager {\n",[53,51682,51683],{"class":55,"line":2990},[53,51684,51685],{}," private X509TrustManager standardTrustManager = null;\n",[53,51687,51688],{"class":55,"line":10443},[53,51689,15330],{},[53,51691,51692],{"class":55,"line":26443},[53,51693,51694],{}," * Constructor for EasyX509TrustManager.\n",[53,51696,51697],{"class":55,"line":26448},[53,51698,51298],{},[53,51700,51701],{"class":55,"line":26453},[53,51702,51703],{}," public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {\n",[53,51705,51706],{"class":55,"line":26458},[53,51707,51708],{}," super();\n",[53,51710,51711],{"class":55,"line":26463},[53,51712,51713],{}," TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n",[53,51715,51716],{"class":55,"line":26468},[53,51717,51718],{}," factory.init(keystore);\n",[53,51720,51721],{"class":55,"line":26473},[53,51722,51723],{}," TrustManager[] trustmanagers = factory.getTrustManagers();\n",[53,51725,51726],{"class":55,"line":26478},[53,51727,51728],{}," if (trustmanagers.length == 0) {\n",[53,51730,51731],{"class":55,"line":27490},[53,51732,51733],{}," throw new NoSuchAlgorithmException(\"no trust manager found\");\n",[53,51735,51736],{"class":55,"line":27496},[53,51737,12712],{},[53,51739,51740],{"class":55,"line":27501},[53,51741,51742],{}," this.standardTrustManager = (X509TrustManager) trustmanagers[0];\n",[53,51744,51745],{"class":55,"line":27507},[53,51746,860],{},[53,51748,51749],{"class":55,"line":27513},[53,51750,15330],{},[53,51752,51753],{"class":55,"line":27519},[53,51754,51755],{}," * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)\n",[53,51757,51758],{"class":55,"line":27525},[53,51759,51298],{},[53,51761,51762],{"class":55,"line":27530},[53,51763,51764],{}," public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[53,51766,51767],{"class":55,"line":27536},[53,51768,51769],{}," standardTrustManager.checkClientTrusted(certificates, authType);\n",[53,51771,51772],{"class":55,"line":27542},[53,51773,860],{},[53,51775,51776],{"class":55,"line":27548},[53,51777,15330],{},[53,51779,51780],{"class":55,"line":27554},[53,51781,51782],{}," * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)\n",[53,51784,51785],{"class":55,"line":27559},[53,51786,51298],{},[53,51788,51789],{"class":55,"line":27565},[53,51790,51791],{}," public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[53,51793,51794],{"class":55,"line":27571},[53,51795,51796],{}," if ((certificates != null) && (certificates.length == 1)) {\n",[53,51798,51799],{"class":55,"line":27576},[53,51800,51801],{}," certificates[0].checkValidity();\n",[53,51803,51804],{"class":55,"line":27581},[53,51805,15185],{},[53,51807,51808],{"class":55,"line":27586},[53,51809,51810],{}," standardTrustManager.checkServerTrusted(certificates, authType);\n",[53,51812,51813],{"class":55,"line":27591},[53,51814,12712],{},[53,51816,51817],{"class":55,"line":27596},[53,51818,860],{},[53,51820,51821],{"class":55,"line":43099},[53,51822,15330],{},[53,51824,51825],{"class":55,"line":43115},[53,51826,51827],{}," * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()\n",[53,51829,51830],{"class":55,"line":43129},[53,51831,51298],{},[53,51833,51834],{"class":55,"line":43145},[53,51835,51836],{}," public X509Certificate[] getAcceptedIssuers() {\n",[53,51838,51839],{"class":55,"line":43161},[53,51840,51841],{}," return this.standardTrustManager.getAcceptedIssuers();\n",[53,51843,51844],{"class":55,"line":43175},[53,51845,860],{},[53,51847,51848],{"class":55,"line":43189},[53,51849,282],{},[18,51851,51852,51853,51858],{},"(Both classes are\nfrom ",[585,51854,51857],{"href":51855,"rel":51856},"https://web.archive.org/web/20111230175916/http://exchangeit.googlecode.com:80/svn-history/r23/trunk/src/com/byarger/exchangeit/",[589],"exchangeit","\nwith a small change on the EasySSLSocketFactory to work on android 2.2)",[18,51860,51861],{},"Now we have to do some other preparations and create a HttpClient that we can use to establish the connection:",[43,51863,51865],{"className":288,"code":51864,"language":290,"meta":48,"style":48},"\n//members\nprivate ClientConnectionManager clientConnectionManager;\nprivate HttpContext context;\nprivate HttpParams params;\n\n",[50,51866,51867,51871,51876,51881,51886],{"__ignoreMap":48},[53,51868,51869],{"class":55,"line":56},[53,51870,500],{"emptyLinePlaceholder":499},[53,51872,51873],{"class":55,"line":86},[53,51874,51875],{},"//members\n",[53,51877,51878],{"class":55,"line":126},[53,51879,51880],{},"private ClientConnectionManager clientConnectionManager;\n",[53,51882,51883],{"class":55,"line":163},[53,51884,51885],{},"private HttpContext context;\n",[53,51887,51888],{"class":55,"line":186},[53,51889,51890],{},"private HttpParams params;\n",[43,51892,51894],{"className":288,"code":51893,"language":290,"meta":48,"style":48},"\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",[50,51895,51896,51900,51905,51910,51915,51919,51924,51929,51934,51939,51944,51949,51954,51959,51964,51969,51974,51979,51984,51989,51994,51999,52004,52009,52014,52019,52024,52029],{"__ignoreMap":48},[53,51897,51898],{"class":55,"line":56},[53,51899,500],{"emptyLinePlaceholder":499},[53,51901,51902],{"class":55,"line":86},[53,51903,51904],{},"//constructor\n",[53,51906,51907],{"class":55,"line":126},[53,51908,51909],{},"public WebService(){\n",[53,51911,51912],{"class":55,"line":163},[53,51913,51914],{}," setup();\n",[53,51916,51917],{"class":55,"line":186},[53,51918,282],{},[53,51920,51921],{"class":55,"line":221},[53,51922,51923],{},"//prepare for the https connection\n",[53,51925,51926],{"class":55,"line":242},[53,51927,51928],{},"//call this in the constructor of the class that does the connection if\n",[53,51930,51931],{"class":55,"line":273},[53,51932,51933],{},"//it's used multiple times\n",[53,51935,51936],{"class":55,"line":279},[53,51937,51938],{},"private void setup(){\n",[53,51940,51941],{"class":55,"line":496},[53,51942,51943],{},"SchemeRegistry schemeRegistry = new SchemeRegistry();\n",[53,51945,51946],{"class":55,"line":503},[53,51947,51948],{}," // http scheme\n",[53,51950,51951],{"class":55,"line":509},[53,51952,51953],{}," schemeRegistry.register(new Scheme(\"http\", PlainSocketFactory.getSocketFactory(), 80));\n",[53,51955,51956],{"class":55,"line":515},[53,51957,51958],{}," // https scheme\n",[53,51960,51961],{"class":55,"line":521},[53,51962,51963],{}," schemeRegistry.register(new Scheme(\"https\", new EasySSLSocketFactory(), 443));\n",[53,51965,51966],{"class":55,"line":527},[53,51967,51968],{}," params = new BasicHttpParams();\n",[53,51970,51971],{"class":55,"line":533},[53,51972,51973],{}," params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);\n",[53,51975,51976],{"class":55,"line":539},[53,51977,51978],{}," params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));\n",[53,51980,51981],{"class":55,"line":545},[53,51982,51983],{}," params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);\n",[53,51985,51986],{"class":55,"line":2070},[53,51987,51988],{}," HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);\n",[53,51990,51991],{"class":55,"line":2075},[53,51992,51993],{}," HttpProtocolParams.setContentCharset(params, \"utf8\");\n",[53,51995,51996],{"class":55,"line":2081},[53,51997,51998],{}," CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n",[53,52000,52001],{"class":55,"line":2087},[53,52002,52003],{}," //set the user credentials for our site \"example.com\"\n",[53,52005,52006],{"class":55,"line":2092},[53,52007,52008],{}," credentialsProvider.setCredentials(new AuthScope(\"example.com\", AuthScope.ANY_PORT),\n",[53,52010,52011],{"class":55,"line":2097},[53,52012,52013],{}," new UsernamePasswordCredentials(\"UserNameHere\", \"UserPasswordHere\"));\n",[53,52015,52016],{"class":55,"line":2103},[53,52017,52018],{}," clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);\n",[53,52020,52021],{"class":55,"line":2109},[53,52022,52023],{}," context = new BasicHttpContext();\n",[53,52025,52026],{"class":55,"line":2115},[53,52027,52028],{}," context.setAttribute(\"http.auth.credentials-provider\", credentialsProvider);\n",[53,52030,52031],{"class":55,"line":2120},[53,52032,282],{},[43,52034,52036],{"className":288,"code":52035,"language":290,"meta":48,"style":48},"\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",[50,52037,52038,52042,52047,52052,52057,52062,52067,52072],{"__ignoreMap":48},[53,52039,52040],{"class":55,"line":56},[53,52041,500],{"emptyLinePlaceholder":499},[53,52043,52044],{"class":55,"line":86},[53,52045,52046],{},"public HttpResponse getResponseFromUrl(String url){\n",[53,52048,52049],{"class":55,"line":126},[53,52050,52051],{},"//connection (client has to be created for every new connection)\n",[53,52053,52054],{"class":55,"line":163},[53,52055,52056],{},"client = new DefaultHttpClient(clientConnectionManager, params);\n",[53,52058,52059],{"class":55,"line":186},[53,52060,52061],{},"HttpGet get = new HttpGet(url);\n",[53,52063,52064],{"class":55,"line":221},[53,52065,52066],{},"HttpResponse response = client.execute(get, context);\n",[53,52068,52069],{"class":55,"line":242},[53,52070,52071],{},"return response;\n",[53,52073,52074],{"class":55,"line":273},[53,52075,282],{},[18,52077,52078],{},"And that's it. I hope this will help some of you to solve their problems with self-signed certs!",[607,52080,989],{},{"title":48,"searchDepth":86,"depth":86,"links":52082},[],[4516,997],"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":50990,"description":50999},"blog/android-and-self-signed-ssl-certificates",[4526,52092,52093,52094,52095,52096],"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":52100,"title":52101,"author":52102,"body":52103,"category":52723,"date":52724,"description":52725,"extension":617,"link":52726,"meta":52727,"navigation":499,"path":52728,"seo":52729,"slug":52107,"stem":52731,"tags":52732,"teaser":52736,"__hash__":52737},"blog/blog/routing-driving-directions-on-android-part-2-draw-the-route.md","Routing / Driving directions on Android – Part 2: Draw the route",[12861],{"type":11,"value":52104,"toc":52718},[52105,52108,52117,52121,52124,52173,52176,52249,52252,52285,52289,52292,52295,52309,52312,52317,52324,52389,52392,52526,52530,52533,52536,52617,52620,52644,52647,52650,52653,52696,52699,52713,52716],[14,52106,52101],{"id":52107},"routing-driving-directions-on-android-part-2-draw-the-route",[18,52109,52110,52111,52116],{},"After you ",[585,52112,52115],{"href":52113,"rel":52114},"http://mobile.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/",[589],"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.",[2352,52118,52120],{"id":52119},"create-a-suiting-overlay","Create a suiting Overlay",[18,52122,52123],{},"We basically need a Overlay that takes two Geopoints and maybe a color in which the lines should be drawn. So here We\nhave:",[43,52125,52127],{"className":288,"code":52126,"language":290,"meta":48,"style":48},"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",[50,52128,52129,52134,52139,52144,52149,52154,52159,52164,52169],{"__ignoreMap":48},[53,52130,52131],{"class":55,"line":56},[53,52132,52133],{},"public class RouteOverlay extends Overlay {\n",[53,52135,52136],{"class":55,"line":86},[53,52137,52138],{}," private GeoPoint gp1;\n",[53,52140,52141],{"class":55,"line":126},[53,52142,52143],{}," private GeoPoint gp2;\n",[53,52145,52146],{"class":55,"line":163},[53,52147,52148],{}," private int color;\n",[53,52150,52151],{"class":55,"line":186},[53,52152,52153],{},"public RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) {\n",[53,52155,52156],{"class":55,"line":221},[53,52157,52158],{}," this.gp1 = gp1;\n",[53,52160,52161],{"class":55,"line":242},[53,52162,52163],{}," this.gp2 = gp2;\n",[53,52165,52166],{"class":55,"line":273},[53,52167,52168],{}," this.color = color;\n",[53,52170,52171],{"class":55,"line":279},[53,52172,860],{},[18,52174,52175],{},"Now all that’s left now for our Overlay is to override the draw() method and draw the line as we need it:",[43,52177,52179],{"className":288,"code":52178,"language":290,"meta":48,"style":48},"@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",[50,52180,52181,52185,52190,52195,52200,52205,52210,52215,52220,52225,52230,52235,52240,52245],{"__ignoreMap":48},[53,52182,52183],{"class":55,"line":56},[53,52184,35338],{},[53,52186,52187],{"class":55,"line":86},[53,52188,52189],{},"public void draw(Canvas canvas, MapView mapView, boolean shadow) {\n",[53,52191,52192],{"class":55,"line":126},[53,52193,52194],{}," Projection projection = mapView.getProjection();\n",[53,52196,52197],{"class":55,"line":163},[53,52198,52199],{}," Paint paint = new Paint();\n",[53,52201,52202],{"class":55,"line":186},[53,52203,52204],{}," Point point = new Point();\n",[53,52206,52207],{"class":55,"line":221},[53,52208,52209],{}," projection.toPixels(gp1, point);\n",[53,52211,52212],{"class":55,"line":242},[53,52213,52214],{}," paint.setColor(color);\n",[53,52216,52217],{"class":55,"line":273},[53,52218,52219],{}," Point point2 = new Point();\n",[53,52221,52222],{"class":55,"line":279},[53,52223,52224],{}," projection.toPixels(gp2, point2);\n",[53,52226,52227],{"class":55,"line":496},[53,52228,52229],{}," paint.setStrokeWidth(5);\n",[53,52231,52232],{"class":55,"line":503},[53,52233,52234],{}," paint.setAlpha(120);\n",[53,52236,52237],{"class":55,"line":509},[53,52238,52239],{}," canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);\n",[53,52241,52242],{"class":55,"line":515},[53,52243,52244],{}," super.draw(canvas, mapView, shadow);\n",[53,52246,52247],{"class":55,"line":521},[53,52248,282],{},[18,52250,52251],{},"Back in the Activity, just iterate over the GeoPoints that you got from google maps and add each of them to the MapView:",[43,52253,52255],{"className":288,"code":52254,"language":290,"meta":48,"style":48},"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",[50,52256,52257,52262,52267,52272,52277,52281],{"__ignoreMap":48},[53,52258,52259],{"class":55,"line":56},[53,52260,52261],{},"private void drawPath(List geoPoints, int color) {\n",[53,52263,52264],{"class":55,"line":86},[53,52265,52266],{}," List overlays = mapView.getOverlays();\n",[53,52268,52269],{"class":55,"line":126},[53,52270,52271],{}," for (int i = 1; i \u003C geoPoints.size(); i++) {\n",[53,52273,52274],{"class":55,"line":163},[53,52275,52276],{}," overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color));\n",[53,52278,52279],{"class":55,"line":186},[53,52280,35830],{},[53,52282,52283],{"class":55,"line":221},[53,52284,282],{},[2352,52286,52288],{"id":52287},"get-location-updates-from-the-location-manager","Get location updates from the location manager",[18,52290,52291],{},"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!",[18,52293,52294],{},"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:",[43,52296,52298],{"className":288,"code":52297,"language":290,"meta":48,"style":48},"\nlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n\n",[50,52299,52300,52304],{"__ignoreMap":48},[53,52301,52302],{"class":55,"line":56},[53,52303,500],{"emptyLinePlaceholder":499},[53,52305,52306],{"class":55,"line":86},[53,52307,52308],{},"locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[18,52310,52311],{},"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.",[18,52313,52314],{},[27,52315,52316],{},"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)",[18,52318,52319,52320,52323],{},"Also don’t forget to ",[27,52321,52322],{},"remove the listener"," if the MapView isn’t visible:",[43,52325,52327],{"className":288,"code":52326,"language":290,"meta":48,"style":48},"\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",[50,52328,52329,52333,52337,52342,52347,52352,52357,52361,52365,52370,52375,52380,52385],{"__ignoreMap":48},[53,52330,52331],{"class":55,"line":56},[53,52332,500],{"emptyLinePlaceholder":499},[53,52334,52335],{"class":55,"line":86},[53,52336,35338],{},[53,52338,52339],{"class":55,"line":126},[53,52340,52341],{}," protected void onPause() {\n",[53,52343,52344],{"class":55,"line":163},[53,52345,52346],{}," //remove the listener\n",[53,52348,52349],{"class":55,"line":186},[53,52350,52351],{}," locationManager.removeUpdates(this);\n",[53,52353,52354],{"class":55,"line":221},[53,52355,52356],{}," super.onPause();\n",[53,52358,52359],{"class":55,"line":242},[53,52360,860],{},[53,52362,52363],{"class":55,"line":273},[53,52364,35338],{},[53,52366,52367],{"class":55,"line":279},[53,52368,52369],{}," protected void onResume() {\n",[53,52371,52372],{"class":55,"line":496},[53,52373,52374],{}," //add the listener again\n",[53,52376,52377],{"class":55,"line":503},[53,52378,52379],{}," locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[53,52381,52382],{"class":55,"line":509},[53,52383,52384],{}," super.onResume();\n",[53,52386,52387],{"class":55,"line":515},[53,52388,860],{},[18,52390,52391],{},"Now we have to let our MapActivity implement LocationListener and react to the updates:",[43,52393,52395],{"className":288,"code":52394,"language":290,"meta":48,"style":48},"\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",[50,52396,52397,52401,52406,52410,52415,52420,52424,52429,52434,52439,52444,52449,52454,52459,52464,52469,52474,52478,52483,52488,52493,52498,52503,52508,52512,52517,52522],{"__ignoreMap":48},[53,52398,52399],{"class":55,"line":56},[53,52400,500],{"emptyLinePlaceholder":499},[53,52402,52403],{"class":55,"line":86},[53,52404,52405],{},"public class RouteActivity extends MapActivity implements LocationListener {\n",[53,52407,52408],{"class":55,"line":126},[53,52409,35338],{},[53,52411,52412],{"class":55,"line":163},[53,52413,52414],{},"public void onLocationChanged(Location location) {\n",[53,52416,52417],{"class":55,"line":186},[53,52418,52419],{},"drawUserPosition(location);\n",[53,52421,52422],{"class":55,"line":221},[53,52423,282],{},[53,52425,52426],{"class":55,"line":242},[53,52427,52428],{},"private void drawUserPosition(Location location) {\n",[53,52430,52431],{"class":55,"line":273},[53,52432,52433],{}," GeoPoint currentLocation;\n",[53,52435,52436],{"class":55,"line":279},[53,52437,52438],{}," currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location\n",[53,52440,52441],{"class":55,"line":496},[53,52442,52443],{}," getLongitude() * 1E6));\n",[53,52445,52446],{"class":55,"line":503},[53,52447,52448],{}," OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location),\n",[53,52450,52451],{"class":55,"line":509},[53,52452,52453],{}," getString(R.string.current_location));\n",[53,52455,52456],{"class":55,"line":515},[53,52457,52458],{}," mapOverlays.clear();\n",[53,52460,52461],{"class":55,"line":521},[53,52462,52463],{}," if (locationOverlays.size() > 1) {\n",[53,52465,52466],{"class":55,"line":527},[53,52467,52468],{}," // remove the old user position if there is one\n",[53,52470,52471],{"class":55,"line":533},[53,52472,52473],{}," locationOverlays.removeOverlay(1);\n",[53,52475,52476],{"class":55,"line":539},[53,52477,35830],{},[53,52479,52480],{"class":55,"line":545},[53,52481,52482],{}," //add new user position\n",[53,52484,52485],{"class":55,"line":2070},[53,52486,52487],{}," locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage));\n",[53,52489,52490],{"class":55,"line":2075},[53,52491,52492],{}," mapOverlays.add(locationOverlays);\n",[53,52494,52495],{"class":55,"line":2081},[53,52496,52497],{}," //.\n",[53,52499,52500],{"class":55,"line":2087},[53,52501,52502],{}," //. calculate / set the mapcenter, zoom to span\n",[53,52504,52505],{"class":55,"line":2092},[53,52506,52507],{}," //. see in previous posts\n",[53,52509,52510],{"class":55,"line":2097},[53,52511,52497],{},[53,52513,52514],{"class":55,"line":2103},[53,52515,52516],{}," RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler);\n",[53,52518,52519],{"class":55,"line":2109},[53,52520,52521],{}," rt.start();\n",[53,52523,52524],{"class":55,"line":2115},[53,52525,282],{},[2352,52527,52529],{"id":52528},"make-it-threaded","Make it threaded",[18,52531,52532],{},"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.",[18,52534,52535],{},"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:",[43,52537,52539],{"className":288,"code":52538,"language":290,"meta":48,"style":48},"\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",[50,52540,52541,52545,52550,52555,52560,52565,52570,52575,52580,52585,52590,52595,52600,52605,52609,52613],{"__ignoreMap":48},[53,52542,52543],{"class":55,"line":56},[53,52544,500],{"emptyLinePlaceholder":499},[53,52546,52547],{"class":55,"line":86},[53,52548,52549],{},"private class RouteHandler extends Handler {\n",[53,52551,52552],{"class":55,"line":126},[53,52553,52554],{}," public void handleMessage(Message msg) {\n",[53,52556,52557],{"class":55,"line":163},[53,52558,52559],{}," boolean error = msg.getData().getBoolean(\"error\", false);\n",[53,52561,52562],{"class":55,"line":186},[53,52563,52564],{}," if (!error) {\n",[53,52566,52567],{"class":55,"line":221},[53,52568,52569],{}," // set the geopoints (we can't just add the overlays\n",[53,52571,52572],{"class":55,"line":242},[53,52573,52574],{}," // to the map here, because it's on a different thread\n",[53,52576,52577],{"class":55,"line":273},[53,52578,52579],{}," geoPoints = (List\u003CGeoPoint>) msg.obj;\n",[53,52581,52582],{"class":55,"line":279},[53,52583,52584],{}," post(updateRoute);\n",[53,52586,52587],{"class":55,"line":496},[53,52588,52589],{}," } else {\n",[53,52591,52592],{"class":55,"line":503},[53,52593,52594],{}," // maybe you want to show an error message here to\n",[53,52596,52597],{"class":55,"line":509},[53,52598,52599],{}," // notice the user that the route can not be displayed\n",[53,52601,52602],{"class":55,"line":515},[53,52603,52604],{}," // because there's no connection to the internet\n",[53,52606,52607],{"class":55,"line":521},[53,52608,21600],{},[53,52610,52611],{"class":55,"line":527},[53,52612,12712],{},[53,52614,52615],{"class":55,"line":533},[53,52616,860],{},[18,52618,52619],{},"Send the geopoints from the RouteThread:",[43,52621,52623],{"className":288,"code":52622,"language":290,"meta":48,"style":48},"\nMessage msg = new Message();\nmsg.obj = decodePoly(encoded);\nhandler.dispatchMessage(msg);\n\n",[50,52624,52625,52629,52634,52639],{"__ignoreMap":48},[53,52626,52627],{"class":55,"line":56},[53,52628,500],{"emptyLinePlaceholder":499},[53,52630,52631],{"class":55,"line":86},[53,52632,52633],{},"Message msg = new Message();\n",[53,52635,52636],{"class":55,"line":126},[53,52637,52638],{},"msg.obj = decodePoly(encoded);\n",[53,52640,52641],{"class":55,"line":163},[53,52642,52643],{},"handler.dispatchMessage(msg);\n",[18,52645,52646],{},"(If you have further data to send, use a Bundle object and add it to the Message.)",[18,52648,52649],{},"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.",[18,52651,52652],{},"First we create a runnable in the Activity:",[43,52654,52656],{"className":288,"code":52655,"language":290,"meta":48,"style":48},"\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",[50,52657,52658,52662,52667,52672,52677,52682,52687,52691],{"__ignoreMap":48},[53,52659,52660],{"class":55,"line":56},[53,52661,500],{"emptyLinePlaceholder":499},[53,52663,52664],{"class":55,"line":86},[53,52665,52666],{},"final Runnable updateRoute = new Runnable() {\n",[53,52668,52669],{"class":55,"line":126},[53,52670,52671],{}," public void run() {\n",[53,52673,52674],{"class":55,"line":163},[53,52675,52676],{}," // draw the path and then invalidate the mapview so that it redraws itself\n",[53,52678,52679],{"class":55,"line":186},[53,52680,52681],{}," drawPath(geoPoints, Color.GREEN);\n",[53,52683,52684],{"class":55,"line":221},[53,52685,52686],{}," mapView.invalidate();\n",[53,52688,52689],{"class":55,"line":242},[53,52690,12712],{},[53,52692,52693],{"class":55,"line":273},[53,52694,52695],{}," };\n",[18,52697,52698],{},"And all that is left to do is to call it from inside the handler:",[43,52700,52702],{"className":288,"code":52701,"language":290,"meta":48,"style":48},"\npost(updateRoute);\n\n",[50,52703,52704,52708],{"__ignoreMap":48},[53,52705,52706],{"class":55,"line":56},[53,52707,500],{"emptyLinePlaceholder":499},[53,52709,52710],{"class":55,"line":86},[53,52711,52712],{},"post(updateRoute);\n",[18,52714,52715],{},"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!",[607,52717,989],{},{"title":48,"searchDepth":86,"depth":86,"links":52719},[52720,52721,52722],{"id":52119,"depth":86,"text":52120},{"id":52287,"depth":86,"text":52288},{"id":52528,"depth":86,"text":52529},[4516,997],"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":52101,"description":52730},"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",[4526,52733,11192,52734,52735],"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":52739,"title":52740,"author":52741,"body":52742,"category":53216,"date":53217,"description":53218,"extension":617,"link":53219,"meta":53220,"navigation":499,"path":53221,"seo":53222,"slug":52746,"stem":53224,"tags":53225,"teaser":53227,"__hash__":53228},"blog/blog/routing-driving-directions-on-android-part-1-get-the-route.md","Routing / Driving directions on Android – Part 1: Get the route",[12861],{"type":11,"value":52743,"toc":53207},[52744,52747,52761,52764,52766,52769,52772,52781,52784,52819,52822,52826,52830,52833,52839,52847,52850,52927,52930,52967,52970,52973,52982,52986,52989,52992,53029,53038,53182,53186,53189,53197,53201,53204],[14,52745,52740],{"id":52746},"routing-driving-directions-on-android-part-1-get-the-route",[18,52748,52749,52750,3038,52755,52760],{},"Complementary to Sebastian’s posts\nabout ",[585,52751,52754],{"href":52752,"rel":52753},"http://mobile.synyx.de/2010/04/google-maps-on-android/",[589],"how to navigate with the MapView",[585,52756,52759],{"href":52757,"rel":52758},"http://mobile.synyx.de/2010/05/google-maps-on-android-part-2-overlays/",[589],"how to add customized overlays to it",", I\nwant to show you, how you display the route between two points.",[18,52762,52763],{},"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.",[2352,52765,21146],{"id":21145},[18,52767,52768],{},"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…”.",[18,52770,52771],{},"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:",[43,52773,52775],{"className":288,"code":52774,"language":290,"meta":48,"style":48},"synyxGeoPoint = new GeoPoint(49002175, 8394160);\n",[50,52776,52777],{"__ignoreMap":48},[53,52778,52779],{"class":55,"line":56},[53,52780,52774],{},[18,52782,52783],{},"And the user location is also quite easy to get:",[43,52785,52787],{"className":288,"code":52786,"language":290,"meta":48,"style":48},"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",[50,52788,52789,52794,52799,52804,52809,52814],{"__ignoreMap":48},[53,52790,52791],{"class":55,"line":56},[53,52792,52793],{},"locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\n",[53,52795,52796],{"class":55,"line":86},[53,52797,52798],{},"Criteria criteria = new Criteria();\n",[53,52800,52801],{"class":55,"line":126},[53,52802,52803],{},"criteria.setAccuracy(Criteria.ACCURACY_FINE);\n",[53,52805,52806],{"class":55,"line":163},[53,52807,52808],{},"criteria.setAltitudeRequired(false);\n",[53,52810,52811],{"class":55,"line":186},[53,52812,52813],{},"Location lastKnownLocation =\n",[53,52815,52816],{"class":55,"line":221},[53,52817,52818],{},"locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));\n",[18,52820,52821],{},"With this we get the last known, accurate location of the user. So all there is left now, is to get the route.",[2352,52823,52825],{"id":52824},"getting-the-geopoints-from-google-maps","Getting the geopoints from google maps",[649,52827,52829],{"id":52828},"kml-with-driving-directions","Kml (with driving directions)",[18,52831,52832],{},"If you need the driving directions, you can build yourself a url like this one to get a kml file with all the\ninformation:",[18,52834,52835],{},[585,52836,52837],{"href":52837,"rel":52838},"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",[589],[18,52840,52841,52842,8526],{},"(For the list of parameters of google maps, see\nhere: ",[585,52843,52846],{"href":52844,"rel":52845},"https://web.archive.org/web/20080901081831/http://mapki.com/wiki/Google_Map_Parameters",[589],"mapki.com",[18,52848,52849],{},"Here’s how we did it:",[43,52851,52853],{"className":288,"code":52852,"language":290,"meta":48,"style":48},"\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",[50,52854,52855,52859,52864,52869,52874,52879,52884,52889,52894,52899,52904,52908,52913,52918,52923],{"__ignoreMap":48},[53,52856,52857],{"class":55,"line":56},[53,52858,500],{"emptyLinePlaceholder":499},[53,52860,52861],{"class":55,"line":86},[53,52862,52863],{},"public String getUrl(GeoPoint src, GeoPoint dest){\n",[53,52865,52866],{"class":55,"line":126},[53,52867,52868],{},"StringBuilder urlString = new StringBuilder();\n",[53,52870,52871],{"class":55,"line":163},[53,52872,52873],{},"urlString.append(\"http://maps.google.com/maps?f=d&hl=en\");\n",[53,52875,52876],{"class":55,"line":186},[53,52877,52878],{},"urlString.append(\"&saddr=\");\n",[53,52880,52881],{"class":55,"line":221},[53,52882,52883],{},"urlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));\n",[53,52885,52886],{"class":55,"line":242},[53,52887,52888],{},"urlString.append(\",\");\n",[53,52890,52891],{"class":55,"line":273},[53,52892,52893],{},"urlString.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));\n",[53,52895,52896],{"class":55,"line":279},[53,52897,52898],{},"urlString.append(\"&daddr=\");// to\n",[53,52900,52901],{"class":55,"line":496},[53,52902,52903],{},"urlString.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));\n",[53,52905,52906],{"class":55,"line":503},[53,52907,52888],{},[53,52909,52910],{"class":55,"line":509},[53,52911,52912],{},"urlString.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));\n",[53,52914,52915],{"class":55,"line":515},[53,52916,52917],{},"urlString.append(\"&ie=UTF8&0&om=0&output=kml\");\n",[53,52919,52920],{"class":55,"line":521},[53,52921,52922],{},"return urlString;\n",[53,52924,52925],{"class":55,"line":527},[53,52926,282],{},[18,52928,52929],{},"The file you get from this url looks like this:",[43,52931,52933],{"className":1980,"code":52932,"language":1982,"meta":48,"style":48},"\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",[50,52934,52935,52939,52944,52949,52954,52958,52963],{"__ignoreMap":48},[53,52936,52937],{"class":55,"line":56},[53,52938,500],{"emptyLinePlaceholder":499},[53,52940,52941],{"class":55,"line":86},[53,52942,52943],{},"Hohenheimer Str./B27 to Karlstraße/L561\n",[53,52945,52946],{"class":55,"line":126},[53,52947,52948],{},"....\n",[53,52950,52951],{"class":55,"line":163},[53,52952,52953],{},"Head southeast on Hohenheimer Str./B27 toward Bopserwaldstraße Continue to follow B27\n",[53,52955,52956],{"class":55,"line":186},[53,52957,52948],{},[53,52959,52960],{"class":55,"line":221},[53,52961,52962],{},"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",[53,52964,52965],{"class":55,"line":242},[53,52966,52948],{},[18,52968,52969],{},"(the first number of each pair is the longitude, the second the latitude)",[18,52971,52972],{},"To convert them to GeoPoints use:",[43,52974,52976],{"className":288,"code":52975,"language":290,"meta":48,"style":48},"GeoPoint geoPoint = new GeoPoint((int)(Double.parseDouble(latitude[0])*1E6),(int)(Double.parseDouble(longitude[0])*1E6));\n",[50,52977,52978],{"__ignoreMap":48},[53,52979,52980],{"class":55,"line":56},[53,52981,52975],{},[649,52983,52985],{"id":52984},"json-without-driving-directions","JSON (without driving directions)",[18,52987,52988],{},"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.",[18,52990,52991],{},"again an example of what the server returns:",[43,52993,52995],{"className":6989,"code":52994,"language":6991,"meta":48,"style":48},"{tooltipHtml:\" (572x26#160;km / 5 hours 14 mins)\",polylines:[{id:\"route0\",points:\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| .....\n",[50,52996,52997],{"__ignoreMap":48},[53,52998,52999,53002,53005,53007,53010,53012,53015,53018,53021,53024,53027],{"class":55,"line":56},[53,53000,53001],{"class":82},"{",[53,53003,53004],{"class":59},"tooltipHtml",[53,53006,2304],{"class":82},[53,53008,53009],{"class":63},"\" (572x26#160;km / 5 hours 14 mins)\"",[53,53011,1073],{"class":82},[53,53013,53014],{"class":59},"polylines",[53,53016,53017],{"class":82},":[{id:",[53,53019,53020],{"class":63},"\"route0\"",[53,53022,53023],{"class":82},",points:",[53,53025,53026],{"class":63},"\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| ....",[53,53028,4070],{"class":102},[18,53030,53031,53032,53037],{},"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 ",[585,53033,53036],{"href":53034,"rel":53035},"http://facstaff.unca.edu/mcmcclur/googlemaps/encodepolyline/",[589],"http://facstaff.unca.edu/",") :",[43,53039,53041],{"className":288,"code":53040,"language":290,"meta":48,"style":48},"// 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",[50,53042,53043,53048,53053,53058,53063,53068,53073,53078,53083,53088,53093,53098,53103,53108,53113,53118,53123,53128,53133,53138,53142,53146,53150,53154,53158,53163,53168,53173,53178],{"__ignoreMap":48},[53,53044,53045],{"class":55,"line":56},[53,53046,53047],{},"// get only the encoded geopoints\n",[53,53049,53050],{"class":55,"line":86},[53,53051,53052],{},"encoded = encoded.split(\"points:\"\")[1].split(\"\",\")[0];\n",[53,53054,53055],{"class":55,"line":126},[53,53056,53057],{},"// replace two backslashes by one (some error from the transmission)\n",[53,53059,53060],{"class":55,"line":163},[53,53061,53062],{},"encoded = encoded.replace(\"\\\\\", \"\\\");\n",[53,53064,53065],{"class":55,"line":186},[53,53066,53067],{},"//decoding\n",[53,53069,53070],{"class":55,"line":221},[53,53071,53072],{},"List poly = new ArrayList();\n",[53,53074,53075],{"class":55,"line":242},[53,53076,53077],{}," int index = 0, len = encoded.length();\n",[53,53079,53080],{"class":55,"line":273},[53,53081,53082],{}," int lat = 0, lng = 0;\n",[53,53084,53085],{"class":55,"line":279},[53,53086,53087],{}," while (index \u003C len) {\n",[53,53089,53090],{"class":55,"line":496},[53,53091,53092],{}," int b, shift = 0, result = 0;\n",[53,53094,53095],{"class":55,"line":503},[53,53096,53097],{}," do {\n",[53,53099,53100],{"class":55,"line":509},[53,53101,53102],{}," b = encoded.charAt(index++) - 63;\n",[53,53104,53105],{"class":55,"line":515},[53,53106,53107],{}," result |= (b & 0x1f) \u003C\u003C shift;\n",[53,53109,53110],{"class":55,"line":521},[53,53111,53112],{}," shift += 5;\n",[53,53114,53115],{"class":55,"line":527},[53,53116,53117],{}," } while (b >= 0x20);\n",[53,53119,53120],{"class":55,"line":533},[53,53121,53122],{}," int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[53,53124,53125],{"class":55,"line":539},[53,53126,53127],{}," lat += dlat;\n",[53,53129,53130],{"class":55,"line":545},[53,53131,53132],{}," shift = 0;\n",[53,53134,53135],{"class":55,"line":2070},[53,53136,53137],{}," result = 0;\n",[53,53139,53140],{"class":55,"line":2075},[53,53141,53097],{},[53,53143,53144],{"class":55,"line":2081},[53,53145,53102],{},[53,53147,53148],{"class":55,"line":2087},[53,53149,53107],{},[53,53151,53152],{"class":55,"line":2092},[53,53153,53112],{},[53,53155,53156],{"class":55,"line":2097},[53,53157,53117],{},[53,53159,53160],{"class":55,"line":2103},[53,53161,53162],{}," int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[53,53164,53165],{"class":55,"line":2109},[53,53166,53167],{}," lng += dlng;\n",[53,53169,53170],{"class":55,"line":2115},[53,53171,53172],{}," GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));\n",[53,53174,53175],{"class":55,"line":2120},[53,53176,53177],{}," poly.add(p);\n",[53,53179,53180],{"class":55,"line":2946},[53,53181,12712],{},[2352,53183,53185],{"id":53184},"getting-the-geopoints-from-open-streetmap","Getting the geopoints from open streetmap",[18,53187,53188],{},"You can also get the geopoints from open streetmap. It’s quite the same procedure, so i don’t write it all again.",[18,53190,53191,53192],{},"Here you can see for yourself: ",[585,53193,53196],{"href":53194,"rel":53195},"http://wiki.openstreetmap.org/wiki/YOURS#Routing_API",[589],"YOURS Routing_API",[2352,53198,53200],{"id":53199},"whats-in-the-next-post","What’s in the next post",[18,53202,53203],{},"That’s it for today, in the next post i will show you how to draw the route on your MapView properly.",[607,53205,53206],{},"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":48,"searchDepth":86,"depth":86,"links":53208},[53209,53210,53214,53215],{"id":21145,"depth":86,"text":21146},{"id":52824,"depth":86,"text":52825,"children":53211},[53212,53213],{"id":52828,"depth":126,"text":52829},{"id":52984,"depth":126,"text":52985},{"id":53184,"depth":86,"text":53185},{"id":53199,"depth":86,"text":53200},[4516,997],"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":52740,"description":53223},"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",[4526,53226,11192,52735],"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…","23Pc9hKpFDatuwCoBwCqHTTtAhn5eTixkOfHWMuYPBM",{"id":53230,"title":53231,"author":53232,"body":53233,"category":53378,"date":53379,"description":53380,"extension":617,"link":53381,"meta":53382,"navigation":499,"path":53383,"seo":53384,"slug":53237,"stem":53385,"tags":53386,"teaser":53389,"__hash__":53390},"blog/blog/template-based-document-generation-using-odfdom.md","Template based document generation using ODFDOM",[41317],{"type":11,"value":53234,"toc":53372},[53235,53238,53241,53256,53260,53269,53280,53283,53287,53295,53298,53301,53304,53307,53310,53312,53315,53322,53327,53330,53333,53336,53339,53342,53345,53348,53351,53354,53357,53359,53361,53364,53367,53369],[14,53236,53231],{"id":53237},"template-based-document-generation-using-odfdom",[18,53239,53240],{},"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.",[18,53242,53243,53244,53249,53250,53255],{},"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 ",[585,53245,53248],{"href":53246,"rel":53247},"http://www.openoffice.org/",[589],"OpenOffice.org",", the open source\nword processor, and transform these documents programmatically. OpenOffice.org uses the\nstandardized ",[585,53251,53254],{"href":53252,"rel":53253},"http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office",[589],"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, …).",[649,53257,53259],{"id":53258},"the-uno-approach","The UNO approach",[18,53261,53262,53263,53268],{},"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 ",[585,53264,53267],{"href":53265,"rel":53266},"http://wiki.services.openoffice.org/wiki/Uno",[589],"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:",[577,53270,53271,53274,53277],{},[580,53272,53273],{},"Understanding and learning the UNO API is hard and takes a lot of time",[580,53275,53276],{},"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)",[580,53278,53279],{},"An instance of OpenOffice.org can only be used by one thread at a time so you need some kind of instance pooling.",[18,53281,53282],{},"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.",[649,53284,53286],{"id":53285},"odfdom","ODFDOM",[18,53288,53289,53290,53294],{},"Some time after the standardization of the Open Document format a new project was\nborn: ",[585,53291,53286],{"href":53292,"rel":53293},"http://odftoolkit.org/projects/odfdom/pages/Home",[589],", 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.",[18,53296,53297],{},"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.",[18,53299,53300],{},"The following snippet creates a new text document, inserts some text and saves the document to a temp file.",[18,53302,53303],{},"`OdfTextDocument doc = OdfTextDocument.newTextDocument();",[18,53305,53306],{},"doc.addText(\"Hello World!\");",[18,53308,53309],{},"doc.save(File.createTempFile(\"odfdom\", \".odt\"));`",[649,53311,40278],{"id":40277},[18,53313,53314],{},"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.",[18,53316,53317,53318,53321],{},"Let’s see how we can replace our placeholder value. The values for user fields as inserted above are stored in a node\n",[585,53319,53320],{"href":53320},"text:user-field-decl",". This is an excerpt from the Open Document content.xml for a simple example document:",[18,53323,53324],{},[50,53325,53326],{},"\u003Ctext:user-field-decl office:value-type=\"string\" office:string-value=\"hello\" text:name=\"test\"/>",[18,53328,53329],{},"The user field is named test, it’s initial value for visual feedback is set to “hello”.",[18,53331,53332],{},"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”):",[18,53334,53335],{},"`Map\u003CString, String> values = new HashMap\u003CString, String>();",[18,53337,53338],{},"values.put(\"test\", \"inserted automatically\");",[18,53340,53341],{},"OdfDocument doc = OdfDocument.loadDocument(\"/path/to/template.odt\");",[18,53343,53344],{},"NodeList nodes = doc.getOfficeBody().getElementsByTagName(OdfTextUserFieldDecl.ELEMENT_NAME.getQName());",[18,53346,53347],{},"for (int i = 0; i \u003C nodes.getLength(); i++) {",[18,53349,53350],{},"OdfTextUserFieldDecl element = (OdfTextUserFieldDecl) nodes.item(i);",[18,53352,53353],{},"if (values.containsKey(element.getTextNameAttribute())) {",[18,53355,53356],{},"element.setOfficeStringValueAttribute(values.get(element.getTextNameAttribute()));",[18,53358,47063],{},[18,53360,47063],{},[18,53362,53363],{},"doc.save(\"/path/to/result.odt\");`",[18,53365,53366],{},"When running the code above, the value in the document is replaced with the value set programmatically.",[649,53368,12763],{"id":12762},[18,53370,53371],{},"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":48,"searchDepth":86,"depth":86,"links":53373},[53374,53375,53376,53377],{"id":53258,"depth":126,"text":53259},{"id":53285,"depth":126,"text":53286},{"id":40277,"depth":126,"text":40278},{"id":12762,"depth":126,"text":12763},[613,996],"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":53231,"description":53240},"blog/template-based-document-generation-using-odfdom",[53387,290,53285,53388],"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…","s3AA3WrDYmRY0S6MePU2SvOKGaEOPjNDiiAX_h4Pn5U",{"id":53392,"title":53393,"author":53394,"body":53395,"category":53425,"date":53426,"description":53427,"extension":617,"link":53428,"meta":53429,"navigation":499,"path":53430,"seo":53431,"slug":53433,"stem":53434,"tags":53435,"teaser":53437,"__hash__":53438},"blog/blog/uberladen-vs-trivialisieren-java-magazin-artikel.md","Überladen vs. Trivialisieren – Zwischen Platin und Blech",[11619],{"type":11,"value":53396,"toc":53423},[53397,53400,53420],[14,53398,53393],{"id":53399},"überladen-vs-trivialisieren-zwischen-platin-und-blech",[18,53401,53402,53403,53407,53408,53413,53414,53419],{},"Das Interview mit Joachim Arrasz, Softwarearchitekt bei ",[585,53404,53406],{"href":11728,"rel":53405},[589],"Synyx GmbH & Co. KG",",\nund ",[585,53409,53412],{"href":53410,"rel":53411},"http://www.pbit.org/index.html",[589],"Pavlo Baron",", Enterprise Architekt in München, ist nun\nim ",[585,53415,53418],{"href":53416,"rel":53417},"http://it-republik.de/jaxenter/java-magazin-ausgaben/Lucene-000399.html",[589],"Java Magazin"," erschienen. In der Rubrik\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.",[18,53421,53422],{},"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":48,"searchDepth":86,"depth":86,"links":53424},[],[613],"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":53393,"description":53432},"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",[53436,3494],"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":53440,"title":53441,"author":53442,"body":53443,"category":53477,"date":53478,"description":53479,"extension":617,"link":53480,"meta":53481,"navigation":499,"path":53482,"seo":53483,"slug":53447,"stem":53485,"tags":53486,"teaser":53487,"__hash__":53488},"blog/blog/wwdc-2010.md","WWDC 2010",[46383],{"type":11,"value":53444,"toc":53475},[53445,53448,53457,53472],[14,53446,53441],{"id":53447},"wwdc-2010",[18,53449,53450,53451,53456],{},"I’m going to ",[585,53452,53455],{"href":53453,"rel":53454},"http://developer.apple.com/wwdc/",[589],"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.",[18,53458,53459,53460,53465,53466,53471],{},"The conference is ",[585,53461,53464],{"href":53462,"rel":53463},"http://developer.apple.com/wwdc/sessions/",[589],"mostly covering iPhone"," related topics. There\nare ",[585,53467,53470],{"href":53468,"rel":53469},"http://www.fscklog.com/2010/05/apple-best%C3%A4tigt-wwdc-keynote-mit-steve-jobs-am-7-juni.html",[589],"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.",[18,53473,53474],{},"If you are going to WWDC as well and would like to meet up, let me know.",{"title":48,"searchDepth":86,"depth":86,"links":53476},[],[4516],"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":53441,"description":53484},"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",[37119,3491,46406],"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":53490,"title":53491,"author":53492,"body":53493,"category":53730,"date":53731,"description":53732,"extension":617,"link":53733,"meta":53734,"navigation":499,"path":53735,"seo":53736,"slug":53497,"stem":53738,"tags":53739,"teaser":53741,"__hash__":53742},"blog/blog/performance-optimization-in-synyx-sudoku.md","Performance optimization in Synyx Sudoku",[12861],{"type":11,"value":53494,"toc":53728},[53495,53498,53507,53510,53513,53516,53519,53528,53531,53534,53537,53580,53583,53695,53698,53713,53720,53723,53726],[14,53496,53491],{"id":53497},"performance-optimization-in-synyx-sudoku",[18,53499,53500,53501,53506],{},"Now that ",[585,53502,53505],{"href":53503,"rel":53504},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku",[589],"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.",[18,53508,53509],{},"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.",[18,53511,53512],{},"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.",[18,53514,53515],{},"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.",[18,53517,53518],{},"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.",[18,53520,53521,53522,53527],{},"After a little bit of research we found the solution to this in the google I/O\nvideo ",[585,53523,53526],{"href":53524,"rel":53525},"http://www.youtube.com/watch?v=N6YdwzAvwOA",[589],"“Make your Android UI Fast and Efficient”",", which you really should\nwatch, if you haven’t by now!",[18,53529,53530],{},"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.",[18,53532,53533],{},"And that’s how it’s looking in the code:",[18,53535,53536],{},"First you have to overwrite the BaseAdapter from android so that you can give it your specific views to display:",[43,53538,53540],{"className":288,"code":53539,"language":290,"meta":48,"style":48},"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",[50,53541,53542,53547,53552,53557,53562,53567,53572,53576],{"__ignoreMap":48},[53,53543,53544],{"class":55,"line":56},[53,53545,53546],{},"public class HighscoreListAdapter extends BaseAdapter {\n",[53,53548,53549],{"class":55,"line":86},[53,53550,53551],{}," private List valueList;\n",[53,53553,53554],{"class":55,"line":126},[53,53555,53556],{}," private LayoutInflater inflater;\n",[53,53558,53559],{"class":55,"line":163},[53,53560,53561],{}," public HighscoreListAdapter(List highscoreValueList, LayoutInflater inflater) {\n",[53,53563,53564],{"class":55,"line":186},[53,53565,53566],{}," this.inflater = inflater;\n",[53,53568,53569],{"class":55,"line":221},[53,53570,53571],{}," this.valueList = highscoreValueList;\n",[53,53573,53574],{"class":55,"line":242},[53,53575,7109],{},[53,53577,53578],{"class":55,"line":273},[53,53579,282],{},[18,53581,53582],{},"and also overwrite the getView method from it, so that you can make your recycling in there:",[43,53584,53586],{"className":288,"code":53585,"language":290,"meta":48,"style":48},"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",[50,53587,53588,53593,53598,53603,53608,53613,53618,53623,53628,53633,53638,53643,53647,53652,53657,53661,53666,53671,53676,53681,53686,53691],{"__ignoreMap":48},[53,53589,53590],{"class":55,"line":56},[53,53591,53592],{},"public View getView(int position, View convertView, ViewGroup parent) {\n",[53,53594,53595],{"class":55,"line":86},[53,53596,53597],{}," HighscoreViewHolder highscoreViewHolder;\n",[53,53599,53600],{"class":55,"line":126},[53,53601,53602],{}," if (convertView == null) {\n",[53,53604,53605],{"class":55,"line":163},[53,53606,53607],{}," // the first few elements of the list are created out of the xml\n",[53,53609,53610],{"class":55,"line":186},[53,53611,53612],{}," convertView = inflater.inflate(R.layout.highscore_list_entry, null);\n",[53,53614,53615],{"class":55,"line":221},[53,53616,53617],{}," highscoreViewHolder = new HighscoreViewHolder();\n",[53,53619,53620],{"class":55,"line":242},[53,53621,53622],{}," highscoreViewHolder.name = (TextView) convertView.findViewById(R.id.highscore_list_entry_name);\n",[53,53624,53625],{"class":55,"line":273},[53,53626,53627],{}," highscoreViewHolder.score = (TextView) convertView.findViewById(R.id.highscore_list_entry_score);\n",[53,53629,53630],{"class":55,"line":279},[53,53631,53632],{}," highscoreViewHolder.rank = (TextView) convertView.findViewById(R.id.highscore_list_entry_rank);\n",[53,53634,53635],{"class":55,"line":496},[53,53636,53637],{}," convertView.setTag(highscoreViewHolder);\n",[53,53639,53640],{"class":55,"line":503},[53,53641,53642],{}," convertView.setFocusable(false);\n",[53,53644,53645],{"class":55,"line":509},[53,53646,35811],{},[53,53648,53649],{"class":55,"line":515},[53,53650,53651],{}," // recycle of the view that went out of the view\n",[53,53653,53654],{"class":55,"line":521},[53,53655,53656],{}," highscoreViewHolder = (HighscoreViewHolder) convertView.getTag();\n",[53,53658,53659],{"class":55,"line":527},[53,53660,7109],{},[53,53662,53663],{"class":55,"line":533},[53,53664,53665],{}," HighscoreValue value = this.valueList.get(position);\n",[53,53667,53668],{"class":55,"line":539},[53,53669,53670],{}," // set the new values\n",[53,53672,53673],{"class":55,"line":545},[53,53674,53675],{}," highscoreViewHolder.name.setText(value.getName());\n",[53,53677,53678],{"class":55,"line":2070},[53,53679,53680],{}," highscoreViewHolder.score.setText(Integer.toString(value.getScore()));\n",[53,53682,53683],{"class":55,"line":2075},[53,53684,53685],{}," highscoreViewHolder.rank.setText(Integer.toString(value.getRank()));\n",[53,53687,53688],{"class":55,"line":2081},[53,53689,53690],{}," return convertView;\n",[53,53692,53693],{"class":55,"line":2087},[53,53694,282],{},[18,53696,53697],{},"now whats only left is to create the adapter, and assign it to the ListView:",[43,53699,53701],{"className":288,"code":53700,"language":290,"meta":48,"style":48},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\nlistView.setAdapter(highscoreListAdapter);\n",[50,53702,53703,53708],{"__ignoreMap":48},[53,53704,53705],{"class":55,"line":56},[53,53706,53707],{},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\n",[53,53709,53710],{"class":55,"line":86},[53,53711,53712],{},"listView.setAdapter(highscoreListAdapter);\n",[18,53714,53715,53716,53719],{},"If you make some changes in the data set you gave to the adapter, just call ",[573,53717,53718],{},"notifyDataSetChanged()"," on it to let it\nrefresh itself.",[18,53721,53722],{},"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.",[18,53724,53725],{},"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.",[607,53727,989],{},{"title":48,"searchDepth":86,"depth":86,"links":53729},[],[4516,997],"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":53491,"description":53737},"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",[4526,53740],"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":53744,"title":53745,"author":53746,"body":53747,"category":53842,"date":53843,"description":53754,"extension":617,"link":53844,"meta":53845,"navigation":499,"path":53846,"seo":53847,"slug":53848,"stem":53849,"tags":53850,"teaser":53851,"__hash__":53852},"blog/blog/5-reasons-for-teams.md","Five reasons why you should not work alone on IT-Projects",[11420],{"type":11,"value":53748,"toc":53835},[53749,53752,53755,53758,53761,53765,53774,53777,53781,53790,53794,53797,53800,53804,53807,53810,53814,53817,53826],[14,53750,53745],{"id":53751},"five-reasons-why-you-should-not-work-alone-on-it-projects",[18,53753,53754],{},"In my opinion its much better to have a team working on a project than a single person.",[18,53756,53757],{},"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.",[18,53759,53760],{},"Here are my top five reasons why you should not leave one guy alone with a IT project…",[649,53762,53764],{"id":53763},"avoid-single-points-of-failure","Avoid Single Points of Failure",[18,53766,53767,53768,53773],{},"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 ",[585,53769,53772],{"href":53770,"rel":53771},"http://en.wikipedia.org/wiki/Single_Point_of_Failure",[589],"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.",[18,53775,53776],{},"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.",[649,53778,53780],{"id":53779},"think-twice-triply","Think Twice / Triply / …",[18,53782,53783,53784,53789],{},"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 ",[585,53785,53788],{"href":53786,"rel":53787},"http://www.extremeprogramming.org/rules/pair.html",[589],"Pairprogramming"," for tricky parts of the application.",[649,53791,53793],{"id":53792},"individual-skills","Individual Skills",[18,53795,53796],{},"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.",[18,53798,53799],{},"Since IT-Projects require alot of different skills each of them will benefit from an increased bandwidth of skills.",[649,53801,53803],{"id":53802},"motivation","Motivation",[18,53805,53806],{},"IT-Projects can be frustrating sometimes. One person that works alone gets demotivated easily because he feels left\nalone with whatever is frustrating him.",[18,53808,53809],{},"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.",[649,53811,53813],{"id":53812},"distraction","Distraction",[18,53815,53816],{},"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.",[18,53818,53819,53820,53825],{},"A small daily standup-meeting (e.g. a ",[585,53821,53824],{"href":53822,"rel":53823},"http://www.scrumbasics.com/conducting-daily-scrum-meeting/",[589],"daily SCRUM",") where\neveryone explains what he has done the last day can help the team to stay focused.",[18,53827,53828,53829,53834],{},"Imagine how guilty you’d feel if everyone worked hard and you only watched videos on ",[585,53830,53833],{"href":53831,"rel":53832},"http://www.youtube.com/",[589],"youtube","\ninstead of writing a unit test for the feature you implemented the day before.",{"title":48,"searchDepth":86,"depth":86,"links":53836},[53837,53838,53839,53840,53841],{"id":53763,"depth":126,"text":53764},{"id":53779,"depth":126,"text":53780},{"id":53792,"depth":126,"text":53793},{"id":53802,"depth":126,"text":53803},{"id":53812,"depth":126,"text":53813},[613],"2010-05-25T11:03:19","https://synyx.de/blog/5-reasons-for-teams/",{},"/blog/5-reasons-for-teams",{"title":53745,"description":53754},"5-reasons-for-teams","blog/5-reasons-for-teams",[44991,47005,25655],"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":53854,"title":53855,"author":53856,"body":53857,"category":53966,"date":53967,"description":53968,"extension":617,"link":53969,"meta":53970,"navigation":499,"path":53971,"seo":53972,"slug":53973,"stem":53974,"tags":53975,"teaser":53977,"__hash__":53978},"blog/blog/scrum-an-anti-word.md","Why is Scrum getting an anti-word?",[40236],{"type":11,"value":53858,"toc":53961},[53859,53862,53865,53868,53875,53880,53886,53889,53915,53918,53922,53925,53928,53931,53934,53937,53940,53943,53946,53949,53952,53955,53958],[14,53860,53855],{"id":53861},"why-is-scrum-getting-an-anti-word",[18,53863,53864],{},"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.",[18,53866,53867],{},"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?",[18,53869,53870,53871,53874],{},"I probably couldn’t find many people who’d respond with an unconditional “",[27,53872,53873],{},"YES!","” to this question.",[18,53876,53877],{},[27,53878,53879],{},"But why? Why is Scrum getting an anti-word for many?",[18,53881,53882],{},[1773,53883],{"alt":53884,"src":53885},"\"Security!\"","http://defunctscrum.blogspot.com/2007/07/should-you-leave-scrum-off-resume.html",[18,53887,53888],{},"There are various different reasons:",[577,53890,53891,53894,53897,53900,53903,53906,53909,53912],{},[580,53892,53893],{},"Scrum’s transparency creats angst for people living in and from intransparency",[580,53895,53896],{},"Scrum’s need for change is uncomfortable for people’s need for stability",[580,53898,53899],{},"Wrong implementation of Scrum",[580,53901,53902],{},"Scrum Master who don’t take people with them and run ahead in their own speed",[580,53904,53905],{},"Unbalanced power division between roles",[580,53907,53908],{},"Scrum Master who have THE solution instead of enabling teams to find differing ways for different problems",[580,53910,53911],{},"The Scrum hype and overwhelming/missleading marketing",[580,53913,53914],{},"Could go on with lots more",[18,53916,53917],{},"Let’s focus on a few and look at them a little bit more in-depth.",[649,53919,53921],{"id":53920},"speedy-scrum-master","Speedy Scrum Master",[18,53923,53924],{},"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.",[18,53926,53927],{},"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.",[18,53929,53930],{},"The more this happens (and I guess it does a lot), the more people will say “Oh no, not another of these Scrum guys”.",[649,53932,53905],{"id":53933},"unbalanced-power-division-between-roles",[18,53935,53936],{},"Unbalanced power division between roles causes wrong implementation and frustration.",[18,53938,53939],{},"For example Scrum Masters who don’t have the support of upper management to make things happen.",[18,53941,53942],{},"Teams without the power to stop a sprint, without the needed skills and not cross-functional are handicapped teams.",[18,53944,53945],{},"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!”.",[18,53947,53948],{},"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.",[649,53950,53911],{"id":53951},"the-scrum-hype-and-overwhelmingmissleading-marketing",[18,53953,53954],{},"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.",[18,53956,53957],{},"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?",[18,53959,53960],{},"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":48,"searchDepth":86,"depth":86,"links":53962},[53963,53964,53965],{"id":53920,"depth":126,"text":53921},{"id":53933,"depth":126,"text":53905},{"id":53951,"depth":126,"text":53911},[613],"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":53855,"description":53864},"scrum-an-anti-word","blog/scrum-an-anti-word",[4221,53976,19860,4232,6503],"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…","61KWdx6d7036gQMdieWCu_XLPod6Mrrx0FD70e-o9Jk",{"id":53980,"title":53981,"author":53982,"body":53983,"category":54103,"date":54104,"description":54105,"extension":617,"link":54106,"meta":54107,"navigation":499,"path":54108,"seo":54109,"slug":53987,"stem":54111,"tags":54112,"teaser":54114,"__hash__":54115},"blog/blog/user-statistics-from-synyxsudoku.md","User statistics from SynyxSudoku",[12861],{"type":11,"value":53984,"toc":54101},[53985,53988,53996,54004,54007,54051,54054,54098],[14,53986,53981],{"id":53987},"user-statistics-from-synyxsudoku",[18,53989,53990,53991,53995],{},"First of all, I was quite surprised as i saw that 70% of the ",[585,53992,53505],{"href":53993,"rel":53994},"http://tinyurl.com/SynyxSudoku",[589]," 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%.",[18,53997,53998,53999,54003],{},"So as you can imagine, we’ve got quite a few samples of data since\nthe ",[585,54000,10726],{"href":54001,"rel":54002},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku/",[589],", on that we now want to give you a little\noverview.",[18,54005,54006],{},"The devices came mostly with the latest Android versions available:",[54008,54009,54010,54023],"table",{},[54011,54012,54013],"thead",{},[54014,54015,54016,54020],"tr",{},[54017,54018,54019],"th",{},"Version",[54017,54021,54022],{},"Count",[54024,54025,54026,54035,54043],"tbody",{},[54014,54027,54028,54032],{},[54029,54030,54031],"td",{},"1.6",[54029,54033,54034],{},"39%",[54014,54036,54037,54040],{},[54029,54038,54039],{},"2.1",[54029,54041,54042],{},"11%",[54014,54044,54045,54048],{},[54029,54046,54047],{},"2.1-update1",[54029,54049,54050],{},"50%",[18,54052,54053],{},"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.",[54008,54055,54056,54065],{},[54011,54057,54058],{},[54014,54059,54060,54063],{},[54017,54061,54062],{},"Resolution",[54017,54064,54022],{},[54024,54066,54067,54075,54083,54091],{},[54014,54068,54069,54072],{},[54029,54070,54071],{},"240×320",[54029,54073,54074],{},"9%",[54014,54076,54077,54080],{},[54029,54078,54079],{},"320×480",[54029,54081,54082],{},"33%",[54014,54084,54085,54088],{},[54029,54086,54087],{},"480×800",[54029,54089,54090],{},"25%",[54014,54092,54093,54096],{},[54029,54094,54095],{},"480×854",[54029,54097,54082],{},[18,54099,54100],{},"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":48,"searchDepth":86,"depth":86,"links":54102},[],[4516,4650],"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":53981,"description":54110},"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",[4526,54113],"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":54117,"title":54118,"author":54119,"body":54120,"category":54223,"date":54224,"description":54225,"extension":617,"link":54226,"meta":54227,"navigation":499,"path":54228,"seo":54229,"slug":54124,"stem":54230,"tags":54231,"teaser":54233,"__hash__":54234},"blog/blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails.md","RedmineUpgrade – Ruby on Debian without leaving the rails",[33954],{"type":11,"value":54121,"toc":54221},[54122,54125,54128,54131,54139,54142,54147,54160,54163,54166,54169,54172,54192,54195,54201,54204,54212,54215,54218],[14,54123,54118],{"id":54124},"redmineupgrade-ruby-on-debian-without-leaving-the-rails",[18,54126,54127],{},"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).",[18,54129,54130],{},"Das Setup:",[577,54132,54133,54136],{},[580,54134,54135],{},"Debian GNU/Linux 5.0",[580,54137,54138],{},"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 😉 )",[18,54140,54141],{},"Nun sind schon bei vergangenen Updates verschiedene Stolpersteine ans Tageslicht gekommen, einige erwiesen sich\nallerdings als ein und der selbe:",[18,54143,54144],{},[573,54145,54146],{},"An der Debian-Paket-Verwaltung vorbei(!) die richtigen Versionen (wie im Redmine-Upgrade-Tutorial genannt)\ninstallieren",[577,54148,54149],{},[580,54150,54151,54152,54155,54156],{},"rubygems – wichtig hierbei ",[27,54153,54154],{},"nicht"," das Paket “rubygems1.8” aus dem Debian-Repository verwenden sondern von Hand\ninstallieren: `wget ",[585,54157,54158],{"href":54158,"rel":54159},"http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz",[589],[18,54161,54162],{},"tar xzf rubygems-1.3.1.tgz",[18,54164,54165],{},"cd rubygems-1.3.1",[18,54167,54168],{},"sudo ruby setup.rb",[18,54170,54171],{},"gem -v`",[577,54173,54174,54183],{},[580,54175,54176,54177,54179,54180],{},"Rake – auch hier gilt ",[27,54178,54154],{}," das Paket “rake” v=0.7.1-1 aus dem Debian-Repository verwenden. Man nutze den\n“Ruby-way”: ",[50,54181,54182],{},"gem install rake -v=0.8.3",[580,54184,54185,54186,54188,54189],{},"rails – guess what… richtig, ",[27,54187,54154],{}," das Paket “rails” aus dem Debian-Repository verwenden. Man nutze abermals den\n“Ruby-way”: ",[50,54190,54191],{},"gem install rails -v=2.3.5",[18,54193,54194],{},"Nachdem die Gleise damit verlegt sind, funktioniert auch der Upgrade-Guide wie gewünscht.",[18,54196,54197,54198],{},"Wenn der kritische “point of no return” ",[50,54199,54200],{},"rake db:migrate RAILS_ENV=production",[18,54202,54203],{},"überstanden ist, die neue Version von Redmine gestartet wurde, aber dann Dinge wie:",[577,54205,54206,54209],{},[580,54207,54208],{},"“*** Exception NoMethodError in application (undefined method…”",[580,54210,54211],{},"“Premature end of script headers…”",[18,54213,54214],{},"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",[18,54216,54217],{},"passenger-install-apache2-module`",[18,54219,54220],{},"Sobald auch die letzten Anweisungen der Installation befolgt worden sind, können die Bugs kommen.",{"title":48,"searchDepth":86,"depth":86,"links":54222},[],[5568],"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":54118,"description":54127},"blog/redmineupgrade-ruby-on-debian-without-leaving-the-rails",[41310,45267,54232,25086],"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":54236,"title":54237,"author":54238,"body":54239,"category":54335,"date":54336,"description":54246,"extension":617,"link":54337,"meta":54338,"navigation":499,"path":54339,"seo":54340,"slug":54243,"stem":54341,"tags":54342,"teaser":54343,"__hash__":54344},"blog/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration.md","Ausbildungsplatz zum Fachinformatiker/in Systemintegeration",[11619],{"type":11,"value":54240,"toc":54333},[54241,54244,54247,54251,54265,54269,54286,54290,54304,54308,54319,54325,54327,54330],[14,54242,54237],{"id":54243},"ausbildungsplatz-zum-fachinformatikerin-systemintegeration",[18,54245,54246],{},"Wir haben noch einen Ausbildungsplatz als Fachinformatiker/in Systemintegration ab September 2010 zu vergeben.",[1217,54248,54250],{"id":54249},"deine-aufgaben","Deine Aufgaben:",[577,54252,54253,54256,54259,54262],{},[580,54254,54255],{},"Unterstützung bei der Planung und Erweiterung unserer Infrastruktur",[580,54257,54258],{},"Inbetriebnahme, Betreuung und Überwachung unserer Systeme (Debian, Ubuntu, SuSE, Windows 2000, Windows XP)",[580,54260,54261],{},"Unterstützen unserer Mitarbeiter bei technischen Problemen",[580,54263,54264],{},"Unterstützung von Kunden bei Software- und Hardwareproblemen",[1217,54266,54268],{"id":54267},"unsere-anforderungen","Unsere Anforderungen:",[577,54270,54271,54274,54277,54280,54283],{},[580,54272,54273],{},"Mittlere Reife oder Hochschulreife",[580,54275,54276],{},"Gute Englischkenntnisse in Wort und Schrift",[580,54278,54279],{},"Erfahrungen in Netzwerktechnik, TCP/IP",[580,54281,54282],{},"Gute EDV-Kenntnisse",[580,54284,54285],{},"Grundkenntnisse im Bereich Linux",[1217,54287,54289],{"id":54288},"das-solltest-du-mitbringen","Das solltest Du mitbringen:",[577,54291,54292,54295,54298,54301],{},[580,54293,54294],{},"Eigeninitiative und Engagement",[580,54296,54297],{},"Teamfähigkeit",[580,54299,54300],{},"Freude am Umgang mit neuen Technologien",[580,54302,54303],{},"Entschlossenheit, Lösungen für Probleme zu finden",[1217,54305,54307],{"id":54306},"das-erwartet-dich","Das erwartet Dich:",[577,54309,54310,54313,54316],{},[580,54311,54312],{},"Ein junges, innovatives Team",[580,54314,54315],{},"Ein technologisch spannendes Umfeld",[580,54317,54318],{},"Flexible, eigenständige, verantwortungsvolle Tätigkeiten",[18,54320,54321,54322,54324],{},"Bewerbungen bitte per Mail an ",[585,54323,17981],{"href":17980}," oder per Post an:",[18,54326,53406],{},[18,54328,54329],{},"Karlstraße 68",[18,54331,54332],{},"D-76137 Karlsruhe",{"title":48,"searchDepth":86,"depth":86,"links":54334},[],[5568],"2010-05-03T11:08:44","https://synyx.de/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration/",{},"/blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration",{"title":54237,"description":54246},"blog/ausbildungsplatz-zum-fachinformatikerin-systemintegeration",[5579,40229,5743],"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":54346,"title":54347,"author":54348,"body":54349,"category":54424,"date":54425,"description":54426,"extension":617,"link":54427,"meta":54428,"navigation":499,"path":54429,"seo":54430,"slug":54432,"stem":54433,"tags":54434,"teaser":54437,"__hash__":54438},"blog/blog/google-maps-on-android.md","Google Maps on Android – Part 1: Navigation",[8219],{"type":11,"value":54350,"toc":54422},[54351,54354,54362,54375,54380,54393,54396,54399,54402,54405],[14,54352,54347],{"id":54353},"google-maps-on-android-part-1-navigation",[18,54355,54356,54357,986],{},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe ",[585,54358,54361],{"href":54359,"rel":54360},"http://developer.android.com/resources/tutorials/views/hello-mapview.html",[589],"tutorial on the android developer site",[18,54363,54364,54365,54370,54371,54374],{},"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 ",[585,54366,54369],{"href":54367,"rel":54368},"http://maps.google.com",[589],"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 ",[573,54372,54373],{},"GeoPoint"," in your Android app:",[18,54376,54377],{},[50,54378,54379],{},"GeoPoint synyxOfficeLocation = new GeoPoint(49002175, 8394160);",[18,54381,54382,54383,54385,54386,54389,54390,2304],{},"As the ",[573,54384,54373],{}," handles its values in microdegrees, the values obtained from google maps must be multiplied with 100 000. 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 ",[573,54387,54388],{},"MapView"," has a ",[573,54391,54392],{},"MapController",[18,54394,54395],{},"`MapController mapController = mapView.getController();",[18,54397,54398],{},"mapController.setCenter(synyxOfficeLocation);",[18,54400,54401],{},"mapController.setZoom(20);`",[18,54403,54404],{},"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.",[18,54406,54407,54408,7143,54411,17019,54414,54417,54418,54421],{},"The above mentioned methods change the map in a very static way. For some more visual effects try ",[573,54409,54410],{},"animateTo()",[573,54412,54413],{},"zoomIn()",[573,54415,54416],{},"zoomOut()"," to change the view of the map by showing a short animation. An also very helpful method is\n",[573,54419,54420],{},"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":48,"searchDepth":86,"depth":86,"links":54423},[],[4516,997],"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":54347,"description":54431},"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",[4526,54435,54436],"google-maps","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…","M12KhufnwFTj7q3RUZch3BzzQs8S7YaCZwbJ9q7ROfs",{"id":54440,"title":54441,"author":54442,"body":54443,"category":54641,"date":54642,"description":54643,"extension":617,"link":54453,"meta":54644,"navigation":499,"path":54645,"seo":54646,"slug":54447,"stem":54648,"tags":54649,"teaser":54652,"__hash__":54653},"blog/blog/modular-web-applications-based-on-spring.md","Modular Web-Applications based on Spring",[11420],{"type":11,"value":54444,"toc":54639},[54445,54448,54457,54460,54469,54483,54486,54508,54514,54522,54532,54566,54583,54589,54627,54637],[14,54446,54441],{"id":54447},"modular-web-applications-based-on-spring",[18,54449,54450,54451,54456],{},"Many of the Web-Applications we develop for our customers are based upon our small Framework on top\nof ",[585,54452,54455],{"href":54453,"rel":54454},"https://synyx.de/blog/modular-web-applications-based-on-spring/",[589],"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.",[18,54458,54459],{},"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.",[18,54461,54462,54463,54468],{},"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 ",[585,54464,54467],{"href":54465,"rel":54466},"http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.html",[589],"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.",[18,54470,54471,54472,54475,54476,54479,54480],{},"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",[50,54473,54474],{},"beans.xml"," files within any subfolder of",[50,54477,54478],{},"META-INF"," in the applications classpath: ",[50,54481,54482],{},"classpath*:META-INF/**/beans.xml",[18,54484,54485],{},"This is a really simple way how the modules can interact since one module can provide a Service another depends on.",[18,54487,54488,54489,54494,54495,54497,54498,54501,54502,54504,54505,54507],{},"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",[585,54490,54493],{"href":54491,"rel":54492},"http://blog.synyx.de/2010/04/21/know-your-apis-lessons-learned-from-resourcebundle/",[589],"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",[50,54496,45095],{}," by defining one bean with id",[50,54499,54500],{},"messageSource"," in your context. And that\nis exactly the problem. It is",[27,54503,19606],{},"bean. So you need a way where each module can register its own",[50,54506,45095],{},", even\nif Spring only supports one. So our framework has to handle this, because it also introduces the modular structure.",[18,54509,54510,54511,54513],{},"The “out of the box” way that would work is that the application, that assembles all these modules together defines the\n",[50,54512,45095],{}," 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?…).",[18,54515,54516,54517],{},"So what did we do? We use a Simple plugin-mechanism a colleague developed and Synyx publishes\nOpenSource:",[585,54518,54521],{"href":54519,"rel":54520},"http://hera.synyx.org",[589],"Hera",[18,54523,54524,54525,54527,54528,54531],{},"We use a bean that gets registered as",[50,54526,45095],{}," to Spring that takes care of dispatching the message-resolving\nrequests to the real ",[50,54529,54530],{},"MessageSources"," implementations spread all over the modules.",[43,54533,54535],{"className":13667,"code":54534,"language":13669,"meta":48,"style":48},"\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",[50,54536,54537,54542,54547,54552,54557,54562],{"__ignoreMap":48},[53,54538,54539],{"class":55,"line":56},[53,54540,54541],{},"\u003Cbean id=\"messageSource\">\n",[53,54543,54544],{"class":55,"line":86},[53,54545,54546],{}," \u003Cproperty name=\"sources\">\n",[53,54548,54549],{"class":55,"line":126},[53,54550,54551],{}," \u003Cplugin:list class=\"org.synyx.minos.message.ModuleMessageSource\"/>\n",[53,54553,54554],{"class":55,"line":163},[53,54555,54556],{}," \u003C/property>\n",[53,54558,54559],{"class":55,"line":186},[53,54560,54561],{}," \u003Cproperty name=\"useCodeAsDefaultMessage\" value=\"true\" />\n",[53,54563,54564],{"class":55,"line":221},[53,54565,39333],{},[18,54567,54568,54569,54572,54573,54576,54577,54579,54580,54582],{},"So this registers our",[50,54570,54571],{},"DispatchingMessageSource"," that gets injected into all beans within the context, implementing\n",[50,54574,54575],{},"ModuleMessageSource"," by Hera. This pretty much does the trick. The reason that we use",[50,54578,54575],{}," instead of\nSprings built-in",[50,54581,45095],{},"-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.",[18,54584,54585,54586,54588],{},"With some simple dispatching logic within",[50,54587,54571],{}," we found a powerful way to conquer the insufficiency\nof Spring, in conjunction with our modular system.",[43,54590,54592],{"className":13667,"code":54591,"language":13669,"meta":48,"style":48},"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",[50,54593,54594,54599,54604,54609,54614,54619,54623],{"__ignoreMap":48},[53,54595,54596],{"class":55,"line":56},[53,54597,54598],{},"List candidates = sources.getPluginsFor(getPrefixFromCode(code));\n",[53,54600,54601],{"class":55,"line":86},[53,54602,54603],{},"for (MessageSourcePlugin source : candidates) {\n",[53,54605,54606],{"class":55,"line":126},[53,54607,54608],{}," MessageFormat format = resolveMessageWithSource(source, code, locale);\n",[53,54610,54611],{"class":55,"line":163},[53,54612,54613],{}," if (null != format) {\n",[53,54615,54616],{"class":55,"line":186},[53,54617,54618],{}," return format;\n",[53,54620,54621],{"class":55,"line":221},[53,54622,860],{},[53,54624,54625],{"class":55,"line":242},[53,54626,282],{},[18,54628,54629,54630,1073,54633,54636],{},"By the way, we use this mechanism a lot when it comes to easily extending functionality of the framework-core including\n",[50,54631,54632],{},"HandlerInterceptor",[50,54634,54635],{},"PropertyEditorRegistrar"," and our Modules itself.",[607,54638,989],{},{"title":48,"searchDepth":86,"depth":86,"links":54640},[],[613],"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":54441,"description":54647},"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",[50383,54650,45137,54651,2244,1010],"framework","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":54655,"title":54656,"author":54657,"body":54658,"category":54879,"date":54880,"description":54881,"extension":617,"link":54882,"meta":54883,"navigation":499,"path":54884,"seo":54885,"slug":54662,"stem":54886,"tags":54887,"teaser":54889,"__hash__":54890},"blog/blog/know-your-apis-lessons-learned-from-resourcebundle.md","Know your APIs – Lessons learned from ResourceBundle",[11420],{"type":11,"value":54659,"toc":54877},[54660,54663,54666,54673,54676,54696,54713,54739,54745,54758,54771,54786,54795,54821,54824,54857,54860],[14,54661,54656],{"id":54662},"know-your-apis-lessons-learned-from-resourcebundle",[18,54664,54665],{},"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:",[18,54667,54668,54669,54672],{},"Message-Lookup – of course – always stands together with Locales (",[50,54670,54671],{},"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.",[18,54674,54675],{},"Within the project I am working on, there were the following message-files at that time:",[577,54677,54678,54684,54690],{},[580,54679,54680,54683],{},[50,54681,54682],{},"messages.properties"," (containing the english translation)",[580,54685,54686,54689],{},[50,54687,54688],{},"messages_de.properties"," (containing the German translation)",[580,54691,54692,54695],{},[50,54693,54694],{},"messages_it.properties"," (containing Italian translation)",[18,54697,54698,54699,54702,54703,54706,54707,54709,54710,54712],{},"Usually you provide a property-file per language containing all the translations. ",[50,54700,54701],{},"ResourceBundle"," uses a\nfallback-mechanism from the full locale down to more general ones (e.g. it first checks for ",[50,54704,54705],{},"messages_de_DE.properties","\nto ",[50,54708,54688],{}," down to ",[50,54711,54682],{}," in the end, being the overall default).",[18,54714,54715,54716,54718,54719,54722,54723,17019,54726,54729,54730,54732,54733,54735,54736,54738],{},"This actually makes much sense because in this way you can provide values for stuff relevant for all languages in\n",[50,54717,54682],{},", English language specific values in ",[50,54720,54721],{},"messages_en.properties"," and stuff that is different for\ndifferent english speaking Countries in files like ",[50,54724,54725],{},"messages_en_US.properties",[50,54727,54728],{},"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 ",[50,54731,54721],{},". Additionally, if ",[50,54734,54725],{},"\nwould exist but the key-lookup fails (the file does not provide a translation for the key), the key gets also looked up\nfrom ",[50,54737,54721],{}," (which may also provide the key).",[18,54740,54741,54742,54744],{},"In my concrete case, resolving of German values (coming from ",[50,54743,54688],{},") worked well, but if you change\nthe users locale to English, the keys also resolved to German values.",[18,54746,54747,54748,54750,54751,54754,54755,54757],{},"I did not create a ",[50,54749,54721],{}," 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",[50,54752,54753],{},"messages_fr.properties"," and then in ",[50,54756,54682],{},", 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.",[18,54759,54760,54761,54764,54765,54770],{},"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 ",[50,54762,54763],{},"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 ",[585,54766,54701],{"href":54767,"rel":54768,"title":54769},"http://api.synyx.de/j2sdk6/api/java/util/ResourceBundle.html",[589],"API-Doc of java.util.ResourceBundle",".\nHere I found the mistake that I made:",[5221,54772,54773],{},[18,54774,54775,54778,54779,54782,54783,986],{},[50,54776,54777],{},"getBundle"," uses the base name, the specified locale, and the default locale (obtained from ",[50,54780,54781],{},"Locale.getDefault",") to\ngenerate a sequence of ",[573,54784,54785],{},"candidate bundle names",[18,54787,54788,54789,54791,54792,54794],{},"I forgot that ",[50,54790,54701],{}," also looks up files for the JVMs Default-Locale before falling back to the base-file (\n",[50,54793,54682],{},"). The Default-Locale of my JVM is de_DE, which lead to the following path:",[577,54796,54797,54802,54806,54811,54816],{},[580,54798,54799,54801],{},[50,54800,54725],{}," (not found)",[580,54803,54804,54801],{},[50,54805,54721],{},[580,54807,54808,54810],{},[50,54809,54705],{}," (from Default-Locale, not found)",[580,54812,54813,54815],{},[50,54814,54688],{}," (from Default-Locale, FOUND)",[580,54817,1067,54818,54820],{},[50,54819,54682],{}," was not checked because the key was found)",[18,54822,54823],{},"So the fix was easy. There are even several ways to fix it:",[577,54825,54826,54835,54844,54851],{},[580,54827,54828,54829,54831,54832,54834],{},"rename ",[50,54830,54682],{}," to ",[50,54833,54721],{}," (which leads to the French dude having to learn German).",[580,54836,54837,54838,54831,54840,54843],{},"copy ",[50,54839,54682],{},[50,54841,54842],{},"message_en.properties"," (this is copy paste but this could be solved within the\nbuild-process using mvn)",[580,54845,54846,54847,54850],{},"set the default-locale of the JVM to an English one by calling ",[50,54848,54849],{},"Locale.setDefault(englishLocale)"," early in\napplication-boot",[580,54852,54853,54854],{},"set the default-locale as commandline-argument of your JVM (e.g.",[50,54855,54856],{},"-Duser.language=en -Duser.country=US",[18,54858,54859],{},"And, last but not least:What do we (or better I)learn from this?",[577,54861,54862,54865,54868],{},[580,54863,54864],{},"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).",[580,54866,54867],{},"Code is not the only place where bugs can be found. A big problem is also understanding the basic APIs you use.",[580,54869,54870,54871,54873,54876],{},"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.",[5298,54872],{},[27,54874,54875],{},"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":48,"searchDepth":86,"depth":86,"links":54878},[],[613],"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":54656,"description":54665},"blog/know-your-apis-lessons-learned-from-resourcebundle",[45267,45135,45137,54888],"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",[54892,54894,54896,54898,54900,54903,54905,54908,54909,54910,54912,54914,54917,54919,54921,54924,54926,54928,54931,54933,54936,54939,54941,54944,54947,54949,54952,54953,54955,54958,54960,54962,54964,54967,54970,54973,54976,54978,54979,54982,54985,54986,54988,54991,54993,54996,54998,55001,55004,55005,55007,55009,55011,55014,55016,55018,55021,55022,55025,55027,55030,55032,55035,55037,55040,55043,55046,55049,55051,55054,55056,55058,55061,55064,55066,55068,55070,55073,55075,55076,55079,55081,55083,55086,55089,55092,55095,55097,55100,55102,55105,55108,55111,55114,55117,55120,55123,55125,55128,55130,55133,55135,55138,55141,55142,55144,55147,55149,55151,55154,55157,55159,55161,55163,55166,55169,55172,55174,55177,55180,55183,55186,55188,55191,55194,55196,55198,55201,55203,55206,55207,55209,55211,55214,55216,55219,55222,55225,55227,55230,55232,55235],{"slug":1758,"name":54893},"Jennifer Abel",{"slug":27670,"name":54895},"Otto Allmendinger",{"slug":11618,"name":54897},"Ben Antony",{"slug":26052,"name":54899},"Joachim Arrasz",{"slug":54901,"name":54902},"bauer","David Bauer",{"slug":18109,"name":54904},"Janine Bechtold",{"slug":54906,"name":54907},"boersig","Jasmin Börsig",{"slug":40236,"name":41094},{"slug":8328,"name":7887},{"slug":9,"name":54911},"Julia Burgard",{"slug":5587,"name":54913},"Caspar Schwedes",{"slug":54915,"name":54916},"christina-schmitt","Christina Schmitt",{"slug":16566,"name":54918},"Michael Clausen",{"slug":8220,"name":54920},"Thomas Pötzsch",{"slug":54922,"name":54923},"damrath","Sebastian Damrath",{"slug":37639,"name":54925},"Markus Daniel",{"slug":54927,"name":7697},"dasch",{"slug":54929,"name":54930},"denman","Joffrey Denman",{"slug":9151,"name":54932},"Daniel Fuchs",{"slug":54934,"name":54935},"dobler","Max Dobler",{"slug":54937,"name":54938},"dobriakov","Vladimir Dobriakov",{"slug":54940,"name":54940},"dreiqbik",{"slug":54942,"name":54943},"dschaefer","Denise Schäfer",{"slug":54945,"name":54946},"dschneider","Dominik Schneider",{"slug":54948,"name":7648},"duerlich",{"slug":54950,"name":54951},"dutkowski","Bernd Dutkowski",{"slug":13202,"name":13202},{"slug":633,"name":54954},"Tim Essig",{"slug":54956,"name":54957},"ferstl","Maximilian Ferstl",{"slug":5192,"name":54959},"Prisca Fey",{"slug":4957,"name":54961},"Leonard Frank",{"slug":9507,"name":54963},"Arnold Franke",{"slug":54965,"name":54966},"frischer","Nicolette Rudmann",{"slug":54968,"name":54969},"fuchs","Petra Fuchs",{"slug":54971,"name":54972},"gari","Sarah Gari",{"slug":54974,"name":54975},"gast","Gast",{"slug":31948,"name":54977},"Johannes Graf",{"slug":3633,"name":7976},{"slug":54980,"name":54981},"guthardt","Sabrina Guthardt",{"slug":54983,"name":54984},"haeussler","Johannes Häussler",{"slug":12156,"name":12039},{"slug":4417,"name":54987},"Julian Heetel",{"slug":54989,"name":54990},"heft","Florian Heft",{"slug":8219,"name":54992},"Sebastian Heib",{"slug":54994,"name":54995},"heisler","Ida Heisler",{"slug":5386,"name":54997},"Patrick Helm",{"slug":54999,"name":55000},"herbold","Michael Herbold",{"slug":55002,"name":55003},"hofmann","Peter Hofmann",{"slug":41317,"name":23928},{"slug":4416,"name":55006},"Alina Jaud",{"slug":8641,"name":55008},"Robin De Silva Jayasinghe",{"slug":32705,"name":55010},"Jonathan Buch",{"slug":55012,"name":55013},"junghanss","Gitta Junghanß",{"slug":55015,"name":7780},"kadyietska",{"slug":11420,"name":55017},"Marc Kannegiesser",{"slug":55019,"name":55020},"karoly","Robert Károly",{"slug":11619,"name":19144},{"slug":55023,"name":55024},"kaufmann","Florian Kaufmann",{"slug":20487,"name":55026},"Mike Kesler",{"slug":55028,"name":55029},"kirchgaessner","Bettina Kirchgäßner",{"slug":17865,"name":55031},"Yannic Klem",{"slug":55033,"name":55034},"klenk","Timo Klenk",{"slug":12861,"name":55036},"Tobias Knell",{"slug":55038,"name":55039},"knoll","Anna-Lena Knoll",{"slug":55041,"name":55042},"knorre","Matthias Knorre",{"slug":55044,"name":55045},"koenig","Melanie König",{"slug":55047,"name":55048},"kraft","Thomas Kraft",{"slug":44479,"name":55050},"Florian Krupicka",{"slug":55052,"name":55053},"kuehn","Christian Kühn",{"slug":8782,"name":55055},"Christian Lange",{"slug":10155,"name":55057},"Luca Arrasz",{"slug":55059,"name":55060},"leist","Sascha Leist",{"slug":55062,"name":55063},"lihs","Michael Lihs",{"slug":46383,"name":55065},"David Linsin",{"slug":4140,"name":55067},"Christian Maniyar",{"slug":4675,"name":55069},"Björnie",{"slug":55071,"name":55072},"martin-koch","Martin Koch",{"slug":37775,"name":55074},"Tobias Matt",{"slug":8052,"name":19147},{"slug":55077,"name":55078},"menz","Alexander Menz",{"slug":8053,"name":55080},"Frederick Meseck",{"slug":12581,"name":55082},"Oliver Messner",{"slug":55084,"name":55085},"michael-ploed","Michael Plöd",{"slug":55087,"name":55088},"mies","Marius Mies",{"slug":55090,"name":55091},"mihai","Alina Mihai",{"slug":55093,"name":55094},"moeller","Jörg Möller",{"slug":36583,"name":55096},"Rebecca Mohr",{"slug":55098,"name":55099},"moretti","David Moretti",{"slug":20220,"name":55101},"Sven Müller",{"slug":55103,"name":55104},"muessig","Alexander Müssig",{"slug":55106,"name":55107},"neupokoev","Grigory Neupokoev",{"slug":55109,"name":55110},"nussbaecher","Carmen Nussbächer",{"slug":55112,"name":55113},"ochs","Pascal Ochs",{"slug":55115,"name":55116},"oelhoff","Jan Oelhoff",{"slug":55118,"name":55119},"oengel","Yasin Öngel",{"slug":55121,"name":55122},"oezsoy","Enis Özsoy",{"slug":12327,"name":55124},"Maya Posch",{"slug":55126,"name":55127},"ralfmueller","Ralf Müller",{"slug":55129,"name":55129},"redakteur",{"slug":55131,"name":55132},"reich","Michael Reich",{"slug":5752,"name":55134},"Karl-Ludwig Reinhard",{"slug":55136,"name":55137},"rmueller","Rebecca Müller",{"slug":55139,"name":55140},"rosum","Jan Rosum",{"slug":13114,"name":13114},{"slug":33954,"name":55143},"Sascha Rüssel",{"slug":55145,"name":55146},"sauter","Moritz Sauter",{"slug":3397,"name":55148},"Julian Schäfer",{"slug":55150,"name":7933},"scherer",{"slug":55152,"name":55153},"schlicht","Anne Schlicht",{"slug":55155,"name":55156},"schmidt","Jürgen Schmidt",{"slug":11620,"name":55158},"Tobias Schneider",{"slug":5191,"name":55160},"Benjamin Seber",{"slug":9693,"name":55162},"Marc Sommer",{"slug":55164,"name":55165},"speaker-fels","Jakob Fels",{"slug":55167,"name":55168},"speaker-gierke","Oliver Gierke",{"slug":55170,"name":55171},"speaker-krupa","Malte Krupa",{"slug":55173,"name":19265},"speaker-mader",{"slug":55175,"name":55176},"speaker-meusel","Tim Meusel",{"slug":55178,"name":55179},"speaker-milke","Oliver Milke",{"slug":55181,"name":55182},"speaker-paluch","Mark Paluch",{"slug":55184,"name":55185},"speaker-schad","Jörg Schad",{"slug":40407,"name":55187},"Jochen Schalanda",{"slug":55189,"name":55190},"speaker-schauder","Jens Schauder",{"slug":55192,"name":55193},"speaker-unterstein","Johannes Unterstein",{"slug":55195,"name":3410},"speaker-wolff",{"slug":55197,"name":23962},"speaker-zoerner",{"slug":55199,"name":55200},"stefan-belger","Stefan Belger",{"slug":38240,"name":55202},"Roland Steinegger",{"slug":55204,"name":55205},"stern","sternchen synyx",{"slug":5743,"name":5743},{"slug":19545,"name":55208},"Mateusz Szulc",{"slug":17078,"name":55210},"Tamara Tunczinger",{"slug":55212,"name":55213},"theuer","Tobias Theuer",{"slug":12582,"name":55215},"Sandra Thieme",{"slug":55217,"name":55218},"thies-clasen","Marudor",{"slug":55220,"name":55221},"toernstroem","Olle Törnström",{"slug":55223,"name":55224},"ullinger","Max Ullinger",{"slug":12081,"name":55226},"Stephan Ulrich",{"slug":55228,"name":55229},"wagner","Stefan Wagner",{"slug":18413,"name":55231},"Andreas Weigel",{"slug":55233,"name":55234},"werner","Fabian Werner",{"slug":55236,"name":55237},"wolke","Sören Wolke",["Reactive",55239],{"$scookieConsent":55240,"$ssite-config":55242},{"functional":55241,"analytics":55241},false,{"_priority":55243,"env":55247,"name":55248,"url":55249},{"name":55244,"env":55245,"url":55246},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",55252],{"category-c":-1,"authors":-1},"/blog/tags/c"]