[{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/tags/Agile/","section":"Tags","summary":"","title":"Agile","type":"tags"},{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/tags/AI/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/blog/","section":"Blog","summary":"","title":"Blog","type":"blog"},{"content":"This website ran for years on a self-patched fork of JBake. The fork was convenient as long as I held it in my head, but every layout change meant: Freemarker templates, an old CSS framework with jQuery, and outdated Java code. Now the site runs on Hugo with the well-maintained theme Blowfish, two languages under /de/ and /en/, and all old URLs redirect.\nThis article documents how I approached the migration. The focus is on Werkbaum, my tool for project planning with work breakdown structures, and on why this kind of planning, in my experience, suits working with agentic AI particularly well. The implementation itself was done by an AI agent, in my case mostly Claude Code with Fable 5.1, plus minor follow-up corrections to my static pages with OpenCode and Muse Spark 1.3.\nFor this migration, I chose a simplified approach. How I generally work with agentic AI in software development is the topic of a separate, later article.\nStarting Point # The initial inventory revealed more legacy burden than I had expected:\n73 articles, 57 of them HTML from an even older WordPress import and 16 in AsciiDoc, plus 7 pages; 44 articles in German, 29 in English, with no translation pairs. 17 admonitions, 6 include:: directives with tag regions from source files, 4 callouts, two articles with MP3 recordings. 23 internal links to files from the WordPress era that were never in the repository, only on the webspace. An .htaccess with 121 redirects of old WordPress paths that had to keep working, and tag URLs with spaces, umlauts, and mixed case. The goals were correspondingly sober: A maintained theme, only visually polished with custom CSS, instead of custom templates, real multilingualism instead of mixed lists, no external requests, every old URL with a 301 redirect, and build plus deployment via Werkator, my own CI/CD server, which also runs without Docker support in a Hostsharing managed webspace.\nThe Plan as a Werkbaum # In a chat with Claude Code, the requirements were evaluated: which static site generator to use at all, and which templates would be candidates. The first commit of the migration was therefore not code, but the plan: doc/Hugo-Migration.werkbaum, a text file in Werkbaum notation. Abridged, it looked like this at the start:\n[ ] #relaunch: Website relaunch michael.hoennig.de with Hugo (XL) - [ ] #inventar: Inventory (S) - [ ] #content-inv: Content inventory: articles, language, special constructs (XS) - [ ] #urls: Document old URL structure (XS) - [ ] #spike: Hugo spike (S) :#inventar - [!] #math-check: Check formula rendering (XS) :#content-inv - [ ] #admon-check: Try admonition rendering (XS) - [ ] #migration: Content migration (L) :#spike - [ ] #conv-script: Conversion script (XS) - [ ] #conv-review: Visual review per article (S) :#conv-script - [ ] #theme: Layout and theme (L) :#spike - [ ] #ci: Build and deployment with Werkator (M) - [ ] #cutover: Go-live (M) :#migration,#theme,#ci - [ ] #qa: Acceptance: old vs. new comparison, redirects (S) - [ ] #switch: Switch webroot (XS) :#qa --- #math-check High risk, since it is unclear whether old articles contain formulas. The notation is deliberately plain, so it stays easy to edit as plaintext and readable as a diff:\nIndentation maps the decomposition. - means a mandatory sub-package (all of them), | an alternative (one of them), + an optional extra. The status box says where a node stands: [?] idea, [ ] planned, [~] in progress, [/] breakthrough, [x] done, [^] in production, [-] discarded, [!] high risk. (S) is a T-shirt size, #math-check an ID, :#content-inv a dependency, %% a comment. Below the --- separator are notes per ID: findings, decisions, discards. The editor renders a live diagram with status colors from this. For steering the work, however, the text file matters, not the image. Still, the rendered diagram is a good way to watch the AI agent\u0026rsquo;s progress.\nHow the Plan Steered the Work # The agent did not just read the Werkbaum, it maintained it continuously. The rules were simple:\nTake the next node whose dependencies are [x]. Set it to [~]. Every change is a commit, with the ID at the front of the commit message. Before every commit, the Hugo build must be error-free. Findings and decisions go into the node\u0026rsquo;s note, including the discarded alternatives and the reasons. Decisions that are mine as product owner are presented to me as two to four options with effort, risk, impact, and reversibility. The recommendation comes first. As a result, the Git log reads like the plan:\n105a531 #inventar: Inventory completed d852bac #conv-script: Conversion script and content import 1fd047b #conv-review: Visual review of all articles and pages b94fb47 #theme-spike: PaperMod, Congo, and Blowfish as candidates 6a96673 #theme-base: Blowfish as default environment 5324e13 #redirect-map: Generated .htaccess with 325 redirects 0b7e95b #werkator-build: Werkator configuration, link check, deploy script f6e9fdc #qa: Acceptance on the preview domain passed Three points show what the plan accomplished.\nRisks First, and Sometimes a Risk Dissolves Into Thin Air # Two nodes were [!] from the start: formula rendering and formula migration. The inventory showed: not a single article contains formulas. So both nodes could be struck through with [-].\nLean Pathfinding in Miniature # Structure and evaluate the features to be reached, then determine which path reaches the next milestone fastest. Once one milestone is reached, determine the next reachable milestone.\nAlternatives as a Spike, Decision by the Human # For the theme, there was a spike instead of a discussion: PaperMod, Congo, and Blowfish with the complete content. The choice fell on Blowfish; reason and discarded candidates are in the note.\nThe Plan May Change, but Visibly # The tree at the end is not the tree from the start. The redirect map became its own work package, dark mode moved to \u0026ldquo;after cutover\u0026rdquo;. Every change is a diff to a text file and thus part of the Git history.\nWhy This Suits Agentic AI # I use Werkbaum without AI too, e.g. as a discussion basis for project planning with colleagues. But in work with an AI agent, the Werkbaum proved a very effective tool. The reasons are:\nThe plan is text in the repository. The agent reads and writes it with the same means as the code. It needs no API to a ticket system, no integration, no permissions. And I see every change to the plan in the same diff as the change to the code.\nDecomposition, dependencies, and status are explicit. An agent need not guess what is next. It sees which nodes are free, which are blocked, which carry [!] and therefore must be clarified first. T-shirt sizes help with scoping: An XS node is a unit of work that an agent can cleanly complete in one go.\nDiscards stay put. That matters more for agents than for humans. An agent without memory happily proposes again what was discarded two hours ago. Everything stays in the plan with [-] and a reason.\nThe plan is my review surface. I did not have to read every commit. The notes with finding and decision are exactly the documentation nobody else writes.\nDecision points for the human are nodes. #theme-choice, #switch: Where the agent should not decide, the plan says so. That keeps the agent\u0026rsquo;s autonomy large without anything slipping past me.\nThe result, read from the Git history: From the first commit with the plan to the accepted preview on the test domain took one working day. Most of my own time went into decisions and visual reviews, not implementation.\nWhat Does Not Belong in the Plan # After acceptance came content corrections: Profile, homepage text, a language switcher as a click-through button instead of a dropdown, the audio players lost in the theme switch. These commits carry no Werkbaum ID. That is intentional, since the Werkbaum plans structure and key features, not every detail.\nI also saw one limit: The notes grew to almost 500 lines over the course of the day. The tree itself stayed clear, but whoever looks for a decision\u0026rsquo;s history must read. The rendered diagram, however, shows each node\u0026rsquo;s explanations directly.\nStatus and Outlook # The migration is done, and once this article is online, the go-live was also completed through my approval on the test stage.\nWerkbaum itself can be tried at werkbaum.javagil.de, the source code is available on GitEA under the MIT license.\nAn article about my general way of working in software development with agentic AI in somewhat larger projects follows shortly.\nThis article was translated from German with AI assistance.\n Software-Development Agile AI Hugo","date":"5 September 2026","externalUrl":null,"permalink":"/en/blog/2026/2026-09-05-website-migration-planned-with-werkbaum/","section":"Blog","summary":"This website ran for years on a very old JBake fork and has now moved to Hugo: 73 articles in two languages, old URLs, audio, admonitions. Implemented with an AI agent, steered through a Werkbaum. This article describes the process and why Werkbaum proved itself as a planning tool for agentic AI in particular.","title":"From JBake to Hugo: a Website Migration Planned with Werkbaum","type":"blog"},{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/tags/Hugo/","section":"Tags","summary":"","title":"Hugo","type":"tags"},{"content":" Software development and consulting # Until autumn 2027 I am only available for consulting engagements with a small number of hours – remote, in German and English.\nMy stack: Java / Kotlin / Spring Boot, PostgreSQL / pl/pgSQL, TypeScript / Angular. Current focus: ReBAC (relationship-based access control) and the use of agentic AI in software development – \u0026ldquo;vibe engineering\u0026rdquo;, i.e. AI-assisted software development with engineering discipline, as opposed to \u0026ldquo;vibe coding\u0026rdquo;.\nHow I work: It starts with a conversation to capture the domain, written down as BDD with Gherkin. Continuously gather fast feedback and incorporate it. During development XP / Kanban. Suitable tooling is built alongside to increase efficiency. Generated documentation is more correct and more current than hand-maintained documentation.\nGet in touch Profile ","date":"5 September 2026","externalUrl":null,"permalink":"/en/","section":"Michael Hönnig","summary":"Software development and consulting # Until autumn 2027 I am only available for consulting engagements with a small number of hours – remote, in German and English.\nMy stack: Java / Kotlin / Spring Boot, PostgreSQL / pl/pgSQL, TypeScript / Angular. Current focus: ReBAC (relationship-based access control) and the use of agentic AI in software development – “vibe engineering”, i.e. AI-assisted software development with engineering discipline, as opposed to “vibe coding”.\n","title":"Michael Hönnig","type":"page"},{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/tags/Software-Development/","section":"Tags","summary":"","title":"Software-Development","type":"tags"},{"content":"","date":"5 September 2026","externalUrl":null,"permalink":"/en/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"A few current examples I developed with vibe engineering, i.e. AI-assisted with engineering discipline:\nWerkator: lightweight, declarative build system (CI/CD); builds this website, among others. Runs on Hostsharing container servers (with rootless Docker) and in Hostsharing Managed Webspace. Running instance: werkator.javagil.de Werkbaum: textual, Markdown-like notation for work breakdown structures with and/or decomposition, plus a live editor that renders it as a diagram. Its central feature is lean pathfinding: deriving the cheapest implementation path from the decomposition. Source on GitHub Werkdock: docker-like sandbox CLI over bwrap, filesystem isolation only; runs without root in Hostsharing Managed Webspace, not necessarily in every other managed webspace Vibe Coding vs. Vibe Engineering: interactive simulation contrasting unguided vibe coding with guided vibe engineering (German, with English toggle) ","date":"5 September 2026","externalUrl":null,"permalink":"/en/own-projects/","section":"Michael Hönnig","summary":"A few current examples developed with vibe engineering: Werkator, Werkbaum, Werkdock and the vibe engineering simulation.","title":"Own projects","type":"page"},{"content":"","date":"1 September 2026","externalUrl":null,"permalink":"/en/tags/CSS/","section":"Tags","summary":"","title":"CSS","type":"tags"},{"content":"","date":"1 September 2026","externalUrl":null,"permalink":"/en/tags/SQL/","section":"Tags","summary":"","title":"SQL","type":"tags"},{"content":"Recently, an article went around on X that essentially claimed CSS is a database. The article itself struck me as AI-generated slop, and of course CSS is not a database.\nBut the thought stuck with me anyway.\nAfter all, SQL and CSS really do share something essential: both are declarative languages. So we mainly describe what we want, and leave it to a system to decide how to arrive at that result.\nAnd yet I know many backend developers who write complex SQL queries without any trouble, yet despair over CSS at the mere question of why an element is five pixels too wide.\nMyself included.\nWhy is that?\nSQL and CSS Are Surprisingly Similar at First # Take a somewhat more interesting SQL query — the most expensive product of each active customer\u0026rsquo;s latest order:\nSELECT DISTINCT ON (\u0026#34;c\u0026#34;.\u0026#34;id\u0026#34;) \u0026#34;c\u0026#34;.\u0026#34;name\u0026#34;, \u0026#34;p\u0026#34;.\u0026#34;name\u0026#34; AS \u0026#34;product_name\u0026#34;, \u0026#34;oi\u0026#34;.\u0026#34;quantity\u0026#34;, \u0026#34;p\u0026#34;.\u0026#34;price\u0026#34; FROM \u0026#34;customer\u0026#34; AS \u0026#34;c\u0026#34; JOIN \u0026#34;order\u0026#34; AS \u0026#34;o\u0026#34; ON \u0026#34;o\u0026#34;.\u0026#34;customer_id\u0026#34; = \u0026#34;c\u0026#34;.\u0026#34;id\u0026#34; JOIN \u0026#34;order_item\u0026#34; AS \u0026#34;oi\u0026#34; ON \u0026#34;oi\u0026#34;.\u0026#34;order_id\u0026#34; = \u0026#34;o\u0026#34;.\u0026#34;id\u0026#34; JOIN \u0026#34;product\u0026#34; AS \u0026#34;p\u0026#34; ON \u0026#34;p\u0026#34;.\u0026#34;id\u0026#34; = \u0026#34;oi\u0026#34;.\u0026#34;product_id\u0026#34; WHERE \u0026#34;c\u0026#34;.\u0026#34;status\u0026#34; = \u0026#39;ACTIVE\u0026#39; AND \u0026#34;o\u0026#34;.\u0026#34;order_date\u0026#34; = (SELECT MAX(\u0026#34;o2\u0026#34;.\u0026#34;order_date\u0026#34;) FROM \u0026#34;order\u0026#34; AS \u0026#34;o2\u0026#34; WHERE \u0026#34;o2\u0026#34;.\u0026#34;customer_id\u0026#34; = \u0026#34;c\u0026#34;.\u0026#34;id\u0026#34;) ORDER BY \u0026#34;c\u0026#34;.\u0026#34;id\u0026#34;, \u0026#34;p\u0026#34;.\u0026#34;price\u0026#34; DESC; Whether the query is performant or not is not the topic here. My point is, I do not describe how the database is supposed to walk through its data structures. I only say which data I want.\nThe database itself decides whether to use an index, for example, to run a sequential scan, or which join order makes sense.\nCSS looks similar at first:\n.orders:has(.order[data-status=\u0026#34;pending\u0026#34;]) { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr)); gap: var(--space-m, 1rem); container-type: inline-size; } .orders \u0026gt; .customer.active .order:last-child .most-expensive { font-weight: bold; font-size: calc(1rem + 0.25cqi); } @container (min-width: 48rem) { .orders \u0026gt; .customer.active .order:last-child .most-expensive { grid-column: span 2; } } Here, too, I do not describe how the browser is supposed to walk through the DOM tree. Instead, I formulate a rule:\nFor all elements this selector matches, this property shall apply.\nYou can even draw some rough analogies:\nSQL CSS Table / relation DOM WHERE Selector Join condition Relationships between elements in selector Result set Set of matching elements Query planner Style and layout engine Declarative query Declarative style rule Of course, this analogy is limited. CSS is not a relational query system, and SQL is not a layout language.\nBut both demand a similar shift from developers compared to imperative programming languages:\nDescribe the desired result, not the algorithm that leads there.\nSo why does SQL feel so much more controllable to many backend developers?\nSQL Is Usually Surprisingly Local # With a normal SQL query, I can reason about what happens in a fairly local way.\nWhen I replace\nWHERE \u0026#34;status\u0026#34; = \u0026#39;ACTIVE\u0026#39; with\nWHERE \u0026#34;status\u0026#34; IN (\u0026#39;ACTIVE\u0026#39;, \u0026#39;PENDING\u0026#39;) the effect is pretty obvious.\nOf course, the query can get complicated. Ten tables may be involved, common table expressions, window functions, subqueries, and aggregations. But the logic that determines the result set is largely right in front of me.\nCSS works differently. Take a seemingly harmless property like:\ndisplay: flex; That does not just change a visible property of an element. It simultaneously changes the layout context of its children.\nOr:\nposition: relative; The element may look exactly the same as before. At the same time, however, it can become the reference point for a completely different, absolutely positioned element.\nSimilarly, overflow, transform, contain, or certain layout properties can affect how other elements are computed or rendered.\nCSS semantics, unlike those of SQL, are unfortunately often anything but local.\nWith CSS, the Real Problem Only Starts After the Selector # Perhaps the comparison with SQL leads to a typical misunderstanding.\nWith SQL, selecting the data is normally already an essential part of the result.\nWith CSS, a selector answers only the first question:\nWhich elements does this rule apply to?\nOnly then does it really get going.\nMultiple rules can apply to one property. So the cascade has to determine which one wins. That involves, among other things, origin, cascade layers, specificity, and order.\nThen there is inheritance.\nThen relative values have to be computed.\nAnd finally, the browser has to determine the layout, where sizes and positions in turn can depend on parent elements, siblings, content, fonts, viewport, flexbox, grid, or container queries.\nVery roughly, CSS thus goes through something like:\nSelection ↓ Cascade ↓ Inheritance ↓ Computed Values ↓ Layout ↓ Rendering That is considerably more than:\nFind all elements with the class customer.\nThe selector is more like the CSS counterpart of WHERE. It is not yet the actual computation of the result.\nBut Is SQL Really That Local? # Up to this point, one might reach the simple conclusion:\nSQL is well structured and local, CSS is full of hidden dependencies.\nBut it is not quite that simple.\nBecause when I think about what I usually mean when I say that I \u0026ldquo;know SQL\u0026rdquo;, I blank out quite a large part of what can happen in real databases.\nTake a trigger.\nI execute:\nUPDATE \u0026#34;customer\u0026#34; SET \u0026#34;status\u0026#34; = \u0026#39;CANCELLED\u0026#39; WHERE \u0026#34;id\u0026#34; = 123; If I look at only this statement, it seems perfectly clear what happens.\nBut there may be a trigger on \u0026quot;customer\u0026quot;.\nIt writes an audit entry. It may update yet another table. That may fire another trigger. Or a function is called that does entirely different things. Suddenly I have exactly the problem that annoys me about CSS:\nI change something here – and something happens somewhere else that is not visible at all at this point.\nSo SQL can definitely become non-local.\nWhen SQL Starts to Feel Like CSS # Triggers are probably the most obvious example of this.\nBut not the only one.\nForeign keys with ON DELETE CASCADE can delete further records when a record is deleted. Views can hide a considerable amount of logic behind a seemingly simple object. Functions can execute further logic inside a query. Generated values, constraints, and other database mechanisms also influence the behavior of a statement, without being fully visible in the statement itself.\nAnd then there are stored procedures. That is where we often even leave the purely declarative world.\nOracle uses PL/SQL for this, for example, PostgreSQL among others PL/pgSQL. That adds variables, conditions, loops, exceptions, and explicit control flow.\nThe interesting thing about this is:\nWhen backend developers say they are good with SQL, they usually do not automatically mean that they also consider 20,000 lines of PL/SQL with stored procedures and nested trigger chains to be clear, easily understandable software.\nQuite the opposite.\nTriggers have a bad reputation on many development teams precisely because they hide behavior.\nSo perhaps backend developers are not as tolerant of non-locality as the comparison with CSS initially suggests.\nWe just no longer necessarily call the problematic parts \u0026ldquo;SQL\u0026rdquo;.\nPerhaps We Compare Nice SQL with Evil CSS # That makes the comparison somewhat unfair.\nWhen we think of SQL, we usually think of something like:\nSELECT ... FROM ... WHERE ... When we think of CSS problems, by contrast, we think of a large application with hundreds of components, browser defaults, multiple stylesheets, inheritance, cascade, flexbox, grid, responsive design, and a rule that someone marked with !important three years ago.\nSo we may be comparing:\nsmall, declarative SQL\nwith:\na complex CSS system.\nThe fairer comparison might be:\na single SQL query with a single CSS rule\nand:\na large database with views, constraints, functions, stored procedures, and triggers with a large CSS codebase.\nAnd suddenly the two worlds are not that far apart after all.\nIn both cases, a local change can be influenced by rules defined somewhere else. In both cases, you have to know a larger context to reliably predict the result. And in both cases, you eventually start using tools to find out what is actually happening. With SQL, that is EXPLAIN ANALYZE, for example.\nWith CSS, it is the browser developer tools with computed styles, layout views, and the question of which rule is actually winning.\nStill, There Is One Important Difference # Still, that does not fully resolve the difference between SQL and CSS.\nAn SQL query essentially describes an operation on data.\nFor a given database state, it has a defined meaning. The query planner may use completely different strategies, as long as they produce the same result.\nWhether PostgreSQL uses an index scan or a sequential scan should not change the functional result of my query.\nThe query planner is thus mainly a performance concern.\nWith CSS, \u0026ldquo;execution\u0026rdquo; is much more closely tied to the actual result.\nThe size of an element can depend, for example, on how much space its siblings need. Those in turn depend on their content. The content depends on the font used. And another container can in turn activate different styles via a container query.\nThe layout itself is part of the result.\nCSS thus does not simply describe a query against a DOM tree. It describes a system of rules and dependencies from which the browser has to compute a concrete visual state.\nModern layout systems like flexbox and grid therefore sometimes feel more like constraint programming.\nI say, roughly speaking:\nThis element may grow, this one may shrink, that one should be at least as large as its content, the columns should share the available space, and different rules apply above a certain container size.\nAnd the browser solves this system for me.\nSQL Has an Escape Hatch # There is another difference that I find particularly interesting.\nWhen a database\u0026rsquo;s declarative model becomes inconvenient, there is almost always a way out.\nI can write a stored procedure. I get variables. I get IF. I get loops. In the extreme case, I can therefore effectively say:\nForget the elegant relational solution. I will just program it now.\nWithin CSS, this option does not exist.\nOf course I can use JavaScript. But then I have left CSS.\nCSS itself forces me with remarkable consistency to keep expressing the problem declaratively.\nModern CSS features like custom properties, calc(), grid, container queries, or :has() make the language ever more powerful. But they do not turn it into a classic imperative programming language.\nPerhaps that is exactly one reason why CSS is sometimes so frustrating for developers with an imperative background.\nSQL eventually lets us step out of the declarative paradigm.\nCSS does not.\nCan Backend Developers Really Do SQL Better Than CSS? # That brings me back to my original question.\nWhy can so many backend developers do SQL, but supposedly not CSS?\nPerhaps the question itself is wrong.\nMost backend developers probably master a relatively benign subset of SQL:\nSELECT, JOIN, and WHERE Aggregations with GROUP BY INSERT, UPDATE, and DELETE That roughly corresponds to the part of the language where cause and effect are still fairly easy to follow locally.\nBut most developers drop out when it comes to stored procedures, triggers, CTE queries, and window functions.\nSo once a database is full of triggers, hidden side effects, and extensive PL/SQL or PL/pgSQL programs, many developers react remarkably similarly to a large legacy CSS stylesheet:\nWhere does this value come from?\nWhy is this happening?\nWho added this rule?\nWhat breaks if I change this?\nPerhaps CSS is not fundamentally harder to understand than SQL after all.\nPerhaps with CSS we just reach the point where declarative rules interact much sooner.\nAnd Which Language Is Better? # That leaves the question from the title.\nSQL or CSS — Which Is the Better Language?\nOf course, the question is nonsense. Both solve completely different problems.\nBut if I had to compare them anyway, I would give SQL an advantage in local comprehensibility. A single query can usually be viewed in fairly isolated fashion. A single CSS block, by contrast, depends on its surroundings more quickly.\nCSS, on the other hand, is impressively consistent in being declarative.\nA browser can apply the same stylesheet on a smartphone, a laptop, or a huge screen, react to content whose length nobody knew when the CSS was written, take different fonts into account, and compute a layout from all of that.\nIf you wanted to program all of that imperatively, the code would probably be considerably worse than the CSS we complain about.\nAnd perhaps that is the real insight from the comparison:\nCSS may not be difficult because it is a bad programming language.\nIt is difficult because we often try to read it like a programming language, while the browser is actually solving a system of rules and constraints.\nSQL long ago got us used to telling the computer what we want.\nWith CSS, we apparently find it harder to also trust it to figure out how to get there.\nThis article was translated from German with AI assistance.\n Software-Development SQL CSS","date":"1 September 2026","externalUrl":null,"permalink":"/en/blog/2026/2026-09-01-sql-or-css-which-is-the-better-language/","section":"Blog","summary":"SQL and CSS are both declarative languages. Why do many backend developers write complex SQL queries with ease, yet despair over CSS? A comparison of locality, hidden dependencies, and the question of how declarative both languages really are.","title":"SQL or CSS — Which Is the Better Language?","type":"blog"},{"content":" Technische Kompetenzen # JVM Technologien # Java, Kotlin\nJavaEE: EJB3, CDI, JMS, JPA\nSpringBoot, SpringFramework\nJUnit, Mockito, Arquillian\nDatenbanken # Oracle\nPostgreSQL, pl/pgSQL\nAWS DynamoDB\nSecurity / IAM # RBAC / ReBAC (Relationship-Based Access Control) AI-gestützte Entwicklung # Einsatz von Agentic AI in der Softwareentwicklung („Vibe Engineering“: AI-gestützte Softwareentwicklung mit Engineering-Disziplin, in Abgrenzung zu „Vibe Coding“)\nTooling entsteht mit den Features\nauto-generierte Dokumentation statt handgepflegter\nWeb-Technologien # TypeScript, JavaScript\nAngular, Ionic-Framework, NgRX\nJest / Jasmine / Mocha\nCypress / TestCafe\nEntwicklungswerkzeuge # IntelliJ IDEA, Eclipse IDE\nGradle, Maven, Jenkins\nGit, Gerrit, Gitlab\nSonstiges # C++98, Solidity\nLinux, Windows\nAnwendungsgebiete / Branchen # Gütertransport-Logistik\n(Container-Seefracht, Luftfracht, Schienenverkehr)\nEnergie/Photovoltaik\nMobilfunk eCommerce/POS\nInternet/Webhosting\nMethodische Kenntnisse # Scrum, Kanban, eXtreme Programming\nBDD mit Gherkin, ATDD, TDD, CI, Refactoring\nOOA/OOD/OOP, UML\nProjektmanagement, Qualitätsmanagement\nSozialkompetenzen # interkulturelle, interdisziplinäre und remote Teamarbeit\nVerantwortungsbewusstsein, Eigeninitiative, Lösungsorientierung\nPräsentationen, Schulungen\nSprachen # Deutsch (Muttersprache)\nEnglisch (fließend, ~C1)\nFranzösisch (Grundkenntnisse, ~A2)\nSpanisch (Grundkenntnisse, ~A2)\nNiederländisch (Anfänger, ~A1)\nAbschlüsse und Zertifikate # Hochschulökonom (FH) für Systementwicklung (AKAD)\nFachinformatiker Anwendungsentwicklung (IHK)\nKanban Management Professional I\nCertified Scrum Developer (CSD, ScrumAlliance)\nCertified Scrum Master (CSM, ScumAlliance)\nIntroduction to CMMI-DEV V1.2\nSun Certified Java Programmer (SCJP 5.0) / Java SE\nSair Linux and GNU Certified Professional (LCP-1)\n","date":"July 30, 2026","externalUrl":null,"permalink":"/de/skills/","section":"Michael Hönnig","summary":"Technische Kompetenzen # JVM Technologien # Java, Kotlin\nJavaEE: EJB3, CDI, JMS, JPA\nSpringBoot, SpringFramework\nJUnit, Mockito, Arquillian\nDatenbanken # Oracle\nPostgreSQL, pl/pgSQL\nAWS DynamoDB\nSecurity / IAM # RBAC / ReBAC (Relationship-Based Access Control) AI-gestützte Entwicklung # Einsatz von Agentic AI in der Softwareentwicklung („Vibe Engineering“: AI-gestützte Softwareentwicklung mit Engineering-Disziplin, in Abgrenzung zu „Vibe Coding“)\n","title":"Kompetenzprofil","type":"page"},{"content":" Overview Skills Client projects Own projects Senior software developer with a backend focus. Until autumn 2027 only available for consulting engagements with a small number of hours, remote.\nCore skills # Java, Kotlin, Spring Boot, Spring Framework, Java EE (EJB3, CDI, JMS, JPA) PostgreSQL, PL/pgSQL, Oracle TypeScript, Angular RBAC / ReBAC (relationship-based access control) Agentic AI in software development: AI-assisted development with engineering discipline (\u0026ldquo;vibe engineering\u0026rdquo;) BDD with Gherkin, ATDD, TDD, CI, refactoring; eXtreme Programming, Kanban, Scrum The full skills profile and the client project history are currently available in German only, see the other tabs.\nThis article is only available in German.\nTechnische Kompetenzen # JVM Technologien # Java, Kotlin\nJavaEE: EJB3, CDI, JMS, JPA\nSpringBoot, SpringFramework\nJUnit, Mockito, Arquillian\nDatenbanken # Oracle\nPostgreSQL, pl/pgSQL\nAWS DynamoDB\nSecurity / IAM # RBAC / ReBAC (Relationship-Based Access Control) AI-gestützte Entwicklung # Einsatz von Agentic AI in der Softwareentwicklung („Vibe Engineering“: AI-gestützte Softwareentwicklung mit Engineering-Disziplin, in Abgrenzung zu „Vibe Coding“)\nTooling entsteht mit den Features\nauto-generierte Dokumentation statt handgepflegter\nWeb-Technologien # TypeScript, JavaScript\nAngular, Ionic-Framework, NgRX\nJest / Jasmine / Mocha\nCypress / TestCafe\nEntwicklungswerkzeuge # IntelliJ IDEA, Eclipse IDE\nGradle, Maven, Jenkins\nGit, Gerrit, Gitlab\nSonstiges # C++98, Solidity\nLinux, Windows\nAnwendungsgebiete / Branchen # Gütertransport-Logistik\n(Container-Seefracht, Luftfracht, Schienenverkehr)\nEnergie/Photovoltaik\nMobilfunk eCommerce/POS\nInternet/Webhosting\nMethodische Kenntnisse # Scrum, Kanban, eXtreme Programming\nBDD mit Gherkin, ATDD, TDD, CI, Refactoring\nOOA/OOD/OOP, UML\nProjektmanagement, Qualitätsmanagement\nSozialkompetenzen # interkulturelle, interdisziplinäre und remote Teamarbeit\nVerantwortungsbewusstsein, Eigeninitiative, Lösungsorientierung\nPräsentationen, Schulungen\nSprachen # Deutsch (Muttersprache)\nEnglisch (fließend, ~C1)\nFranzösisch (Grundkenntnisse, ~A2)\nSpanisch (Grundkenntnisse, ~A2)\nNiederländisch (Anfänger, ~A1)\nAbschlüsse und Zertifikate # Hochschulökonom (FH) für Systementwicklung (AKAD)\nFachinformatiker Anwendungsentwicklung (IHK)\nKanban Management Professional I\nCertified Scrum Developer (CSD, ScrumAlliance)\nCertified Scrum Master (CSM, ScumAlliance)\nIntroduction to CMMI-DEV V1.2\nSun Certified Java Programmer (SCJP 5.0) / Java SE\nSair Linux and GNU Certified Professional (LCP-1)\nThis article is only available in German.\nseit 2026: Weiterentwicklung einer digitalen Plattform eines genossenschaftlichen Cloud-Providers # Branche: Internet-Hoster\nBesonderheiten: Keycloak/ReBAC-Integration\nTechnologien: Java 21 • Spring Boot 3 • Spring Framework 6 • JUnit • Mockito • ArchUnit • OpenAPI • WireMock • PostgreSQL 13 • PL/pgSQL • SQL • Liquibase • Git • GitEA • Gradle • IntelliJ IDEA • Werkator • Testcontainers • Docker • Keycloak • RBAC/ReBAC • Claude Code\nMethoden: Kanban • PullReqests/Code-Reviews • Agentic AI / Vibe-Engineering\n2025/2026: Entwicklung von Modulen im Bereich Payment-Reporting # Branche: Mobilität\nTechnologien: Kotlin • Kotlin Coroutines • WebFlux • Spring Boot 3 • Spring Framework 6 • JUnit 5 • Strikt • MockK • PostgreSQL • Flyway • Git • GitHub • GitHub Copilot • Gradle • IntelliJ IDEA • Embedded PostgreSQL (zonky.io) • Kubernetes • AWS • Microservices • OpenAPI (interne APIs) • Smithy (externe APIs)\nMethoden: API-First • PullReqests/Code-Reviews\n2024/2025: Entwurf und Entwicklung einer digitalen Plattform eines genossenschaftlichen Cloud-Providers # Branche: Internet-Hoster\nBesonderheiten: Legacy-Datenmigration\nTechnologien: Java 21 • Spring Boot 3 • Spring Framework 6 • JUnit • Mockito • ArchUnit • OpenAPI • WireMock • PostgreSQL 13 • PL/pgSQL • SQL • Liquibase • Git • GitEA • Gradle • IntelliJ IDEA • Jenkins • Testcontainers • Docker • RBAC/ReBAC\nMethoden: Kanban • PullReqests/Code-Reviews\n2022/2023: Weiterentwicklung und Betrieb eines B2B Seefracht-Informationssystem # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 8-17 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito\nPITest • OpenAPI • Angular • TypeScript • Git • Testcontainers • Docker • Jenkins • Gradle • WireMock • Rabbit MQ • IntelliJ IDEA • JIRA • Sonar • Kibana • Grafana • OpenShift • Oracle 12 SQL • H2 • Liquibase Methoden: Extreme-Programming (XP) • Mob-Programming • Pair-Programming • Kanban • CI/CD\n2022: Entwurf und Entwicklung einer dynamischen und hierarchischen Datenbank-integrierten Zugriffsrechteverwaltung (RBAC/ReBAC) # Branche: Internet-Hoster\nTechnologien: Java 17 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito • PITest • OpenAPI • ArchUnit • PostgreSQL 13 • pl/pgSQL • SQL • Liquibase Git • GitEA • Gradle • IntelliJ IDEA • Testcontainers • Docker\nMethoden: Kanban • PullReqests/Code-Reviews\n2021/2022: Entwicklung und Betrieb eines B2B eCommerce-System mit Hybrid-Mobile-App im intermodalen Speditionswesen # Branche: Logistik / intermodal\nTechnologien: Java 8 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito • PITest • Angular • TypeScript • Ionic • Oracle 12 SQL • H2 • Liquibase • Kibana • Grafana • Git • Jenkins • Gradle • RabbitMQ • IntelliJ IDEA • OpenAPI • JIRA\nMethoden: Extreme-Programming (XP) • Mob-Programming • Pair-Programming • Kanban • CI/CD\n2019/2020: Neuentwicklung eines B2B Kundenportals # Branche: Logistik / Schienengüterverkehr\nTechnologien: Angular 10 • TypeScript 3.x • jest • ts-mockito • Cypress • Kotlin • AWS Lambda/DynamoDB/SNS/SQS • Serverless Framework • Gitlab\nMethoden: Continuous Integration • Continuous Deployment • Scrum\n2018/2019: Weiterentwicklung eines B2B eCommerce-Systems # Branche: Logistik / Spedition / Luftfacht\nTechnologien: Java 8 • Spring Boot 1.5.x bis 2.1.x • Spring Framework 4.3.x bis 5.1.x • WebServices • Thymeleaf • GWT 2.7 • JUnit • Mockito • WireMock • TDD • BDD • DDD • JBehave • Selenium • Serenity • Selenide • PiTest • JavaScript • Vue.js • Oracle 11 SQL • Mob-Programming • Pair-Programming • git • Gradle • IntelliJ IDEA • Jenkins\nMethoden: Continuous Integration • Continuous Deployment • Kanban\n2017/2018: Microlearning Hybrid-App # Branche: eLearning (eigenes Projekt zum Zwecke der Weiterbildung)\nTechnologien: Angular 5.x • NgRX • IonicFramework 3.x • Ionic Native 4.x • Cordova 7.1 • TypeScript 2.4 • Event-Sourcing • npm • webpack • IntelliJ IDEA • git\n2017: Lead Developer / Framework-Technologien # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • Glassfish3.1 • JBoss AS EAP 7 • JUnit • Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • SonarQube • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Jira\n2011-2016: Entwicklung und Wartung eines Application-Frameworks # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 6+7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • OC4J • Glassfish 3.1 • JBoss AS EAP 6.4 • TIBCO • JUnit, Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Dynatrace\nseit 2014: Operatives Managementsystem # Branche: Maschinenbau\nStichworte: Java 7 • Swing • Nuclos OpenSource ERP (API) • Tomcat 7 • JUnit • Mockito • Git • Microsoft SQL Server 2012 • Eclipse IDE • Trello\n2011-2016: Entwicklung und Wartung eines Application-Frameworks # Branche: Logistik / Container-Seeschifffahrt\nStichworte: Java 6+7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • OC4J • Glassfish 3.1 • JBoss AS EAP 6.4 • TIBCO • JUnit, Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Dynatrace\n2011: Operatives Managementsystem für Containerlogistik # Branche: Logistik / Container-Seeschifffahrt\nStichworte: Java 6 • JavaEE (EJB3, JPA/EclipseLink, JMS, JSF/IceFaces) • OC4J • TIBCO • JUnit • ClearCase • Jenkins • Oracle 11 SQL • Maven • Eclipse IDE • ClearQuest\n2011: Kundenspezifische Anpassung eines online Shops # Meine Aufgaben: Programmierung • Test\nStichworte: Struts 1.2 • Spring 2.5 • JUnit • Hibernate • mySQL • Tomcat 6 • Eclipse IDE • Git • ant\n2010-2011: Funktions-/Leistungs- und Ertragsmonitoring von Solarkraftwerken # Branche: Energie/Photovoltaik\nStichworte: JEE mit JSF • Facelets • RichFaces • jQuery • Spring • Jersey REST • JUnit Hibernate • iBatis • PostgreSQL • Tomcat 6 • Eclipse IDE • SVN • Maven2 • FIT/Fitnesse • TDD\n2009-2010: GUI für Realtime-Sensorensystem # Branche: Meeresbodenforschung\nStichworte: Java 6 • Swing • Java 3D • Java 2D • JUnit • Jemmy • TCP/IP • Eclipse IDE • Netbeans Matisse • SVN • Maven2 • (ecl)EMMA • TDD • Kanban • C • Linux Kernel Driver (Parallel Port)\n2008-2009: Web-Basiertes POS System # Branche: Mobilfunk-Vertrieb\nStichworte: JEE mit JSF • Facelets • EJB3-Client • RichFaces • WebServices • JPA/Hibernate • Oracle 10g • Spring Web Flow • Spring • JUnit • Tomcat 6 • Eclipse IDE • SVN • Maven2 • EMMA • Hudson\n2007-2008, 2009: Web-basiertes, verteiltes Administrationswerkzeug # Branche: Internet / Webhosting\nStichworte: JEE mit JSF/Facelets • EJB3 Client+Server • JMS • JPA/PostgreSQL • JBoss AS mit Tomcat • SVN • Ant • Eclipse IDE • Cobertura • TDD\n1986 bis 2007: diverse Projekte und Festanstellung # Bezahlsystem: Java • JavaEE / EJB • Swing • JDBC/PostgreSQL\ndiverse Kleinprojekte: PHP • MySQL • PostgreSQL\nStarOffice/OpenOffice.org: C++, Java\nBranchensoftware-Systeme für Immobilienverwaltungen, Immobilienmakler, Ärzte und Reisebüros: TurboPascal • C • DBase • Clipper\nISAM-Server / Serieller Multiplexer: TurboPascal, C, x86 Assembler\nFür konkrete Projektangebote erhalten Sie mein vollständiges Profil gerne auf Anfrage.\nA few current examples I developed with vibe engineering, i.e. AI-assisted with engineering discipline:\nWerkator: lightweight, declarative build system (CI/CD); builds this website, among others. Runs on Hostsharing container servers (with rootless Docker) and in Hostsharing Managed Webspace. Running instance: werkator.javagil.de Werkbaum: textual, Markdown-like notation for work breakdown structures with and/or decomposition, plus a live editor that renders it as a diagram. Its central feature is lean pathfinding: deriving the cheapest implementation path from the decomposition. Source on GitHub Werkdock: docker-like sandbox CLI over bwrap, filesystem isolation only; runs without root in Hostsharing Managed Webspace, not necessarily in every other managed webspace Vibe Coding vs. Vibe Engineering: interactive simulation contrasting unguided vibe coding with guided vibe engineering (German, with English toggle) ","date":"30 July 2026","externalUrl":null,"permalink":"/en/profile/","section":"Michael Hönnig","summary":"Senior software developer, backend focus: Java, Kotlin, Spring Boot, PostgreSQL, ReBAC, agentic AI in software development. Short profile, full skills profile and project history.","title":"Profile","type":"page"},{"content":"","date":"November 13, 2025","externalUrl":null,"permalink":"/de/tags/ISMS/","section":"Tags","summary":"","title":"ISMS","type":"tags"},{"content":" Einleitung # Seitdem ich, vor langer Zeit, mit dem Konzept Secrets-Manager in Kontakt gekommen bin, hatte ich ehrlich gesagt immer nur eine vage Vorstellung davon, was für einen Vorteil diese eigentlich bieten, wo doch das System, welches ein Secret abfragen darf, sich dafür bereits authentifizieren muss. Wie genau autorisiert sich also z.B. ein Webserver gegenüber einem Secrets-Manager, um dort Datenbank-Zugangsdaten abzurufen, und warum ist das besser, als diesem System mit derselben Authentifikation direkt auf die Datenbank Zugriff zu gewähren? In diesem Artikel möchte ich dieses Thema etwas beleuchten.\nWozu Secrets-Manager im Rahmen der Informationssicherheit dienen # In einem Informationssicherheitskontext – egal ob unter ISO 27001, NIS2 oder anderen Standards – stehen drei Schutzziele im Mittelpunkt:\nVertraulichkeit\nIntegrität\nVerfügbarkeit\nZudem muss dieser Schutz nachweisbar sein und Verletzungen erkannt werden können.\nSecrets-Manager adressieren vor allem die ersten beiden Ziele. Statische Passwörter in Konfigurationsdateien oder Git-Repositories untergraben die Vertraulichkeit sofort, und ihre Verbreitung macht Rotation nahezu unmöglich – was wiederum die Integrität schwächt.\nEin Secrets-Manager stellt sicher, dass:\nSecrets zentralisiert statt verteilt gespeichert werden.\nSecrets verschlüsselt abgelegt werden.\nZugriff nur über klar definierte Berechtigungen erfolgt.\nAuditierbarkeit gegeben ist (wer hat wann welches Secret abgerufen?).\nSecrets rotierbar sind, ohne Deployments auszulösen.\nMaschinen nicht mit statischen Passwörtern bootstrappen müssen.\nAus Sicht der Informationssicherheit reduziert ein Secrets-Manager somit:\ndas Risiko von Leaks,\ndie Angriffsfläche durch Kopien,\nund den Missbrauch unkontrollierter Zugangsdaten.\nFür welche Arten von Accounts ein Secrets-Manager verwendet wird # Besonders wichtig ist für mich die Unterscheidung der Account-Typen:\n1. Menschliche Accounts # Für Benutzerkonten eignet sich ein Secrets-Manager nur indirekt. Hauptsächlich, um:\nAPI-Tokens abzulegen,\nSSH-Schlüssel sicher zu verwalten,\noder hochprivilegierte „Break-Glass-Zugänge“ zu schützen.\nFür Passwörter menschlicher Benutzer ist ein Passwortmanager geeigneter.\n2. Maschinenaccounts # Der eigentliche Kern ist die Maschinen-zu-Maschine-Kommunikation:\nDatenbank-Benutzerkonten\nAPI-Accounts für Microservices\nTLS-Zertifikate und private Schlüssel\nService-Tokens\nZugangsdaten für externe Systeme\ninterne Systemidentitäten für Automatisierungsprozesse\nMaschinen benötigen Secrets, um automatisch starten, skalieren oder sich verbinden zu können – und genau dafür sind Secret Stores optimiert.\nWarum ein Server überhaupt an Secrets kommt – das Bootstrapping-Problem # Einer der größten Aha-Momente war:\nEin Server kann niemals „gar nichts“ besitzen. Er braucht eine grundlegende Identität.\nDas führt zum sogenannten First-Secret-Problem: Wie weist sich der Server beim Secrets-Manager aus?\nDafür existieren verschiedene Mechanismen, die nicht auf kopierbaren Passwörtern basieren:\nhardwaregebundene Schlüssel (z. B. TPM),\nkontextsensitive Identitäten (nur gültig auf bestimmter Hardware/VM),\neinmalige, kurzlebige Tokens,\nCloud-basierte Instanzidentitäten,\nZertifikate mit starker Bindung.\nDamit wird das Grundproblem entschärft, ohne es vollständig zu eliminieren. Aber der Effekt ist entscheidend: Maschinen speichern kein dauerhaftes Passwort, sondern eine nicht klonbare Identität.\nWarum ein Administrator dennoch Secrets sehen kann – und warum das unvermeidbar ist # Ein wichtiger Punkt war die Erkenntnis:\nEin Administrator mit Root-Rechten auf einem Server kann die Secrets sehen, die dieser Server im laufenden Betrieb verwendet.\nAber:\nEr kann nicht automatisch andere Secrets abfragen.\nAlle Zugriffe sind auditierbar.\nDie Identitäten sind nicht kopierbar.\nSecrets können kontextgebunden sein (nur auf dieser Maschine gültig).\nSecrets sind rotierbar und oft kurzlebig.\nEin Secrets-Manager kann also keinen Insider vollständig ausschließen, aber er kann Missbrauch einschränken, sichtbar machen und zeitlich begrenzen.\nAdvanced-Konzepte, die ich verstanden habe # Kontextgebundene Secrets # Ein Secret ist nur dann gültig, wenn bestimmte Bedingungen erfüllt sind:\nrichtige Hardware,\nrichtige VM oder Container,\nrichtige Netzwerkzone,\nrichtige Softwareintegrität (z. B. gemessener Hash).\nDas macht ein kopiertes Secret wertlos.\nEphemere Secrets # Hier bekommen Maschinen Zugangsdaten, die nur Sekunden oder Minuten gültig sind.\nMissbrauch lohnt sich nicht, weil die Daten praktisch sofort ablaufen.\nSecrets müssen nicht mehr ausgeliefert werden # Statt Secrets zu geben, kann der Manager Operationen durchführen:\nSignaturen,\nEntschlüsselungen,\nAufbau einer Session.\nDie Maschine erhält nur das Ergebnis, nie das Secret selbst.\nTrusted Execution Environments (TEE) # Secrets laufen nur innerhalb isolierter Enklaven:\nSGX, SEV, ARM Realm, Secure Enclave,\nMicro-VMs für isolierte Kryptographie.\nSelbst Root kommt nicht an die Klartexte.\nGrenzen des Systems # Ich habe auch gelernt, dass Secrets Management keine Magie ist:\nEin kompromittierter Server kann seine Secrets im RAM preisgeben.\nEin Root-Admin kann laufende Prozesse manipulieren.\nPerfekter Schutz ist unmöglich.\nAber: Die Angriffsfläche und der mögliche Schaden werden massiv reduziert.\nSchlussgedanke # Secrets Management ist ein zentraler Baustein moderner Informationssicherheit. Nicht nur, um Passwörter zentral zu halten, sondern um das gesamte System kontrollierbarer, auditierbarer und widerstandsfähiger zu machen.\nFür mich persönlich war der Schlüssel:\nNicht verhindern, dass ein System seine eigenen Secrets sieht – sondern verhindern, dass es mehr sieht als nötig.\nUnd: alles, was passiert, muss nachvollziehbar sein.\nDieser Artikel ist Teil meines eigenen Lernprozesses. Er soll dokumentieren, wie ich die theoretische Seite des Secrets-Managements verstanden habe – ohne Anspruch auf Vollständigkeit oder Perfektion.\nWarum man die Maschinen-Identität nicht direkt als Zugangsdaten verwenden sollte # Während meiner Beschäftigung mit Secrets-Management kam mir irgendwann die Frage, warum man überhaupt separate Zugangsdaten für Dienste verwaltet. Könnte man nicht einfach die Maschinen-Identität – also den hardwaregebundenen Schlüssel oder das Host-Zertifikat – direkt verwenden, um auf Zielsysteme wie Datenbanken zuzugreifen? Immerhin muss sich der Server sowieso beim Secrets-Manager authentifizieren. Warum nicht diesen Schritt überspringen?\nWas ich gelernt habe: Das würde mehr Probleme schaffen, als es löst. Eine Maschinen-Identität gehört immer zum physischen oder virtuellen Host, nicht zur Anwendung. Datenbanken oder APIs sollen jedoch nicht erkennen, welche Maschine sie anspricht, sondern welcher Dienst oder welches Deployment dahintersteht. Wenn man dem Host selbst weitreichende Berechtigungen gibt, verlieren Microservices ihre Trennung, Container-Isolation wird ausgehebelt, und jeder Exploit eines einzelnen Prozesses hätte weitreichende Folgen. Außerdem wären solche Berechtigungen kaum rotierbar, während Secrets bewusst kurzlebig und leicht austauschbar sind. Maschinen-Identitäten sind langlebig, schwer zu erneuern und repräsentieren eine völlig andere Ebene (Hardware statt Logik).\nDer Secrets-Manager dient genau dazu, diese Ebenen zu entkoppeln: Der Host beweist lediglich, dass er eine vertrauenswürdige Umgebung ist. Die Anwendung bekommt dann ein spezifisches Secret, das ihre logische Identität darstellt und das unabhängig vom Host rotiert, entzogen oder geändert werden kann. Dadurch bleibt das System granular, auditierbar und sicher – und genau dafür ist Secrets Management überhaupt erst notwendig.\n ISMS","date":"November 13, 2025","externalUrl":null,"permalink":"/de/blog/2025/2025-11-13-why-secrets-management-and-how-does-it-work/","section":"Blog","summary":"Seitdem ich, vor langer Zeit, mit dem Konzept Secrets-Manager in Kontakt gekommen bin, hatte ich ehrlich gesagt immer nur eine vage Vorstellung davon, was für einen Vorteil diese eigentlich bieten, wo doch das System, welches ein Secret abfragen darf, sich dafür bereits authentifizieren muss. Wie genau autorisiert sich also z.B. ein Webserver gegenüber einem Secrets-Manager, um dort Datenbank-Zugangsdaten abzurufen, und …","title":"Warum Secrets-Manager wichtig sind – und was ich darüber gelernt habe","type":"blog"},{"content":"If you’re already using Lombok in a project, you’re likely leveraging its concise and boilerplate-reducing features. Among these features is Lombok’s val, a powerful tool that introduces local variable type inference into Java. Even though Java’s own var keyword (introduced in Java 10) provides similar functionality, there may still be situations in which Lombok’s val feels like an appealing option. However, before adopting val broadly, it is essential to evaluate what it is, how it works, and the risks and advantages associated with it—especially given the ongoing evolution of Java.\nWhat is Lombok’s val? # Lombok’s val is a real generic type provided by the Lombok library. You can declare local variables using val as their type, and Lombok’s annotation processor recognizes this special type during compilation. The processor then infers the actual type from the initializer expression and replaces the val type with the concrete type, while also making the variable final.\nFor instance:\nimport lombok.val; // ... val myList = new ArrayList\u0026lt;String\u0026gt;(); Note that val must be imported ( import lombok.val;) for the code to compile; using it as lombok.val whould possible, but quite ugly.\nHere, you’re declaring a variable of type val, which is valid Java syntax. Lombok’s processor will infer that myList is of type ArrayList\u0026lt;String\u0026gt;, make the variable final, and transform the code to:\nfinal ArrayList\u0026lt;String\u0026gt; myList = new ArrayList\u0026lt;\u0026gt;(); How Does Lombok’s val Work Under the Hood? # Lombok uses Java’s annotation processing mechanism to detect the use of the val type in variable declarations. Specifically, Lombok:\nScans your code for val type declarations.\nAnalyzes the initializer expression to determine its actual type.\nRewrites the code in the Abstract Syntax Tree (AST) before the compiler moves to the type checking phase, replacing val with the inferred concrete type and adding the final modifier.\nFor example, when you write:\nval x = 42; Lombok transforms this into:\nfinal int x = 42; This transformation occurs between Java’s parsing phase (which validates the syntax) and its type checking phase (which ensures type compatibility). By the time the type checker analyzes the code, Lombok has already replaced the val type with the appropriate final type. The fact that val is a real, importable type means this transformation is completely compatible with Java’s standard compilation process.\nAdvantages of Lombok’s val # Given that Lombok is already part of your project, the additional benefits of using val include:\nCleaner and More Readable Code:\nEliminates repetitive boilerplate such as explicitly specifying types that are already clear from the initializer.\nImproves focus on the logic rather than the types.\nval result = someMethod(); // Cleaner than explicitly declaring the return type Compile-Time Safety:\nUnlike dynamic languages, Lombok’s val ensures static type checking. If the initializer’s type is incompatible, the compiler will produce an error. Immutability Enforcement:\nAll val variables are automatically final, reducing accidental reassignment and promoting immutability practices. Consistency Across All Java Versions:\nYou can use val for type inference even if your project is running on a Java version prior to Java 10, where var wasn’t available. Part of the Lombok Ecosystem:\nIf your project is already relying on Lombok for features like @Data, @Builder, or @Getter, val integrates seamlessly with no additional dependencies. Disadvantages of Lombok’s val # However, there are some trade-offs to consider:\nRedundancy with var (Java 10 or Later):\nSince Java natively supports type inference with the var keyword, using Lombok’s val in modern projects may seem redundant and unnecessary. // Native Java solution (Java 10+) var myList = new ArrayList\u0026lt;String\u0026gt;(); Less Flexible Than var in Non-Final Scenarios:\nUnlike var, Lombok’s val enforces immutability by making variables final. For mutable variables, you need to avoid val. Reliance on Lombok’s Annotation Processing:\nLombok’s code manipulation creates a dependency on its annotation processor. While val is a real type and thus valid Java, any changes to how annotation processing works in Java (e.g., compiler internals) could potentially affect how Lombok detects and transforms val declarations. Future Compatibility Risks:\nLombok relies on undocumented/internal APIs of the Java compiler (like com.sun.tools.javac) and integrates deeply with the compilation process. This reliance means that changes in Java’s compiler infrastructure may render val incompatible without updates to Lombok. Learning Curve:\nDevelopers unfamiliar with Lombok may find val counterintuitive or confusing, particularly if they are already accustomed to Java’s var. Risks for Future Java Versions # 1. Annotation Processing Policy Changes # Future versions of Java could modify how annotation processing works, making Lombok’s AST manipulations more difficult or outright impossible. Discussions around stricter controls on annotation processing have already surfaced in Java’s community.\n2. Conflict with Native Features # Java already offers var for type inference natively. Introducing further advancements in type inference, or changes to type inference mechanics (e.g., with Project Valhalla), might conflict with Lombok’s val.\n3. Dependency on Java Internals # Lombok depends on internal compiler APIs like com.sun.tools.javac, which are neither stable nor guaranteed to be supported across major Java versions. Breaking changes could require significant updates to Lombok or make it incompatible until fixed.\n4. Inconsistent Support for New Java Versions # Historically, Lombok has been slow to support new JDK releases, often requiring updates to remain compatible. This lag might impose delays on projects relying on val when upgrading the Java version.\nShould You Use val? # Here’s a balanced perspective:\nWhen to Use # If you are already using Lombok widely and the Java version in your project is below Java 10, val is a natural choice for type inference.\nUse it in scenarios where variables will remain immutable (final).\nWhen to Avoid # If you are using Java 10 or above, prefer the native var keyword for type inference.\nIn mutable contexts, avoid val since it enforces final.\nPractical Recommendation # Short-Term (2-5 Years): Lombok’s val is stable and safe to use today in projects already relying on Lombok. However, weigh its usage carefully against var in Java 10+ projects.\nLong-Term (Beyond 5 Years): Consider migrating towards Java-native features like var for future-proofing. As Java evolves, Lombok’s reliance on annotation processing and compiler internals introduces increasing risk.\nConclusion # Lombok’s val remains a compelling choice for modern Java projects—provided you’re already using Lombok and understand its mechanics. It simplifies local variable declarations, promotes immutability, and remains compatible with older Java versions. However, with the rise of native features like var and ongoing changes in the Java platform, the long-term stability of val is not guaranteed. For newly written code, utilizing var is generally more sustainable over time.\nBy carefully considering your project’s Java version, team familiarity, and potential risks, you can decide whether val deserves a permanent place in your codebase!\n Java","date":"22 August 2025","externalUrl":null,"permalink":"/en/blog/2025/2025-08-22-discussing-lombok-val/","section":"Blog","summary":"This article examines Lombok’s val — how it uses annotation processing to infer local variable types and enforce finality—highlighting benefits like cleaner, more readable code, compile-time safety, immutability, pre–Java 10 support, and seamless Lombok integration, while weighing drawbacks such as redundancy with Java 10+ var, reduced flexibility due to final variables, reliance on compiler internals with potential compatibility risks, and possible lag in new JDK support; it recommends using val chiefly in Lombok-heavy, pre–Java 10, immutable contexts, but preferring native var in modern Java and planning a long-term shift toward Java-native features.","title":"Exploring Lombok’s val: Advantages and Disadvantages","type":"blog"},{"content":"","date":"22 August 2025","externalUrl":null,"permalink":"/en/tags/Java/","section":"Tags","summary":"","title":"Java","type":"tags"},{"content":"While most AI code generation examples show how to quickly bootstrap a new web application, I was curious how far an AI coding assistant can take me in an existing, non-trivial codebase – one with a Java backend, automated tests, APIs (OpenAPI), PostgreSQL database migrations (Liquibase). I decided to explore the open-source AI tool aider, applying it to one of my projects: hsadmin-NG.\nHow to Use aider AI Coding Assistant # aider is an open source command-line tool for coding with LLMs (Large Language Models) such as GPT (OpenAI), Claude (Anthropic), or Gemini (Google) and others.\naider allows you to easily analyze and edit code files by chatting with an AI in your terminal. It works across languages, and lets you iteratively build, refactor, and fix code. And it’s surprisingly effective — if you know how to guide it.\nWarning aider sends your code to the selected LLM provider. If you’re working on proprietary or sensitive code, make sure you understand what that means.\nIn my case, the project is open source, so this isn’t a blocker. For more details, see the aider privacy policy.\nInstallation # Assuming you have Python 3 and pipx installed:\npipx install aider-chat To use OpenAI’s vision models or other advanced capabilities:\npipx inject aider-chat openai --include-apps To use Google’s Gemini:\npipx inject aider-chat google-generativeai --include-apps Configuration # You need to provide your API key depending on the model:\nOpenAI\nexport OPENAI_API_KEY=\u0026#34;your-api-key-here\u0026#34; Google Gemini\nexport GEMINI_API_KEY=\u0026#34;your-api-key-here\u0026#34; These can be added to your .bashrc or .zshrc.\nBasic Workflow # Before you start, it’s suggested having a CONVENTIONS.md file in your project. This files contains rules for the aider AI coding agent, see .aider.conf.yaml.\nFind my CONVENTIONS.md for this project in my git repository..\nGo to the project root\nRun the AI agent\naider Add files you want aider to work with:\n/add path/to/file.java another/file.yaml Chat with the AI, e.g., \u0026ldquo;Add field X to entity Y and update the DB schema\u0026rdquo;\nLet aider propose changes. You can confirm or skip each diff.\nExit with /quit\nFor more commands seen aider\u0026rsquo;s usage documentation.\nExample Session: Adding a Field to an Existing Entity # My use case is adding a notes field to the partner_details table and related code (Java entity, patcher, Liquibase, OpenAPI, tests).\nI initially tried to just ask aider to figure it all out, e.g. like this:\n$ aider /ask My use case is adding a `notes` field to the `partner_details` table and related code (Java entity, patcher, Liquibase, OpenAPI, tests). Where do I need to add field `notes`? That didn’t work too well. Even with advanced models like GPT-4 or DeepSeek’s reasoning model, it missed files or proposed unnecessary ones. Of course, I tried different ways of phrasing my question - nothing worked well enough.\nSo I pre-selected all relevant files using good old grep:\naider `grep -rl registrationOffice src/main/java/ src/test/java/ src/main/resources/api-definition src/main/resources/db/` Then I told aider:\nI want to add a text field notes to the database table hs_office.partner_details and related files. Files to amend have already been added to aider AI. Please apply all required changes for Java production+test-code, add the Liquibase changeset and amend the OpenAPI-Spec.\n— aider\nAfter letting aider propose and apply all changes, I ran the tests (using my custom test alias gw-test) and found one issue: The field was added in test data, but not asserted anywhere. I asked aider:\nPlease doublecheck if you followed all conventions. Any other amendments necessary to support the new field notes in the partner details?\n— aider\nIt suggested more changes, some useful, some not so useful. Eventually, I decided to just revert some test data changes with git and re-ran the tests. All tests passed now.\nThe resulting changes can be found in this git commit.\nDeterminism and Randomness # Keep in mind that LLMs operate with a parameter called temperature which introduces randomness. So the AI might not always produce the same result for the same input.\nSometimes that’s helpful. Sometimes it’s annoying. Be prepared.\nSecurity: What Does aider Access? # I wanted to know which files aider actually opens. So I traced it with strace:\nstrace -f -t -e trace=file -o build/aider.strace aider ... In a second terminal:\ntail -f build/aider.strace | grep -oP \u0026#39;\u0026#34;\\K[^\\n\u0026#34;]+(?=\u0026#34;)\u0026#39; All accessed files made sense. Of course, there’s no guarantee — it’s still a local app running with your user permissions.\nThere is a Docker image, but it’s pretty restricted. If you need advanced features (like vision or Gemini), you might need to rebuild the image with extra support.\nSummary # aider is a promising tool for LLM-driven development. Though, it won’t yet fully understand the domain logic or large code base structures — but if you provide the right files and instructions, it can save a lot of time.\nIt acts somewhat like an entry level programmer, as it needs a senior to provide some rules and guidance.\nI’m definitely looking forward how aider and the LLMs will improve in the future. But I also think they’ll always need somebody who knows how to guide them to read the intended goal. Anyway, the software development industry is at the brim of a disruptive change.\nAny comments about this article are welcome on X.\n AI Java Agentic AI Agentic Coding","date":"11 April 2025","externalUrl":null,"permalink":"/en/blog/2025/2025-04-11-exploring-the-aider-ai-coding-assistant/","section":"Blog","summary":"While most AI code generation examples show how to quickly bootstrap a new web application, I was curious how far an AI coding assistant can take me in an existing, non-trivial codebase – one with a Java backend, automated tests, APIs (OpenAPI), PostgreSQL database migrations (Liquibase). So, I decided to explore the open-source AI tool aider, applying it to one of my Open Source projects.","title":"Exploring the aider AI Coding Assistant in a Non-Trivial Codebase","type":"blog"},{"content":"(This translation of my original German Datenschutzvereinbarung just for informational purposes.)\nGeneral # For me, Michael Hönnig, protection of your personal data has a high priority and to the best of my knowledge I obey the data protection laws of the Federal Republic of Germany (BDSG or EU-DSVGO) as well as the Telemediengesetz (TMG).\nThis privacy statement applies to the use of my business Internet services under the domains hoennig.de and javagil.de, it applies regardless of the type of access (e.g. PC, smartphone, tablet) and regardless of the type of access used (e.g. web or e-mail).\nMy digital services may contain third-party links. Where I, personally, also adhere to the principles of data protection in my activities on third-party media platforms (such as Twitter or XING), only the respective operators can be responsible for the final guarantee of data protection in their realm. Therefore, the privacy statements of respective third party providers apply.\nResponsible Person # Responsible for the processing of your personal data is:\nMichael Hönnig\nBürgermeister-Bolt-Straße 50\nD-26424 Hage\nE-Mail: datenschutz@hoennig.de\nPurpose of processing personal data # I gather, process and store personal data only for\nthe settlement of contractual relationships,\nproject acquisition,\nthe technical exchange of knowledge,\ntechnical reasons\nand to fulfill legal obligations.\nData transfer to third parties # Your personal data will only be passed on to third parties if this is necessary for the purpose of contract execution, billing or technical implementation (for example, to my Internet server service provider), you have previously explicitly consented or there is a legal basis for disclosure.\nDuration of storage # Generally, I delete personal data in a timely manner, after this date is not relevant anymore for any of the above purposes. Legal retention periods are up to ten years after the end of the calendar year in which the data was gathered.\nSince e-mails sent to me can also contain legally-relevant content, I archive incoming and outgoing e-mails for up to 11 years.\nI also keep personal data for the periods in which claims could be issued against me (legal period of three or up to thirty years).\nBackups on my Internet server provider will also be kept for two weeks, thus the duration of data storage can be extended by this period.\nCookies # Some of my web services may use a technology called \u0026ldquo;cookies\u0026rdquo; to recognize repeated visitors on the respective website to save settings and to adapt the website to personal preferences as well as to be able to keep a single visiting session when changing pages on the website.\nCookies are small text files that are stored on the website visitor’s computer and contain data about the respective user, e.g. login credentials. I may use both session cookies, which are only stored temporarily during a session on the visitor’s computer, at most until the respective window in the web browser is closed, and persistent cookies, which you can delete at any time in your web browser.\nThe use of cookies on my websites is announced to you on the respective site and their storage takes only place if you agree explicitly agree to it. It should be noted, however, that refusing cookie storage can interfere with some functions in the related website.\nWeb-Analytics / Webtracking / Log-Files # I reserve the right to create and store anonymous access statistics to optimize my services. Neither the IP address, nor the specific browser recognition can be recovered from this anonymization.\nAt the moment I do not use web-analytics (webtracking) software on my websites. Only the IP addresses incl. referrer URL (link origin), browser type identification as well as date and time of access are stored for a few days for technical reasons, e.g. for troubleshooting or detection of attacks.\nFor billing purposes, my Internet server service provider stores the aforementioned access logs. Further, log data is gathered when sending e-mails, in particular the sender and recipient e-mail address as well as the date, time, and the IP address of your mail server or a forwarding mail server.\nSocial-Media # Currently, I do not use third-party social media plugins on my websites. Links to third-party sites are not covered by this restriction because you must actively click them to send any data to the linked website.\nData Security # Data transmission from and to my Internet services is always encrypted, e.g. via HTTPS on the web and via TLS for e-mails.\nThe data itself is stored exclusively on encrypted storage media, including the data backups.\nAccess to personal data is restricted to authorized persons subject to the obligation of confidentiality.\nYour Rights # Within the applicable legal regulations, you have the right to receive information for free about:\npersonal data stored by me,\nthe origins of this data,\nthe recipient(s) in case of forwarding to third parties\nas well as the purpose of data gathering.\nUnder the condition of applicable law, you also have the right, to get any invalid data corrected and to have your personal data deleted on request, this applies as long as your request is not in conflict with any other legal or contractual obligation.\nFurthermore, you have the right to have your personal data handed over to you in a standard, machine-readable format.\nIf you have given consent to the processing of personal data for specific purposes, you may revoke your consent at any time with future effect.\nAbove actions about information, deletion, correction, delivery in machine-readable form or revocation of a consent will be performed within 4 weeks after your request.\nIn any of such cases, please feel free to contact me anytime through the above contact details.\nIn relevant cases, you have also the opportunity to contact the governments\u0026rsquo; data privacy agency which is responsible for me:\nLandesbeauftragter für den Datenschutz Niedersachsen\nPostfach 221\n30002 Hannover\nTelefon: 0511 120-4500\nFax: 0511 120-4599\nE-Mail: poststelle@lfd.niedersachsen.de\nWeb: https://www.lfd.niedersachsen.de/startseite/\n","date":"3 November 2024","externalUrl":null,"permalink":"/en/privacy-policy/","section":"Michael Hönnig","summary":"(This translation of my original German Datenschutzvereinbarung just for informational purposes.)","title":"Privacy Policy","type":"page"},{"content":" The Problem # I had severe problems with PulseSecure VPN (with a MacBook Pro). Direct WiFi connections seemed to work fine, but as soon as the VPN was active, connections to the intranet were super slow or stopped working at all. When using a proxy to access the Internet, nothing went through anymore, except short packages, e.g. a message sent through a messenger.\nThe Setup # My WiFi router sits one story below with a steel/concrete sealing in between. My MacBook is a bit further away than my Linux laptop with a monitor and KVM in between.\nMost of the WiFI devices in my household connect via 2.4 GHz and only 3-4 via 5 GHz, including my MacBook. And without the VPN I never noticed any problems with my MacBook Internet connection.\nI know well from my former apartment in Hamburg, what trouble ~10 nearby WiFi networks can cause. But I had this problem in a single home with no other neighbour’s 5 GHz WiFi in range and, according to my FritzBox WiFi router, without any other electrical noise in this frequency range.\nMy Experiments # The IT support for the VPN said, there is no issue on their side and such problems usually are caused by client side problems, which meant my WiFi or my WAN connection. Thus, I conducted a few experiments:\nFirst, I wanted to check the stability of the connection (without VPN). After all, the VPN has to be tunneled through my raw Internet connection; and packet loss could lead to retries and somehow cause the VPN to disconnect. To have comparable results between Linux and MacOS without installing any extra tools, I used packetlosstest.com to verify my hypothesis. It showed a 3% package loss for my Linux laptop and 26% package loss for my MacBook. That’s quite a difference!\nNext, I wanted to move my MacBook to the other side of my desk to test if the WiFi reception improves. For a surprise, as soon as I disconnected the USB-adapter for KVM + HDMI, the VPN seemed to work fine, the packet loss went down to 3%. Unfortunately, after reconnecting, it kept working fine! I switched the KVM to my Linux laptop and back a few times, and sometimes the VPN worked, other times not. Coincidence?\nBy the way, finally moving the MacBook to the other side of my desk did not improve anything.\nThe Solution === # Then, I set up a WiFi repeater, using a different network name, right close to the MacBook in ~1m distance with just a stone wall in between, connected to the FritzBox router by a Develo Powerline. Now the VPN worked fine!\nBut the real surprise is, packetlosstest.com was still often indicating up to 30% packet loss, sometimes just 3% like on my Linux laptop. This meant, the package seemed not to be the cause of the problem, and I still have not found the real cause.\nAnyway, the MacBook is now the only device connecting to this extra network. Maybe that is what makes the difference?\nAny comments about this article are welcome on Twitter.\n Networking WiFi VPN","date":"30 January 2021","externalUrl":null,"permalink":"/en/blog/2021/2021-01-30-bad-vpn-connection-via-wifi-with-surprise-solution/","section":"Blog","summary":"I had severe problems with PulseSecure VPN (with a MacBook Pro). Direct WiFi connections seemed to work fine, but as soon as the VPN was active, connections to the intranet were super slow or stopped working at all. When using a proxy to access the Internet, nothing went through anymore.  I am still not sure about the cause, but found a solution.","title":"Bad VPN Connection via WiFi With Surprise Solution","type":"blog"},{"content":"","date":"30 January 2021","externalUrl":null,"permalink":"/en/tags/Networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"","date":"30 January 2021","externalUrl":null,"permalink":"/en/tags/VPN/","section":"Tags","summary":"","title":"VPN","type":"tags"},{"content":"","date":"30 January 2021","externalUrl":null,"permalink":"/en/tags/WiFi/","section":"Tags","summary":"","title":"WiFi","type":"tags"},{"content":"","date":"14 January 2021","externalUrl":null,"permalink":"/en/tags/Followerpower/","section":"Tags","summary":"","title":"Followerpower","type":"tags"},{"content":"In this article I collection questions which I posted on Twitter, maybe you have an answer? Looking forward to your responses!\nHere you can find all my Tweets.\n2020-01-14: MacOS vs. PC (Windows/Linux) keyboard # What’s the best way to use (or get used) to the difference of #MacOS and PC (#Windows/#Linux) keyboard layouts, especially on an external keyboard via KVM switch? Anyone sharing their experience?\nJump to my Tweet.\n2020-01-06: ratio infrastructure vs. domain code in a Microservice # Tell if if I’m wrong or not:\nIf in a #Microservice, the infrastructure (#CloudFormation, #Terraform, whatever) code is more complicated than the domain related code, or even just simply larger, I think that something is seriously flawed. But what do I know …​.\nJump to my Tweet.\n2020-01-03: Anyone with an Apple M1? # Anyone with an Apple M1?\nRegarding this real-world benchmark from another of my Tweets:\nI ran a \u0026ldquo;gradlew clean pitest\u0026rdquo; using 6 threads on an 8 core i7-8650U CPU @ 1.90GHz: - Ubuntu Linux 20.04: 2min 3sec - Windows 10 Pro: 2min 44sec\nAnd on a MacBook Pro with 8 core i7-1068NG7 CPU @ 2.30GHz - macOS Catalina 10.15: 1min 21sec\n(time from 2nd of two subsequent runs)\nJump to my Tweet.\n2020-12-22: #PairProgramming / #MobProgramming for OpenSource/#FreeSoftware? # Ever anybody has done #PairProgramming, #MobProgramming/#EnsembleProgramming or just #CodeReviews on their #OpenSource/#FreeSoftware projects? How did you ensure the license? I mean, make sure you are allowed to publish their inputs? cc @fsf\nJump to my Tweet.\n2020-10-27: A Term for \u0026ldquo;#FaaS and friends\u0026rdquo;? # I am looking for a term for \u0026ldquo;#FaaS and friends\u0026rdquo;, with friends I mean #serverless (!) services (*aaS) for databases, messaging, queueing, API gateway, bridges etc.\n#FaaS is just compute and #PaaS and #Serverless are both too broad. What would be the right term?\n#followerpower\nJump to my Tweet.\n2020-10-25: Who’s going to maintain such software? # Just a thought: If you have a team of just the brightest software developers you could employ, and they are well delivering value using modern technologies, who’s going to maintain this software once these technologies are not so modern anymore?\nJump to my Tweet.\n2020-10-22: Correlation of productivity vs. employability? # Regarding software-development, what are your thoughts about the correlation of productivity vs. employability?\nJump to my Tweet.\n2020-10-21: Best #FaaS + friends system for separation of concerns? # Which is the best #FaaS + friends (API-Gateway, Messaging, Queueing, Database, Storage etc.) system if separation of concerns (tech- vs. business-driven systems) and ease of configuration+programming is most important?\nJump to my Tweet.\n2020-10-13: Best #Serverless provider for #JVM based applications? # Which is the best #Serverless provider for #JVM based applications?\nJump to my Tweet.\n Software-Development Social-Media Twitter Followerpower","date":"14 January 2021","externalUrl":null,"permalink":"/en/blog/featured/my-questions-on-twitter/","section":"Blog","summary":"In this article I collection questions which I posted on Twitter, maybe you have an answer?  Looking forward to your responses!","title":"My Questions on Twitter","type":"blog"},{"content":"","date":"14 January 2021","externalUrl":null,"permalink":"/en/tags/Social-Media/","section":"Tags","summary":"","title":"Social-Media","type":"tags"},{"content":"","date":"14 January 2021","externalUrl":null,"permalink":"/en/tags/Twitter/","section":"Tags","summary":"","title":"Twitter","type":"tags"},{"content":"","date":"November 30, 2020","externalUrl":null,"permalink":"/de/tags/Freelancer/","section":"Tags","summary":"","title":"Freelancer","type":"tags"},{"content":"Immer mehr Konzerne gehen aufgrund der Rechtsunsicherheit im Bezug auf rückwirkende Sozialversicherungspflicht keine Verträge mehr mit Freelancern ein, sondern beschäftigen externe Mitarbeiter nur noch über Arbeitnehmerüberlassung (ANÜ). Hier stelle ich einen Ansatz zur Berechnung eines Gehaltsäquivalents auf Basis eines Stundensatzes vor.\nHinweis This article is in German because its content is very specific to German employment as well as social security laws and contains German legal terms.\nDie Problematik # Angesichts der Rechtsunsicherheit bezüglich Sozialversicherungspflicht von Kontraktoren, scheuen sich vor allem Konzern-Kunden zunehmend, Dienstverträge mit IT-Freelancern abzuschließen; hier inbegriffen sind auch indirekte Verträge über Vermittlungsagenturen. Im Falle der nachträglichen Feststellung der Sozialversicherungspflicht müssen die Sozialversicherungsbeiträge nachgezahlt werden; zudem werden Strafzahlungen fällig. Auch private Kranken- und Rentenversicherungen dieser Freelancer schützen hier die Auftraggeber nicht.\nAus diesem Grund wird das Angebot an Dienstverträgen für Freelancer trotz steigender Nachfrage nach gut ausgebildeten IT-Fachkräften immer kleiner.\nHinweis Für die IT-Freelancer besteht bei Dienstverträgen außerdem ebenfalls ein Risiko, falls nachträglich zwar keine Sozialversicherungspflicht, nämlich wenn sie als arbeitnehmerähnliche Selbständige gelten, unterliegen sie der Rentenversicherungspflicht, und müssen die Rentenbeiträge für bis zu 5 Kalenderjahren aus eigener Tasche nachzahlen.\nDaher werden für Mitarbeit in Projekten statt Dienstleistungsverträgen zunehmend Arbeitnehmerüberlassungsverträge angeboten. Bei der Arbeitnehmerüberlassung (ANÜ), wird der Freelancer von einem Personaldienstleister projektweise eingestellt und dieser als dessen Arbeitnehmer an den eigentlichen Auftraggeber verliehen. Der Freelancer wird also zum Angestellten auf Zeit und somit Sozialversicherungspflichtig. Die o.g. Risiken für den Auftraggeber und den Freelancer entfallen im Gegenzug.\nEchte Werkverträge, also solche, die tatsächlich als Werkvertrag gelebt werden und nicht nur auf dem Papier so genannt werden, mögen grundsätzlich eine Alternative sein. Auch wenn IT-Projekte ohne Integration der externen Mitarbeiter in das Unternehmen des Auftraggebers auskommen, ist aber i.d.R. eine sehr enge Zusammenarbeit mit dem Projektteam nötig, sodass Werkverträge sich kaum formulieren oder gar leben lassen.\nVor wenigen Tagen hat das Bundesarbeitsministerium Reformvorschläge aus der Wirtschaft leider zurückgewiesen, die berücksichtigen sollten, dass insbesondere IT-Freelancer aufgrund ihrer Einkommenshöhe nicht zu einer besonders schützenswerten Gruppe gehören. — vgl. Artikel bei heise.de\nBegriffsklärung # Die in diesem Artikel verwendeten Begriffe sind weiter unten in Glossar erläutert und klar voneinander abgegrenzt.\nInsbesondere verwende ich den Begriff Freelancer unabhängig vom rechtlichen Status in erweitertem Sinne für Mitarbeiter, die ihre Mitarbeit für ein Projekt, Teilprojekt oder Auftragsbezogen anbieten; damit umfasst dieser Begriff in diesem Artikel sowohl den Kontraktor als auch den Angestellten in Arbeitnehmerüberlassung. Den Begriff Kontraktor wiederum verwende ich für juristisch selbständige Freelancer, die ihre Leistungen über Dienst- oder Werkverträgen ausüben.\nDie Kalkulation # Der hier vorgestellte Gehaltsäquivalenzrechner steht als Download im OpenOffice.org/LibreOffice-Calc-Format und im Excel Office Open XML-Format zur Verfügung. Im Folgenden einige Erläuterungen zur Anwendung.\nHinweis Der in dieser Tabellenkalkulation verwendete Stundensatz ist willkürlich gewählt, lässt keinen Rückschluss auf tatsächliche Verträge zu und muss selbstverständlich individuell angepasst werden.\n1. Berechnung der Arbeitszeit # Die Arbeitszeit eines Kontraktors kann nicht 1:1 auf die eines Angestellten umgerechnet werden, weil Angestellte bezahlten Urlaub, bezahlte Feiertage und bezahlte Krankentage haben, Kontraktoren aber nicht. Im 1. Abschnitt wird daher zunächst die Arbeitszeit umgerechnet.\nFigure 1. 1. Berechnung der Arbeitszeit\nDazu werden, jeweils auf ein Jahr gerechnet die gesamten Tage, die Wochenendtage, die auf Arbeitstage fallenden Feiertage sowie kalkulatorische Krankheitstage eingetragen. Die Tabellenkalkulation rechnet diese dann in effektive Arbeitstage pro Zeiteinheit um.\n2. Berechnung des Kontraktor- Monats- und Jahreseinkommens vor Steuern # Basierend auf einem einzugebenden Stundensatz wird anhand der Eingaben aus 1. das monatliche und jährliche Umsatzsteuer-Netto-Einkommen, sowie ein auf 40 Wochenstunden umgerechnetes Jahreseinkommen errechnet. Letzteres ist sinnvoll, da viele ANÜ-Gehaltsvereinbarungen auf einer 40 Stunden Kalkulation basieren, selbst wenn der Angestellte in Arbeitnehmerüberlassung mehr oder weniger Stunden arbeit; das Gehalt wird dann meist nachträglich nach vertraglich festgelegten Formeln angepasst.\nFigure 2. 2. Einkommen als Kontraktor\n3. Umrechnungen aufgrund diverser Basisdaten # Die Blöcke 3a bis 3c umfassen eigentlich jeweils dieselben Rechnungen. Der Unterschied ist jeweils nur, welcher Wert als gegeben gesetzt wird und welcher Wert daraus errechnet wird.\nIm Block 3b müssen aber in jeden Fall die Sozialabgaben erfasst werden, da deren Berechnung zu komplex ist, um hier mit erfasst zu werden. Daher wird auf eine externe Website verwiesen, auf der diese Rechnung durchgeführt werden kann.\nHinweis Da die Einkommen von IT-Freelancern üblicherweise deutlich über den Beitragsbemessungsgrenzen der Sozialversicherungen liegt, muss diese Berechnung nicht nach jeder kleinen Änderung der sonstigen Eingabewerte angepasst werden, sondern nur, wenn das Monatseinkommen unter eben diese Beitragsbemessungsgrenzen fällt.\nDie folgenden Blöcke betrachten jeweils das Jahresgehalt normiert auf eine 40 Stunden-Woche, das Jahresgehalt basierend auf der angesetzten Wochenstundenzahl, das Monatsgehalt (basierend auf 12 Monatsgehältern) und den Stundenlohn. Der Stundenlohn bezieht sich dabei auf die tatsächlich geleisteten Arbeitsstunden aus der Kalkulation in Block 1. Ein Stundenlohn, der auch Urlaubs-, Feier- und Krankentage umfassen würde, wäre entsprechend höher, aber sinnlos im Vergleich zum Stundensatz als Kontraktor.\nDie meisten Personaldienstleister werden sich kaum darauf einlassen, einen nennenswerten Anteil der Sozialversicherungsbeiträge zu übernehmen, da die Leitungen daraus im Endeffekt nur dem Angestellten zugutekommen. Daher kann praktisch gesehen nur der Stundensatz in der Zeile \u0026ldquo;AG-Brutto\u0026rdquo; zum Vergleich herangezogen werden.\nTipp Andererseits kommt der Wunsch nach Arbeitnehmerüberlassung statt Dienstvertrag vom Auftraggeber, also dem entleihenden Unternehmen, da dieses das Risiko scheut, bei nachträglich festgestellter Sozialversicherungspflicht für ggf. mehrere Jahre die Sozialversicherungsbeiträge für den Konktraktor nachzuzahlen, der dann rückwirkend als Angestellter gilt. D.h. der Auftraggeber mindert durch die ANÜ-Verträge sein Risiko. Meiner persönlichen Meinung nach sollte dieser sich das also auch etwas kosten lassen, sodass man den Stundensatz durchaus auch etwas höher ansetzen kann oder zumindest jegliche weiteren Kosten vom verleihenden Unternehmen an das entleihende Unternehmen und nicht an den Arbeitnehmer weitergegeben werden sollten.\nIm Block 3a erden zudem die o.g. Sozialabgaben erfasst, die dann auch in den weiteren Blöcken wieder verwendet werden. Der AG-Anteil der Sozialabgaben erhöht zum einen die Kosten für das verleihende Unternehmen, den Arbeitgeber des Angestellten; der AN-Anteil der Sozialabgaben verringert zudem die Auszahlung für den Angestellten; beides ist in der Kalkulation berücksichtigt. Beim AN-Anteil der Sozialabgaben müssen aber die Krankenversicherungsbeiträge wieder herausgerechnet werden, weil der Freelancer diese schließlich auch als Kontraktor zahlen müsste.\nWarnung Für alle folgenden Berechnungen gilt, dass das Gehalt von Angestellten von Personaldienstleistern zur Arbeitnehmerüberlassung nicht mit Angestellten in IT-Beratungen und -Systemhäusern oder im Anwenderbetrieb verglichen werden können. So sind die Anstellungen nur projektweise, es werden nur die vom Kunden abgenommenen Arbeitsstunden bezahlt, die Kündigungsfrist ist meist so kurz wie es das Gesetz zulässt und Weiterbildungen sind i.d.R. ebenfalls nicht enthalten. Dadurch sind andererseits logischerweise angesichts der Marktlage die Gehälter höher.\n3a. Berechnung des äquivalenten Bruttogehalts anhand des Stundensatzes und Sozialabgaben # Basierend auf der unter 1. erfassten Arbeitszeit und dem unter 2. erfassten Stundensatz sowie den hier erfassten Sozialabgaben wird in diesem Block das Arbeitgeber-Brutto, das Arbeitnehmer-Brutto und das um die Krankenversicherungsbeiträge korrigierte Arbeitnehmer-Brutto errechnet.\nDiese Rechnung ist also relevant, wenn man als Freelander erstmalig errechnen möchte, in welcher Größenordnung man ein Gehalt als Angestellter in ANÜ\nFigure 3. 3a. Berechnung Bruttogehalt aus Stundensatz\n3b. Berechnung des effektiven Stundensatzes basierend auf 40-Stunden/Woche-Äquivalenz-Bruttogehalt und Sozialabgaben # Basierend auf der unter 1. erfassten Arbeitszeit und einem hier erfassten auf 40-Stunden basierenden Jahresgehalt werden im Wesentlichen die drei o.g. Stundensätze errechnet.\nDer Haupt-Anwendungsfall dieser Variante dürfte sein, aus einem Gehaltsangebot eines Personaldienstleisters den äquivalenten Stundensatz zu errechnen, um diesen mit seinen Zielvorstellungen zu vergleichen.\nFigure 4. 3b. Berechnung Stundensatz aus Bruttogehalt\nWarnung Noch einmal zur Klarstellung: Die Stundensätze in dieser Kalkulation sind alle bezogen auf die tatsächliche Arbeitszeit, also vergleichbar mit dem Stundensatz als Kontraktor. Lediglich einmal aus der Perspektive des Arbeitgebers und einmal aus der Perspektive des Arbeitnehmers. Ein in einem Angestelltenvertrag genannter Stundensatz wäre noch niedriger, da dieser auch für kalkulatorische Stunden an Urlaubs-, Feier- und Krankheitstagen gezahlt würde.\nSteuerliche Aspekte # Steuerliche Aspekte sind in dieser Berechnung außen vor gelassen, da sich die Steuerlast zwischen Kontraktoren und Angestellten im IT-Umfeld nicht wesentlich unterscheidet. Viele beruflich veranlasste Ausgaben, z.B. Hotelunterkunft bei wechselndem Einsatzort oder Arbeitsgeräte, müssen nun vom Arbeitgeber, also vom entleihenden Unternehmen getragen werden. Und weitere beruflich veranlasste Ausgaben bleiben schließlich auch als Angestellter weiterhin als Werbungskosten absetzungsfähig, wenn diese auch steuerrechtlichen Grenzwerten unterliegen. Fahrtkosten zur Arbeitsstelle können allerdings nur noch als Kilometerpauschale abgesetzt werden, wer also bisher einen Dienstwagen steuerlich abgesetzt hat, könnte hier finanzielle Nachteile erleiden.\nFalls der Freelancer noch weitere Kunden hat, für die er nach wie vor als Kontraktor arbeitet, bleibt der steuerliche Status als Freiberufler sowieso i.d.R. bestehen.\nFür weitere Informationen zu diesem Thema verweise ich auf fachkundige Hilfe eines Steuerberaters.\nSchlussbemerkungen # Ein Freelancer nach der o.g. Begriffsdefinition möchte eigentlich selbständig arbeiten, wird aber durch die in Deutschland Selbständigen-feindliche Gesetzgebung beim Thema Scheinselbständigkeit zunehmend in Angestellten-Verhältnisse gedrängt. Aus meiner Perspektive als Freelancer sind dies Schein-Angestellten-Verhältnisse, da ich von meiner Geisteshaltung her auch als Angestellter in Arbeitnehmerüberlassung immer noch wie ein Selbständiger denken und handeln würde.\nDie so errechneten Zahlen stimmten bei identischem Ansatz von Urlaubs-, Feier- und Krankheitstagen in etwa mit den mir in einem Personaldienstleister errechneten Gehalt überein. Sollte jedoch jemand Berechnungsfehler finden, würde ich mich über einen Hinweis zur Korrektur freuen.\nWichtig Dieser Artikel stellt keine Rechtsberatung dar, sondern soll lediglich Anregungen zur Umrechnung zwischen Stundensatz und Jahresgehalt geben. Jeder persönliche Fall wird besondere Aspekte umfassen, die ggf. speziell zu berücksichtigen sind und zu anderen Zahlen führen werden.\nKommentare zu diesem Artikel gerne über meinen Twitter-Account.\nGlossar # (alphabetisch sortiert)\nAngestellter in Arbeitnehmerüberlassung Ein Mitarbeiter, der bei einem verleihenden Unternehmen angestellt ist, und an ein entleihendes Unternehmen zeitweise verliehen wurde. Juristisch gesehen ist dadurch das verleihende Unternehmen weisungsberechtigt, was mangels Integration des Mitarbeiters in das verleihende Unternehmen aber praktisch auf die Weisung reduziert, für das entleihende Unternehmen tätig zu werden. arbeitnehmerähnliche Selbständige Neben der Scheinselbständigkeit gibt es noch diese zweite Stufe zwischen Angestellten und Selbständigen. Wenn von der Deutschen Rentenversicherung zwar nicht als scheinselbständig, aber als arbeitnehmerähnlich selbständig eingestuft wird, bleibt zwar steuerlich selbständig, wird aber Rentenversicherungspflichtig und muss zudem die Rentenversicherungsbeiträge für bis zu 5 Kalenderjahre nachzahlen. Für den Freelancer ist dies das größere finanzielle Risiko. — vgl. Haufe Arbeitnehmerüberlassung Ein bei einem verleihenden Unternehmen angestellter Mitarbeiter wird an ein entleihendes Unternehmen (hier der Auftraggeber) zeitweise ausgeliehen. In Branchen außerhalb der Wissensarbeit wird hierfür meist der Begriff Zeitarbeit verwendet. Weitere Begriffe sind Personalleasing oder Leiharbeit. Die verleihenden Unternehmen brauchen für die Arbeitnehmerüberlassung eine Erlaubnis. — vgl. Gabler Wirtschaftslexikon Auftraggeber Das Unternehmen, welches das Projekt durchführt, in dem der Mitarbeiter eingesetzt wird. Dienstvertrag Ein Vertrag bei dem der Auftragnehmer (Kontraktor) dem Auftraggeber seine Arbeitszeit schuldet. In agilen IT-Projekten ist dies die Regel. externer Mitarbeiter Ein Mitarbeiter eines Auftraggebers, der nur Auftrags- oder Projektbezogen beschäftigt wird, nicht in das Unternehmen des Auftraggebers integriert und damit in Bezug auf dieses auch nicht weisungsgebunden ist. Freelancer Eigentlich versteht man unter Freelancer einen rechtlich gesehen Selbständigen, der aufgrund von Dienst- oder Werkverträgen Aufträge für andere Unternehmen ausführt (vgl. Gabler Wirtschaftslexikon). Da dies aber kein juristischer Begriff, verstehe ich in diesem Artikel unter einem Freelancer einen Mitarbeiter, der sich unabhängig von der juristischen Ausgestaltung wie ein Selbständiger ausrichtet, sich an seine Arbeitgeber nur projektweise oder gar nur für Teilabschnitte von Projekten oder gar einzelne Aufträge bindet, laufend eigenes Marketing und eigene Weiterbildung betreibt. Damit entsteht ein Oberbegriff für Kontraktor und projektweise-Angestellter in Arbeitnehmerüberlassung, welcher der Selbstsicht dieser Gruppe von Berufstätigen entspricht. Freelancer in beiderlei Sinne sind in Bezug auf den Auftraggeber nicht in dessen Unternehmen eingebunden und nicht weisungsgebunden. Freiberufler / freiberufliche Tätigkeit Steuerlicher Status eines Selbständigen, der dadurch unabhängig vom Jahresumsatz nicht Bilanz-pflichtig ist und nach vereinnahmten Entgelten versteuern darf. Dieser Begriff spielt in diesem Artikel keine wesentliche Rolle. freier Mitarbeiter Dasselbe wie ein Kontraktor, nur aus der Sicht des Auftraggebers. Kontraktor Ein Mitarbeiter, der aufgrund eines Dienst- oder Werkvertrages auf eigene Rechnung für einen Auftraggeber tätig ist. Den Begriff habe ich in diesem Artikel im Sinne des englischen Contractor verwendet, um ihn juristisch klar vom allgemeineren Begriff Freelancer abzugrenzen. Scheinselbständigkeit Ein Arbeitsverhältnis, das als Dienstvertrag ausgelegt ist, aber von der Deutschen Rentenversicherung praktisch als Angestelltenverhältnis bewertet wird. Wird Scheinselbständigkeit nachträglich festgestellt, wird der Kontraktor nachträglich zum Angestellten und der Auftraggeber zum Arbeitgeber, der dann die Sozialversicherungsbeiträge zzgl. einer Strafzahlung rückwirkend nachzahlen muss. — vgl. HK Hamburg Werkvertrag Ein Vertrag bei dem der Auftragnehmer dem Auftraggeber ein konkretes Werk schuldet. In IT-Projekten wäre dies z.B. die Erstellung einer per Pflichtenheft beschriebenen Software.  Freelancer","date":"November 30, 2020","externalUrl":null,"permalink":"/de/blog/2020/2020-11-30-gehaltsaequivalent-anue-angestellter-im-vergleich-fuer-freelancer/","section":"Blog","summary":"Immer mehr Konzerne gehen aufgrund der Rechtsunsicherheit im Bezug auf rückwirkende Sozialversicherungspflicht keine Verträge mehr mit Freelancern ein, sondern beschäftigen externe Mitarbeiter nur noch über Arbeitnehmerüberlassung (ANÜ). Hier stelle ich einen Ansatz zur Berechnung eines Gehaltsäquivalents auf Basis eines Stundensatzes vor.","title":"Gehaltsäquivalent als Angestellter (ANÜ) im Vergleich für Freelancer","type":"blog"},{"content":" Listen to an audio recording of this blog post:\nDownload the audio recording (MP3) The gradle wrapper can download and verify a specified version of gradle for a project without the need of a local gradle executable. The gradle documentation is suggesting to check it into the VCS (version control system, e.g. git). This article discusses the advantages and disadvantages of keeping the gradle wrapper in a VCS and presents an alternative.\nThe Idea Behind the Gradle Wrapper # To build a gradle project, you need a gradle executable; to be more precise we need a jar-file which can be run by the JVM as an executable. The gradle-distribution is not just almost 100 MB in size (for gradle 6.7.1), but it’s also a binary file. Thus, we definitely don’t want to have it checked into our VCS.\nFurthermore, our build file might need a specific version of gradle. Even if it was compatible with a relatively wide range of gradle versions, we usually want exact versions to be used in a build system for repeatability of the build. So, all developers should use exactly the same version of gradle.\nThat’s why the gradle wrapper was introduced. And, with just 55 KB (for gradle 6.7.1) it’s a relatively small jar-file. Thus, we could check it into our VCS; exactly how its suggested by the gradle team:\nTo make the Wrapper files available to other developers and execution environments you’ll need to check them into version control. All Wrapper files including the JAR file are very small in size. Adding the JAR file to version control is expected. Some organizations do not allow projects to submit binary files to version control. At the moment there are no alternative options to the approach.\n— from the \u0026lsquo;https://docs.gradle.org/current/userguide/gradle_wrapper.html[_gradle_ documentation]\u0026rsquo;\n\u0026ldquo;No alternatives\u0026rdquo;? That’s not quite true, there is actually an alternative, or at least a variation, as I’ll show later.\nTip Another big advantage of using the gradle wrapper is, that the project can even be build by developers without any gradle version installed on their machine at all. The approach presented below cannot fix that problem.\nHow the Gradle Wrapper Works # When the gradle wrapper is installed in a project, either a provided Windows batch (gradlew.bat) or shell script (gradlew) is used to start it in a locally installed JVM. It then reads the gradle/wrapper/gradle-wrapper.properties configuration file, and checks if the specified gradle distribution is available; if not, it’s downloaded into the specified gradle cache directory. Finally, it starts the actual build using this specific gradle version.\nexample gradle/wrapper/gradle-wrapper.properties file\ndistributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists The Problems with Binary Files # Git and most other VCS are designed to store line oriented text files, like typical source code. They can’t create diffs for new versions of binary files, thus each new version of a binary file results in a complete new copy, taking up a lot of space.\nOther than picture files, for example, which are often checked into a VCS, despite being binary files as well, executable code has another issue: It could contain malicious code, but as the VCS cannot display the changes even less in a human readable format, such dangers are not very obvious. That’s why some companies even disallow checking in binary files with executable code.\nNote Even wrappers from very old gradle versions can still be used for projects which require a new gradle version. Therefore, you usually don’t need to upgrade the wrapper binary itself too frequently.\nAn Alternative # Instead of checking in the gradle wrapper binary jar-file, you can generate it after checking out a copy of the project, .e.g. in a shell on a UNIXoid-system this is done with the following command:\ngradle wrapper --gradle-version 6.7.1 But in this case the developer has to know the correct version to be used.\nThis problem can be fixed, though, by specifying the version for the wrapper task:\nbuild.gradle\ntasks.named(\u0026#39;wrapper\u0026#39;) { gradleVersion = \u0026#34;6.7.1\u0026#34; } build.gradle.kts\nimport org.gradle.api.tasks.wrapper.Wrapper tasks.named\u0026lt;Wrapper\u0026gt;(\u0026#34;wrapper\u0026#34;) { gradleVersion = \u0026#34;6.7.1\u0026#34; } Now, the wrapper can be generated simply like this:\ngradle wrapper Additionally, the generated files should be excluded from your VCS. For git e.g. this could be done this way:\n.gitignore\n# Ignore Gradle-wrapper files /gradlew /gradlew.bat /gradle/wrapper/ ... Caution In either case, if you don’t check the gradle wrapper into your VCS, all developers need at least some gradle version installed on their machine.\nConclusion # The gradle wrapper is a helpful feature of the gradle build system. You could build a freshly checked out project right away, without the need of a locally installed gradle version at all.\nIf you don’t want to check in executable binary code, the gradle wrapper can still be utilized, but at least any gradle version must be locally installed.\nAny comments about this article are welcome on Twitter.\n Software-Development Gradle Git VCS","date":"20 November 2020","externalUrl":null,"permalink":"/en/blog/2020/2020-11-20-checkin-gradle-wrapper-or-not/","section":"Blog","summary":"The gradle wrapper can download and verify a specified version of gradle for a project without the need of a local gradle executable.  The gradle documentation suggests to check it into the VCS (version control system, e.g. git).  This article discusses the advantages and disadvantages of keeping the gradle wrapper in a VCS and presents an alternative.","title":"Checking in the Gradle Wrapper or Not?","type":"blog"},{"content":"","date":"20 November 2020","externalUrl":null,"permalink":"/en/tags/Git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"","date":"20 November 2020","externalUrl":null,"permalink":"/en/tags/Gradle/","section":"Tags","summary":"","title":"Gradle","type":"tags"},{"content":"","date":"20 November 2020","externalUrl":null,"permalink":"/en/tags/VCS/","section":"Tags","summary":"","title":"VCS","type":"tags"},{"content":"","date":"30 October 2020","externalUrl":null,"permalink":"/en/tags/COBOL/","section":"Tags","summary":"","title":"COBOL","type":"tags"},{"content":" Listen to an audio recording of this blog post:\nDownload the audio recording (MP3) COBOL (COmmon Business Oriented [programming] Language) dates back to 1959, and is still used in its realm of business software, mostly on mainframe computers. Of course, there are also many related software-modernization and -migration projects. Thus, once in a while even non-mainframe developers might need some COBOL knowledge. Commercial COBOL systems, especially on real mainframes, are expensive. In this article I demonstrate how to set up the GnuCOBOL compiler and integrate it with Visual Studio Code on Ubuntu Linux, but it also works similar on other Linux distributions, Windows and macOS. This way, experimenting with COBOL becomes easy and cheap.\nNote If you are not interested in some prosa about COBOL, but you just want to install and run that thing? Then jump to Installing the Compiler right away!\nThe Ideas Behind COBOL # From today’s perspective, COBOL is more like a DSL (domain specific language) in its realm of business applications for finance, accounting, business administration etc. Its core idea was enabling people from these subject areas to be able to read the business logic without deeper programming knowledge, e.g. for validation or audits. Many employees from business fields even switched their profession and became COBOL developers. If that all really worked out in the end, is another question.\nFurther, COBOL was made to deal with large amounts of data - at least what once counted as such. It directly supports, batch processing, reporting and online transaction processing (OLTP), the latter just through text-based user interfaces, though. With COBOL you can process random access files with multiple indices, create dialog screens and simple reports out-of-the-box without the need of any extra libraries.\nOne of its strength are monetary calculations through its intrinsic support of fixed point arithmetic and currency support. The latest standards even support object oriented programming, with a quite clumsy notation, though.\nTip As a little anecdote: Did you know that the COBOL evangalist and one of the inventors of OO COBOL, Jerome Garfunkel (✝2018), was the brother of the famous musician Art Garfunkel?\nReasons for COBOL in 2020 # According to a survey in the context of the COBOL60 celebrations (at about 45:05, needs registration) 87% of COBOL users expected that core business applications written in COBOL will still be in use when they retire. Most of these applications are running on mainframe computers.\nBut there are even reasons why software developers from outside of the mainframe world might need some knowledge in this legacy programming language, e.g. in the context of:\nUnderstanding business logic coded in COBOL for software migration purposes.\nIntegrating COBOL software systems with systems using modern programming languages.\nExporting data from software written in COBOL, because sometimes that’s the easiest way to access the original data.\nBy my experience, old ideas often come back in new shapes. So knowledge about the these old ideas can help to come up with new ones.\nI am sure there are many other reasons why one might need some COBOL knowledge these days, even without being a mainframe developer. Micro Focus, one of the major vendors or COBOL compilers and development environments, even made it possible to migrate COBOL software from mainframes to microservices running in the cloud! This opens the door for completely new levels of software integration.\nWarning I have only very limited COBOL experience, and the few projects where I got in touch with it, are decades ago. Just recently I needed to read some COBOL code again, which was actually the igniting spark for this blog post. Dealing with COBOL is fine for me, as long as it’s only a minor part of the project. Especially in the process of a software modernization, it can even be fun. But I’m certainly not interested, neither qualified in mainly COBOL projects.\nOptions for using COBOL with a PC # Without much research I ran across these options to use COBOL with a PC system:\nThe commercial product Micro Focus Visual COBOL, a 30-day free trial is available.\nAn online editor and compiler by Tutorialspoint based on GnuCobol 2.2, which currently seems to be down, though.\nThe MVS 3.8j Tur(n)key 4- System, a ready to use OS/VS2 MVS 3.8j system built for the Hercules IBM System/370, ESA/390, and z/Architecture mainframe emulator, which supposedly contains a COBOL compiler.\nGnuCobol (formerly OpenCOBOL) , implements most parts of COBOL 85 to COBOL 2014 and other standards, as well as many extensions from COBOL vendors like IBM or Micro Focus.\nIn this article I’ll show how to install and use GnuCOBOL 3.1 together with Visual Studio Code (vscode) on an Ubuntu 20.04 system. There are also blog articles for Windows and macOS, both also using GnuCOBOL 3.1 as well.\nInstalling the Compiler # Ubuntu 20.04 only contains a package of GnuCobol 2.2, which works but is quite old. The recently released Ubuntu 20.10 contains even a nightly build of upcoming GnuCOBOL 4.0.\nBecause I wanted to stick with Ubuntu 20.04 for now, and prefer not to use bleeding edge testing versions, I decided to install GnuCobol 3.1-rc1. It’s not yet released either, but at least a release candidate. Here the installation steps ($ just indicates the command prompt, do no type it):\ninstalling gnucobol-3.1-rc1 on Ubuntu 20.04\n$ GNU_COBOL_DIR=$HOME/Programs/GnuCobol $ wget https://sourceforge.net/projects/gnucobol/files/gnucobol/3.1/gnucobol-3.1-rc1.tar.gz/download $ sudo apt-get install libdb-dev # only for Berkeley DB, if needed $ ./configure --prefix=$GNU_COBOL_DIR # alternatively, add `--without-db` $ make $ make check $ make install $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GNU_COBOL_DIR/lib $ export PATH=$PATH:$GNU_COBOL_DIR/bin Find further information about the installation process in the GnuCobol FAQ.\nTo try it out, run this in your shell ($ just indicates the command prompt, do no type it):\n$ echo \u0026gt;hello-world.cbl \u0026lt;\u0026lt;EOF IDENTIFICATION DIVISION. PROGRAM-ID. hello-world. PROCEDURE DIVISION. DISPLAY \u0026#34;Hello world!\u0026#34;. STOP RUN. EOF $ cobc hello-world.cbl $ cobcrun hello-world You just compiled and ran your first COBOL program!\nWarning What’s important here, the program-id hello-world has to be exactly match the module name which is the basename of the file. Otherwise, it won’t work and cobcrun will complain about not finding the module.\nGnuCobol transpiles COBOL-code to C-code and then compiles the C-code to a shared-object-file (.so). It can also create executable files, but using cobcrun takes care for shared libraries and is easier to use for experiments.\nChoosing an IDE # For my daily work, I prefer IntelliJ IDEA as an IDE: Unfortunately, as of now, there is no plugin available for COBOL, even though I found two experimental projects for such a plugin:\nhttps://github.com/npgeorgiou/cobol-plugin,\n(there is also a blog article about it on Medium)\nhttps://github.com/JustinEspinosa/IntelliCOB.\nThen there is OpenCobolIDE, due to library version conflicts I was not able to get it to run on Ubuntu 20.04. Additionally, it’s not maintained anymore and even its supposed successor HackEdit looks quite dead for years.\nThere seem to be some plugins for the Eclipse IDE, which I have not tried, though. And the COBOL-plugins for NetBeans look all very outdated.\nFinally, there is, Microsoft Visual Studio Code, a free source-code editor for Windows, Linux and macOS which also includes many IDE features. Here we get plenty of recently maintained COBOL plugins, surprise! I installed bitlang.cobol which comes with IntelliSense, highlighting, snippets and more. Feel free to try any of the others.\nIntegrating the Compiler into the IDE # It was bit awkward to always run the compiler and runtime after each change I’ve made to the source code during my experiments. For sure, with some more knowledge about Visual Studio Code, it would be possible to implement a real integration. But as this is not my usual IDE, and I needed it only for some experiments, I wrote a simple bash script:\nwatch-compile-and-run\n#!/bin/bash # --- configuration --- gnu_cobol_dir=$HOME/Programs/GnuCobol #gnu_cobol_cobc_args=\u0026#39;--std=ibm-strict\u0026#39; # (1) # --- initialization --- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$gnu_cobol_dir/lib:. export PATH=$PATH:$gnu_cobol_dir/bin cobc --version echo \u0026#34;----\u0026#34; echo # --- wait for changes in file system --- inotifywait -e close_write,moved_to,create -m . | # (2) while read -r directory events source; do ext=\u0026#34;${source##*.}\u0026#34; if [ \u0026#34;$ext\u0026#34; = \u0026#34;cbl\u0026#34; ]; then # (3) source_basename=$(basename -- $source .cbl) # ---- compile ---- rm -f $source_basename.so compile_command_line=\u0026#34;cobc $gnu_cobol_cobc_args $source\u0026#34; echo \u0026#34;---- compiling: $compile_command_line\u0026#34; $compile_command_line if [ $? -eq 0 ]; then # ---- execute ---- execute_command_line=\u0026#34;cobcrun $source_basename\u0026#34; echo \u0026#34;---- executing: $execute_command_line\u0026#34; $execute_command_line \u0026lt;/dev/tty # (4) if [ $? -eq 0 ]; then echo \u0026#34;---- DONE.\u0026#34; else echo \u0026#34;---- FAILED.\u0026#34; fi fi # ---- cleanup --- rm -f $source_basename.so echo fi done Here you can easily set compiler options for cobc, e.g. the COBOL-standard to use. See https://superuser.com/questions/181517/how-to-EXECUTE_COMMAND_LINE-a-command-whenever-a-file-changes Typical COBOL-file-name extensions for COBOL are .cob and .cbl, both lower- or uppercase. This script only recognizes lowercase `.cbl'. The pipe from inotifywait redirects stdin, but our program might need the normal terminal (TTY) for input. As COBOL has a maximum line length of 80 characters, I moved the vscode terminal window to the right and started my script: ./watch-compile-and-run. After displaying the compiler version, it waits for *.cbl files to change and then compiles and runs them automatically. Of course, this only works for single-module programs. Once your programs become larger, you’ll need a more professional setup.\nFigure 1. watch-compile-and-run in Visual Studio Code\nConclusion # It’s quite easy to get a pretty powerful COBOL development system to run on Linux. In general, it’s also possible to do this on Windows and macOS, they are just not my preferred development systems, so sorry, I don’t have instructions for these. You can find the instructions in the two external blog articles mentioned before.\nCaution But be warned, GnuCobol is not meant to be used for production systems. And if your code runs with GnuCobol, it does not mean that it also runs on commercial Cobol systems like Micro Focus COBOL or IBM COBOL or the other way around.\nAny comments about this article are welcome on on Twitter.\n Software-Development COBOL vscode Ubuntu Linux IDE Legacy","date":"30 October 2020","externalUrl":null,"permalink":"/en/blog/2020/2020-10-30-cobol-in2020-try-it-on-your-PC/","section":"Blog","summary":"COBOL (COmmon Business Oriented [programming] Language) dates back to 1959, and is still used in its realm of business software, mostly on mainframe computers. Of course, there are also many related software modernization and migration projects. Thus, once in a while even non-mainframe developers might need some COBOL knowledge. Commercial COBOL systems, especially on real mainframes, are expensive. In this article I demonstrate how to set up the GnuCOBOL compiler and integrate it with Visual Studio Code on Ubuntu Linux, but it also works on Windows and macOS. This way, experimenting with COBOL becomes easy and cheap.","title":"COBOL in 2020? Try it on your PC!","type":"blog"},{"content":"","date":"30 October 2020","externalUrl":null,"permalink":"/en/tags/IDE/","section":"Tags","summary":"","title":"IDE","type":"tags"},{"content":"","date":"30 October 2020","externalUrl":null,"permalink":"/en/tags/Legacy/","section":"Tags","summary":"","title":"Legacy","type":"tags"},{"content":"","date":"30 October 2020","externalUrl":null,"permalink":"/en/tags/Ubuntu-Linux/","section":"Tags","summary":"","title":"Ubuntu Linux","type":"tags"},{"content":"","date":"30 October 2020","externalUrl":null,"permalink":"/en/tags/vscode/","section":"Tags","summary":"","title":"Vscode","type":"tags"},{"content":"","date":"26 October 2020","externalUrl":null,"permalink":"/en/tags/AsciiDoc/","section":"Tags","summary":"","title":"AsciiDoc","type":"tags"},{"content":"","date":"26 October 2020","externalUrl":null,"permalink":"/en/tags/Blog/","section":"Tags","summary":"","title":"Blog","type":"tags"},{"content":"","date":"26 October 2020","externalUrl":null,"permalink":"/en/tags/JBake/","section":"Tags","summary":"","title":"JBake","type":"tags"},{"content":"When blogging about software development, it’s often helpful to include snippets of source code examples to the prosa. Even if we took these examples from working code, sooner or later they might break. E.g. a search \u0026amp; replace got applied to reserved words in the snippets. Such is very confusing for the readers because they want to learn from the examples, if these don’t even compile?\nA great practice is including the snippets automatically from real source code files. Then we can check these files for correctness using a compiler and unit tests. AsciiDoctor can do this with AsciiDoc documents, which are the source article format of my JBake blog.\nHere an example for what we want to achieve:\nfunction example(number1, number2) { return `${number1} + ${number2} = ${parseInt(number1) + parseInt(number2)}` } Let’s see in detail how it can be done with JBake.\nGiven Some Example-Source-Code # Given, we have some piece of working source code which we want to embed in a blog article as well as a unit test.\nthe whole source code file\nfunction example(number1, number2) { return `${number1} + ${number2} = ${parseInt(number1) + parseInt(number2)}` } // test cases assertEquals(\u0026#39;12 + 30 = 42\u0026#39;, example(12, 30)) // test helper // - check when using a unit test framwork becomes more appropriate! function assertEquals(expected, actual) { if (actual !== expected) { console.log(\u0026#39;example broken: \u0026#39; + __filename) console.log(` expected \u0026#39;${expected}\u0026#39;, but got: \u0026#39;${actual}\u0026#39;`) process.exit(1) } } As you can see, a kind of unit test is included in the source code. If things are getting more complicated, we might need a unit testing framework.\nTesting the Source Code # To test my code examples, I’ve created a batch job which simply executes the files:\nfind content/ -name \u0026#34;*.js\u0026#34; | xargs --max-lines=1 node This finds all Java-Script files below my content directory and runs node on each. For other programming languages, similar solutions can be applied.\nIncluding the Example Snippet into the Document # In my AsciiDoc document I only want to render the example snippet without the tags and without the test. Again, how it should look like:\nfunction example(number1, number2) { return `${number1} + ${number2} = ${parseInt(number1) + parseInt(number2)}` } Now see, how such can be accomplished:\nI put related files in sub-folders below my articles, e.g. img for images and src for source files. For example, this article is in content/blog/2020/2020-10-26-jbake-asciidoc-render-snippets-from-source-code.adoc, and the related source code from above is in content/blog/2020/2020-10-26-some-source-code.js.\nThis way I can include this file into my AsciiDoc-document with a relative path:\nAsciiDoc snippet to include the JavaScript example from above:\n[source,js] ---- include::src/2020-10-26-some-source-code.js[tag=exampleCode] ---- That’s all - happy blogging, with working source code snippets!\nMore information on these partial inclides can be found in the AsciiDoctor user manual.\nBy the way, even the AsciiDoc plugin for IntelliJ IDEA can display such includes and has autocompletion for the tag names.\n Blog JBake AsciiDoc","date":"26 October 2020","externalUrl":null,"permalink":"/en/blog/2020/2020-10-26-jbake-asciidoc-render-snippets-from-source-code/","section":"Blog","summary":"Using snippets from compilable source code files in prosa documents is a great approach. By doing so, I can make sure that the source code actually compiles. By adding unit tests, I can even make sure it does what it’s supposed to do.  I knew such is possible with AsciiDoc/AsciiDoctor, but does it also work if AsciiDoctor is run via JBake?","title":"Render Snippets from Source Code with JBake + AsciiDoc","type":"blog"},{"content":"Finally, I had enough time to migrate my website and blog from WordPress [1] to JBake [2], a static website generator written in Java. In this article I explain why I switched the technology and how I automatically migrated my contents — conversion-script included.\nWhy I Needed a New Website and Blog # Since GDPR [3] became effective, my website wasn’t a live version of WordPress anymore, instead it was just a static snapshot of the rendered contents. This way I avoided any legal problems e.g. with Cookies or fetching external resources initiated by WordPress or any of its plugins.\nThe downside was, writing new blog articles became very unhandy.\nAnother reason I badly wanted a new website, was its dated layout from over 10 years ago, see screenshot:\nFigure 1. my former homepage\nEven worse, its template was based on a very old WordPress which, after all these years, became less and less compatible. Where, on a large enough screen, it was just ugly, on smartphones or other smaller screens it was hard to read because it was not responsive at all. Especially source code formatting was hideous and needed some layout work:\nFigure 2. source code formatting problem\nWhy I chose JBake # Definitely, I wanted a static blog and website generator which fully supports AsciiDoc [4], my preferred markup language. JBake supports AsciiDoc out of the box, as well as raw HTML blog articles and Markdown [5]. It turned out that the HTML support came in handy for migrating my old blog articles from WordPress, see below for more information.\nAs Java is still my main programming language, JBake also looked like a good choice, just for the case I ever want to fix a bug or even add some feature.\nFurther, JBake supports layout languages which I am familiar with. Originally I wanted to use Thymeleaf [6] which I had already used in professional work; but as I’ve found a JBake template based on Freemarker [7] created by Manik Magar, which came very close to how I wanted my website, I chose his work [8] as the base for my layout.\nThe Migration # Initial Setup # At first, I upgraded some resource versions like for Fontawesome [9] of which I have a (currently perpetual) license, as well as some JavaScript libraries like jQuery ^[10] and highlight.js [11], for which I also switched to a theme with a bright background. Also, I self-hosted all resources, to avoid problems with GDPR by exposing my visitors request data to other companies.\nThen I made some amendments to the layout, e.g. moving a smaller version of the top menu into the sidebar, because the original appearance was too dominant for my likes, as well as some minor changes in the templates and CSS code. JBake supports custom metadata in contents, thus I’ve also added a summary field to some templates which even allows (some) HTML markup:\nFreemarker template snippet\n\u0026lt;#if (post.summary?has_content)\u0026gt; \u0026lt;p\u0026gt;${post.summary?replace(\u0026#34;\u0026amp;gt;\u0026#34;, \u0026#34;\u0026gt;\u0026#34;)?replace(\u0026#34;\u0026amp;lt;\u0026#34;, \u0026#34;\u0026lt;\u0026#34;)}\u0026lt;/p\u0026gt; \u0026lt;/#if\u0026gt; Unfortunately, JBake already encodes the HTML entities, and Freemarker has no unescape function. Thus, I had to use replace, which of course is a rather heuristic approach.\nYes, it would be much better to apply AsciiDoc-to-HTML to the custom field before it’s used, maybe a nice idea to improve JBake?\nWorkaround for Wrong Path in Rendered HTML # The original template which I’ve used rendered the most recent blog article on the homepage. At the time of the migration process, it was this article with the screenshots of my old website.\nUnfortunately, the paths of relative image URLs was wrong in the HTML because JBake, even though it was rendering /index.html was using the article path as a base, thus the images could not be found.\nAs a workaround, I changed the templated to show a preview of the first few articles, and thus no images paths needed to be converted.\nMigrating the Contents # While I manually migrated my page content like imprint, privacy-policy, my profile and project history with some slight amendments for the new website, I wrote a little Python script to export the blog articles and convert them to the format needed by JBake:\nwp-to-jbake.py\nimport pathlib import mysql.connector from mysql.connector.cursor import MySQLCursor def main(): try: db = mysql.connector.connect( host=\u0026#34;localhost\u0026#34;, user=\u0026#34;mih00_mh\u0026#34;, password=\u0026#34;4-wordPress\u0026#34;, database=\u0026#34;mih00_mh\u0026#34; ) cursor = db.cursor(dictionary=True) with open(\u0026#34;wp-export.sql\u0026#34;) as sqlFile: cursor.execute(sqlFile.read()) for row in cursor: export_post(row) cursor.close() finally: db.close() def export_post(row): post_id = row[\u0026#34;ID\u0026#34;] post_date = row[\u0026#34;post_date\u0026#34;].date() post_name = row[\u0026#34;post_name\u0026#34;] post_status = row[\u0026#34;post_status\u0026#34;] post_title = row[\u0026#34;post_title\u0026#34;] post_categories = row[\u0026#34;categories\u0026#34;] post_description = row[\u0026#34;description\u0026#34;] post_keywords = row[\u0026#34;keywords\u0026#34;] post_content = row[\u0026#34;post_content\u0026#34;] target_dir = f\u0026#34;content/blog/{post_date.year}\u0026#34; target_name = f\u0026#34;{post_date}-{post_name}.html\u0026#34; pathlib.Path(target_dir).mkdir(parents=True, exist_ok=True) with open(f\u0026#34;{target_dir}/{target_name}\u0026#34;, \u0026#34;w\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as target_file: # write redirect rules to stdout target_path = f\u0026#34;/{target_dir}/{target_name}\u0026#34; print(f\u0026#34;Redirect 301 /{post_date.year}/{post_date.month}/{post_date.day}/{post_name}$ {target_path}\u0026#34;) print(f\u0026#34;Redirect 301 /index.html@p={post_id}.html$ {target_path}\u0026#34;) # generate article file with metadata-header target_file.write(f\u0026#34;title={post_title}\\n\u0026#34;) target_file.write(f\u0026#34;date={post_date}\\n\u0026#34;) target_file.write(\u0026#34;type=post\\n\u0026#34;) if post_description is not None: target_file.write(f\u0026#34;summary={post_description}\\n\u0026#34;) if post_categories is not None: target_file.write(f\u0026#34;tags={post_categories}\\n\u0026#34;) if post_keywords is not None: target_file.write(f\u0026#34;keywords={post_keywords}\\n\u0026#34;) target_file.write(\u0026#34;status=published\\n\u0026#34;) target_file.write(\u0026#34;~~~~~~\\n\u0026#34;) target_file.write(\u0026#34;\\n\u0026#34;) target_file.write(post_content.replace(\u0026#34;\\r\u0026#34;, \u0026#34;\u0026#34;)) if __name__ == \u0026#34;__main__\u0026#34;: main() wp-export.sql\nSELECT DISTINCT ID, post_date, post_name, post_status, post_title, post_content, ( SELECT meta_value FROM wp_postmeta WHERE wp_postmeta.post_id = wp_posts.ID AND meta_key=\u0026#39;_amt_description\u0026#39; ) AS \u0026#34;description\u0026#34;, ( SELECT meta_value FROM wp_postmeta WHERE wp_postmeta.post_id = wp_posts.ID AND meta_key=\u0026#39;_amt_keywords\u0026#39; ) AS \u0026#34;keywords\u0026#34;, ( SELECT group_concat(wp_terms.name separator \u0026#39;, \u0026#39;) FROM wp_terms INNER JOIN wp_term_taxonomy ON wp_terms.term_id = wp_term_taxonomy.term_id INNER JOIN wp_term_relationships wpr ON wpr.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id WHERE taxonomy= \u0026#39;category\u0026#39; AND wp_posts.ID = wpr.object_id ) AS \u0026#34;categories\u0026#34;, ( SELECT group_concat(wp_terms.name separator \u0026#39;, \u0026#39;) FROM wp_terms INNER JOIN wp_term_taxonomy ON wp_terms.term_id = wp_term_taxonomy.term_id INNER JOIN wp_term_relationships wpr ON wpr.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id WHERE taxonomy= \u0026#39;post_tag\u0026#39; AND wp_posts.ID = wpr.object_id ) AS \u0026#34;tags\u0026#34; FROM wp_posts WHERE post_type = \u0026#39;post\u0026#39; AND post_status = \u0026#39;publish\u0026#39; ORDER BY post_date To run this script, you need to have the imported Python libraries installed, and most likely you need to use UTF-8; on Linux you can run it this way:\nPYTHONIOENCODING=utf-8 python3 wp-to-jbake.py The script will generate the blog article files including metadata headers below content/blog. It also prints redirect statements to stdout which you can add to your .htaccess file to redirect the former blog article paths to the new ones:\n.htaccess (example)\n... # redirects for URLs from my old website Redirect 301 /2008/6/28/jsf-mit-spring-webflow /blog/2008/2008-06-28-jsf-mit-spring-webflow.html Redirect 301 /index.html@p=74.html /blog/2008/2008-06-28-jsf-mit-spring-webflow.html Redirect 301 /2008/7/2/resource-bundles-automatisch-konvertieren /blog/2008/2008-07-02-resource-bundles-automatisch.html Redirect 301 /index.html@p=68.html /blog/2008/2008-07-02-resource-bundles-automatischkonvertieren.html ... Fixing Dead Links # After I had imported my old blog articles, I ran W3C Link Checker [12] which found many references to files in my former directory structure, as well as dead external links. So far I’ve only added documents from my former WordPress website, but there are still open issues. Where my older blog articles reference resources which are not available anymore, I’ll leave as it is.\nCaring about Security # Next, I’ve checked my website with a Web-Security Scanner [13] which found some issues which I could fix with some entries in my .htaccess:\n.htaccess\n# security configuration \u0026lt;IfModule mod_headers.c\u0026gt; Header always set Strict-Transport-Security \u0026#34;max-age=63072000; includeSubDomains\u0026#34; Header set X-XSS-Protection \u0026#34;1; mode=block\u0026#34; Header set X-Frame-Options \u0026#34;DENY\u0026#34; Header set X-Content-Type-Options \u0026#34;nosniff\u0026#34; Header set Referrer-Policy \u0026#34;same-origin\u0026#34; Header set Feature-Policy \u0026#34;geolocation \u0026#39;self\u0026#39;; vibrate \u0026#39;none\u0026#39;\u0026#34; Header set Content-Security-Policy \u0026#34;default-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; script-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; child-src \u0026#39;none\u0026#39;; img-src \u0026#39;self\u0026#39; data:\u0026#34; \u0026lt;/IfModule\u0026gt; Options -Indexes Depending on the libraries which you are using, you might need more open settings for Content-Security-Policy. Open your website from the real server, not just from the file-system, and check the console logs in the dev-tools of your browser for warnings like these:\nbrowser dev-tools console log example\nContent Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”). Content Security Policy: The page’s settings blocked the loading of a resource at data:image/svg+xml;charset=utf8,%3Csvg x… (“default-src”). More information about these headers you can find on htaccessbook.com [14]\nFor the case that you run into an HTTP code 500 error, or even better to avoid such, you can check your .htaccess on htaccesscheck.com [15].\nSome Final Polish # I used a Favicon Converter [16] to generate favicon.ico, android-chrome-*.png and apple-touch-icon.png files from an image of mine, as well as the site.webmanifest.\nThen I noticed that some words of my titles were too long, so the layout broke; the article got too wide, and the sidebar got pushed to the left outside of the visible area. I’ve fixed this issue by adding soft hyphens (\u0026amp;shy;) to long words in the metadata titles of my articles.\nFinally, I linked my error document for 404 errors:\n.htaccess\n... # redirect unknown URLs to the generated error page ErrorDocument 404 /404.html Open Issues # For sure, this website already looks much better than my over 10 years old layout, and it finally makes adding new blog articles easy again, thus *I considered it ready for release — *Bang!\nI am not done yet, though. But, is any website ever done?\nFirst, I need to go through all my old blog articles, add the summary and fix any layout problems.\nThen, I would like to keep the menu always on top, even on small screen devices; my amended menu, other than the menu in the original layout, should be small enough to fit even on a smartphone. I am also thinking about moving the rather long Tags section to a separate page with a button in the sidebar next to Blog Archive.\nAlso am I not yet happy with the performance, as you can see on Google PageSpeed Insights [17].\nThen I know of some glitches in the CSS, e.g. that the widths and positioning of sidebar and main column seem to depend on content, and thus sometimes jump when switching pages.\nFurthermore, I’ve many ideas for new features. For example, I would like to do something about multi-language. Currently, most of my website is purely in German, the same is true for older blog articles, only recent blog articles are in English. It would be too much work to translate all blog articles, but at least the website should eventually support both languages.\nReferences # [1] WordPress: https://wordpress.org/\n[2] JBake: https://jbake.org/\n[3] GDPR: https://en.wikipedia.org/wiki/General_Data_Protection_Regulation\n[4] AsciiDoc: https://asciidoc.org/\n[5] Markdown: https://de.wikipedia.org/wiki/Markdown\n[6] Thymeleaf: https://www.thymeleaf.org\n[7] Freemarker: https://freemarker.apache.org\n[8] Manik Mangar’s JBake template: https://github.com/manikmagar/jbake-future-imperfect-template\n[9] Fontawesome: https://fontawesome.com/\n[10] iQuery: https://jquery.com/\n[11] highlight.js: https://highlightjs.org/\n[12] W3C Link Checker: https://validator.w3.org/checklink\n[13] Web-Security Scanner: https://www.immuniweb.com/websec/\n[14] htaccessbook.com: https://htaccessbook.com/important-security-headers\n[15] htaccesscheck.com: http://www.htaccesscheck.com/index.html\n[16] Favicon Converter: hhttps://favicon.io/favicon-converter/\n[17] Google PageSpeed Insights: https://developers.google.com/speed/pagespeed/insights/?hl=de\u0026url=michael.hoennig.de\n blog JBake WordPress","date":"15 October 2020","externalUrl":null,"permalink":"/en/blog/2020/2020-10-16-website-and-blog-migration-from-wordpress-to-jbake/","section":"Blog","summary":"Finally, I had enough time to migrate my website and blog from WordPress to JBake, a static website generator written in Java. In this article I explain why I switched the technology and how I automatically migrated my contents – conversion-script included.","title":"Website and Blog Migration from WordPress to JBake","type":"blog"},{"content":"","date":"15 October 2020","externalUrl":null,"permalink":"/en/tags/WordPress/","section":"Tags","summary":"","title":"WordPress","type":"tags"},{"content":" seit 2026: Weiterentwicklung einer digitalen Plattform eines genossenschaftlichen Cloud-Providers # Branche: Internet-Hoster\nBesonderheiten: Keycloak/ReBAC-Integration\nTechnologien: Java 21 • Spring Boot 3 • Spring Framework 6 • JUnit • Mockito • ArchUnit • OpenAPI • WireMock • PostgreSQL 13 • PL/pgSQL • SQL • Liquibase • Git • GitEA • Gradle • IntelliJ IDEA • Werkator • Testcontainers • Docker • Keycloak • RBAC/ReBAC • Claude Code\nMethoden: Kanban • PullReqests/Code-Reviews • Agentic AI / Vibe-Engineering\n2025/2026: Entwicklung von Modulen im Bereich Payment-Reporting # Branche: Mobilität\nTechnologien: Kotlin • Kotlin Coroutines • WebFlux • Spring Boot 3 • Spring Framework 6 • JUnit 5 • Strikt • MockK • PostgreSQL • Flyway • Git • GitHub • GitHub Copilot • Gradle • IntelliJ IDEA • Embedded PostgreSQL (zonky.io) • Kubernetes • AWS • Microservices • OpenAPI (interne APIs) • Smithy (externe APIs)\nMethoden: API-First • PullReqests/Code-Reviews\n2024/2025: Entwurf und Entwicklung einer digitalen Plattform eines genossenschaftlichen Cloud-Providers # Branche: Internet-Hoster\nBesonderheiten: Legacy-Datenmigration\nTechnologien: Java 21 • Spring Boot 3 • Spring Framework 6 • JUnit • Mockito • ArchUnit • OpenAPI • WireMock • PostgreSQL 13 • PL/pgSQL • SQL • Liquibase • Git • GitEA • Gradle • IntelliJ IDEA • Jenkins • Testcontainers • Docker • RBAC/ReBAC\nMethoden: Kanban • PullReqests/Code-Reviews\n2022/2023: Weiterentwicklung und Betrieb eines B2B Seefracht-Informationssystem # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 8-17 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito\nPITest • OpenAPI • Angular • TypeScript • Git • Testcontainers • Docker • Jenkins • Gradle • WireMock • Rabbit MQ • IntelliJ IDEA • JIRA • Sonar • Kibana • Grafana • OpenShift • Oracle 12 SQL • H2 • Liquibase Methoden: Extreme-Programming (XP) • Mob-Programming • Pair-Programming • Kanban • CI/CD\n2022: Entwurf und Entwicklung einer dynamischen und hierarchischen Datenbank-integrierten Zugriffsrechteverwaltung (RBAC/ReBAC) # Branche: Internet-Hoster\nTechnologien: Java 17 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito • PITest • OpenAPI • ArchUnit • PostgreSQL 13 • pl/pgSQL • SQL • Liquibase Git • GitEA • Gradle • IntelliJ IDEA • Testcontainers • Docker\nMethoden: Kanban • PullReqests/Code-Reviews\n2021/2022: Entwicklung und Betrieb eines B2B eCommerce-System mit Hybrid-Mobile-App im intermodalen Speditionswesen # Branche: Logistik / intermodal\nTechnologien: Java 8 • Spring Boot 2 • Spring Framework 5 • JUnit • Mockito • PITest • Angular • TypeScript • Ionic • Oracle 12 SQL • H2 • Liquibase • Kibana • Grafana • Git • Jenkins • Gradle • RabbitMQ • IntelliJ IDEA • OpenAPI • JIRA\nMethoden: Extreme-Programming (XP) • Mob-Programming • Pair-Programming • Kanban • CI/CD\n2019/2020: Neuentwicklung eines B2B Kundenportals # Branche: Logistik / Schienengüterverkehr\nTechnologien: Angular 10 • TypeScript 3.x • jest • ts-mockito • Cypress • Kotlin • AWS Lambda/DynamoDB/SNS/SQS • Serverless Framework • Gitlab\nMethoden: Continuous Integration • Continuous Deployment • Scrum\n2018/2019: Weiterentwicklung eines B2B eCommerce-Systems # Branche: Logistik / Spedition / Luftfacht\nTechnologien: Java 8 • Spring Boot 1.5.x bis 2.1.x • Spring Framework 4.3.x bis 5.1.x • WebServices • Thymeleaf • GWT 2.7 • JUnit • Mockito • WireMock • TDD • BDD • DDD • JBehave • Selenium • Serenity • Selenide • PiTest • JavaScript • Vue.js • Oracle 11 SQL • Mob-Programming • Pair-Programming • git • Gradle • IntelliJ IDEA • Jenkins\nMethoden: Continuous Integration • Continuous Deployment • Kanban\n2017/2018: Microlearning Hybrid-App # Branche: eLearning (eigenes Projekt zum Zwecke der Weiterbildung)\nTechnologien: Angular 5.x • NgRX • IonicFramework 3.x • Ionic Native 4.x • Cordova 7.1 • TypeScript 2.4 • Event-Sourcing • npm • webpack • IntelliJ IDEA • git\n2017: Lead Developer / Framework-Technologien # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • Glassfish3.1 • JBoss AS EAP 7 • JUnit • Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • SonarQube • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Jira\n2011-2016: Entwicklung und Wartung eines Application-Frameworks # Branche: Logistik / Container-Seeschifffahrt\nTechnologien: Java 6+7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • OC4J • Glassfish 3.1 • JBoss AS EAP 6.4 • TIBCO • JUnit, Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Dynatrace\nseit 2014: Operatives Managementsystem # Branche: Maschinenbau\nStichworte: Java 7 • Swing • Nuclos OpenSource ERP (API) • Tomcat 7 • JUnit • Mockito • Git • Microsoft SQL Server 2012 • Eclipse IDE • Trello\n2011-2016: Entwicklung und Wartung eines Application-Frameworks # Branche: Logistik / Container-Seeschifffahrt\nStichworte: Java 6+7 • JavaEE (EJB3, CDI, JPA/EclipseLink, JMS, JSF/IceFaces) • JavaScript • OC4J • Glassfish 3.1 • JBoss AS EAP 6.4 • TIBCO • JUnit, Mockito • Cobertura • Arquillian • Git • Gerrit • Artifactory • Jenkins • Oracle 12 SQL • H2 • Gradle • ClearQuest • IntelliJ IDEA • Eclipse IDE • Dynatrace\n2011: Operatives Managementsystem für Containerlogistik # Branche: Logistik / Container-Seeschifffahrt\nStichworte: Java 6 • JavaEE (EJB3, JPA/EclipseLink, JMS, JSF/IceFaces) • OC4J • TIBCO • JUnit • ClearCase • Jenkins • Oracle 11 SQL • Maven • Eclipse IDE • ClearQuest\n2011: Kundenspezifische Anpassung eines online Shops # Meine Aufgaben: Programmierung • Test\nStichworte: Struts 1.2 • Spring 2.5 • JUnit • Hibernate • mySQL • Tomcat 6 • Eclipse IDE • Git • ant\n2010-2011: Funktions-/Leistungs- und Ertragsmonitoring von Solarkraftwerken # Branche: Energie/Photovoltaik\nStichworte: JEE mit JSF • Facelets • RichFaces • jQuery • Spring • Jersey REST • JUnit Hibernate • iBatis • PostgreSQL • Tomcat 6 • Eclipse IDE • SVN • Maven2 • FIT/Fitnesse • TDD\n2009-2010: GUI für Realtime-Sensorensystem # Branche: Meeresbodenforschung\nStichworte: Java 6 • Swing • Java 3D • Java 2D • JUnit • Jemmy • TCP/IP • Eclipse IDE • Netbeans Matisse • SVN • Maven2 • (ecl)EMMA • TDD • Kanban • C • Linux Kernel Driver (Parallel Port)\n2008-2009: Web-Basiertes POS System # Branche: Mobilfunk-Vertrieb\nStichworte: JEE mit JSF • Facelets • EJB3-Client • RichFaces • WebServices • JPA/Hibernate • Oracle 10g • Spring Web Flow • Spring • JUnit • Tomcat 6 • Eclipse IDE • SVN • Maven2 • EMMA • Hudson\n2007-2008, 2009: Web-basiertes, verteiltes Administrationswerkzeug # Branche: Internet / Webhosting\nStichworte: JEE mit JSF/Facelets • EJB3 Client+Server • JMS • JPA/PostgreSQL • JBoss AS mit Tomcat • SVN • Ant • Eclipse IDE • Cobertura • TDD\n1986 bis 2007: diverse Projekte und Festanstellung # Bezahlsystem: Java • JavaEE / EJB • Swing • JDBC/PostgreSQL\ndiverse Kleinprojekte: PHP • MySQL • PostgreSQL\nStarOffice/OpenOffice.org: C++, Java\nBranchensoftware-Systeme für Immobilienverwaltungen, Immobilienmakler, Ärzte und Reisebüros: TurboPascal • C • DBase • Clipper\nISAM-Server / Serieller Multiplexer: TurboPascal, C, x86 Assembler\nFür konkrete Projektangebote erhalten Sie mein vollständiges Profil gerne auf Anfrage.\n","date":"October 10, 2020","externalUrl":null,"permalink":"/de/projects/","section":"Michael Hönnig","summary":"Für konkrete Projektangebote erhalten Sie mein vollständiges Profil gerne auf Anfrage.","title":"Kundenprojekte","type":"page"},{"content":"While researching why so many people like Python, I found and article which showed how much more readable Python code is compared to Java code. I was curious how well Kotlin does that job.\nThe first job was to create an array of mixed data types and print these line by line. The Python and Java code can be found in Python vs Java: Which is best? Code examples and comparison for 2019 by Eric Goebelbecker. His Python code has 3 lines and 80 characters, his Java code has 8 lines and 221 characters. Here my Kotlin (actually kscript) code with 2 lines and 92 characters:\nval stuff = arrayOf(\u0026#34;Hello, World!\u0026#34;, \u0026#34;Hi there, Everyone!\u0026#34;, 6) stuff.forEach { println(it) } Where in this example, Python is much shorter than Java, Kotlin is not behind by much.\nThe second job was to create a function which reads a text file and returns a list consisting of strings each grouping 50 lines of the text file separated by commas. Eric’s Python code consists of 14 lines and 403 characters, his Java code of 18 lines with 641 characters, where the Python version is much easier to read as I have to confess.\nMy Kotlin code need 13 lines and only 356 characters:\nimport java.io.File fun getSymbols(filename: String): MutableList\u0026lt;String\u0026gt; { val records = mutableListOf\u0026lt;String\u0026gt;() for ((index, record) in File(filename).readLines().withIndex()) { if (index % 5 == 0) { records.add(record) } else { records[records.size - 1] += \u0026#34;,$record\u0026#34; } } return records } Because it’s just a function it needs no main-method in Kotlin, therefore the kscript version is the same here. Additionally, to me the Kotlin version is the easiest to read.\nI’ve to admit, though, that the Kotlin version keeps an extra copy of the data in memory because readLines(...) reads the whole file as List\u0026lt;String\u0026gt;.\nConclusion: For sure, as Python has many handy built-in functions and needs no type declarations, it’s sorter and more easy to read than Java. But Kotlin, especially in conjunction with kscript can compete very well.\n Software-Development Python Kotlin kscript","date":"20 July 2019","externalUrl":null,"permalink":"/en/blog/2019/2019-07-20-can-kotlin-be-as-short-as-python/","section":"Blog","summary":"While researching why so many people like Python, I found and article which showed how much more readable Python code is compared to Java code. I was curious how well Kotlin does that job.","title":"Can Kotlin or kscript be as short as Python?","type":"blog"},{"content":"","date":"20 July 2019","externalUrl":null,"permalink":"/en/tags/Kotlin/","section":"Tags","summary":"","title":"Kotlin","type":"tags"},{"content":"","date":"20 July 2019","externalUrl":null,"permalink":"/en/tags/kscript/","section":"Tags","summary":"","title":"Kscript","type":"tags"},{"content":"","date":"20 July 2019","externalUrl":null,"permalink":"/en/tags/Python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"Kotlin supports an array spread operator , similar to … in modern JavaScript versions like ES6 or the splat operator ** since Python 3.5, but it does not have such an object spread operator.\nThis article demonstrates how at least some uses cases for an object spread operator can be implemented with Kotlin.\nIf you are generally familiar with the spread operator, you can directly jump to An Object Spread Operator for Kotlin below.\nThe Object Spread Operator in ES6 # With the ES6 object spread operator it’s possible to combine the properties of multiple objects as well as single properties to a new object as shown in this example:\n// JavaScript needs property names const fullName = { first: \u0026#39;Henry\u0026#39;, last: \u0026#39;O\\\u0026#39;Conner\u0026#39; }; const streetAddress = { street: \u0026#39;Dog Basket\u0026#39;, city: \u0026#39;Canine Town\u0026#39;, zipCode: \u0026#39;K9T\u0026#39; }; const fullAddress = { …fullName, …streetAddress, planet: \u0026#39;Earth\u0026#39; }; The third line takes all properties from fullName and then all properties from streetAddress and finally adds the property for 'planet': 'Earth' to create a new object fullAddress. If streetAddress contained a property name which is also in fullName, the property from streetAddress would win as it comes further right in the parameter list.\nThere is also a similar syntax to combine arrays in ES6.\nThe Array Spread Operator in Kotlin # The Kotlin * operator is very similar to the ES6 array spread operator shortly mentioned above. In Kotlin it can be used either for spreading an array onto single vararg parameters:\n// given some method taking variable arguments fun someMethod(vararg values: Int) { // whatever it needs to do with the values } // and an array val arr = arrayOf(1, 2, 3) // then the array elements can be passed foo(*arr) This is similar to Java, where an array can be directly passed as varargs.\nBut the Kotlin syntax is more flexible than in Java and thus more like in ES6. It’s possible to spread multiple arrays and even add single values as shown here:\nval arr1 = arrayOf(\u0026#34;value 1\u0026#34;, \u0026#34;value 2\u0026#34;) val arr2 = arrayOf(\u0026#34;value 4\u0026#34;, \u0026#34;value 6\u0026#34;, \u0026#34;value 7\u0026#34;) someVarargsFunction(\u0026#34;value 0\u0026#34;, *arr1, \u0026#34;value 3\u0026#34;, *arr2) An Object Spread Operator for Kotlin # I found two options for implementing an object spread operator with Kotlin:\nUsing a Map as the actual property-store of the object and the by operator to create getters for each property.\nThis approach cannot be applied to data classes because it needs a primary constructor with a Map. Using extension functions to convert objects from and to Map instances.\nThis approach combines very well with data classes, but does not require those. In both cases, combining properties from multiple objects is done by combining the maps.\n1st Approach: Using a Map as the Actual Property-Store # The first approach to implement an object spread operator with Kotlin is based on classes which store their properties in a Map. Therefore, it needs access to the source code of the classes to which we want to apply it.\nclass FullNameMC(val map: Map) { var first: String by map var last: String by map // optional if you want simple direct construction from values // but it makes the map-based-class more verbose constructor(first: String, last: String) : this(mutableMapOf(\u0026#34;first\u0026#34; to first, \u0026#34;last\u0026#34; to last)) { } } Unfortunately this approach cannot be combined with data classes.\nBut because the map is the actual property store, it can be directly used:\n// create one example object from a map val map = mutableMapOf(\u0026#34;first\u0026#34; to \u0026#34;Henry\u0026#34;, \u0026#34;last\u0026#34; to \u0026#34;O\u0026#39;Conner\u0026#34;) val fullName = FullNameMC(map) // create another sample object by constructor val streetAddress = StreetAddressMC(\u0026#34;Dog Basket\u0026#34;, \u0026#34;Canine Town\u0026#34;, \u0026#34;K9T\u0026#34;) // combine the two with an additional property to a new object val fullAddress = FullAddressMC(fullName() + streetAddress() + (\u0026#34;planet\u0026#34; to \u0026#34;Earth\u0026#34;)) The Magic behind the () and + Operators # The last line of code might look like magic. If you have a closer look, it uses the Kotlin invoke operator (), which kinda replaces the ES6 …​ operator. And this () operator is overwritten to implement the magic:\noperator fun Any.invoke(): Map = javaClass.kotlin.memberProperties .filter { it.visibility == KVisibility.PUBLIC } .map { it.name to it.getter.call(this) } .toMap() Finally, the Kotlin + operator can be applied to the resulting maps as well as the additional property.\n2nd Approach: Converting Objects from and to Map Instances # But we would like an object spread operator which can be applied to any existing class, right? That’s possible with this second approach.\n// create one example object from a map val map = mutableMapOf(\u0026#34;first\u0026#34; to \u0026#34;Henry\u0026#34;, \u0026#34;last\u0026#34; to \u0026#34;O\u0026#39;Conner\u0026#34;) val fullName = FullNameDC::class.by(map) // create another sample object by constructor val streetAddress = StreetAddressDC(\u0026#34;Dog Basket\u0026#34;, \u0026#34;Canine Town\u0026#34;, \u0026#34;K9T\u0026#34;) // combine the two with an additional property to a new object val fullAddress = FullAddressDC::class.by( fullName() + streetAddress() + (\u0026#34;planet\u0026#34; to \u0026#34;Earth\u0026#34;)) The Magic of the by Function # ℹ️ See also The Magic behind the () an + Operators in the previous section.\nWe already know that the actual argument of the by function in the above example is a Map. As there is no by function in Kotlin classes, it’s of course an extension function.\nKotlin extension functions can be used wherever we import these. The following concrete approach needs all properties to be settable in the constructor. But many other implementation could be imagined, e.g. calling setters of the target class. Thus it should be possible to use this general approach for all existing classes, as long as these represent something like properties.\nHere one example implementation of such a by extension function:\nfun KClass.by(values: Map): T { val ctor = this.primaryConstructor!! val params = ctor.parameters.associateBy({ it }, { values.get(it.name) }) return ctor.callBy(params) } Where a normal function could also do the job, an extension function makes the code read more natural.\nThe magic relies in the associateBy call:\nIt iterates over all declared parameters of the constructor of the target class. For each declared parameter it associates it with the value of the respective value from the values-Map. The result is a Map\u0026lt;KParameter, Any?\u0026gt; which can be used to call the constructor. ⚠️ This implementation does simply ignore map elements which are not also properties of the underlying class. Though, a production-ready implementation of this extension function could implement a check and throw an exception.\nFinal Thoughts # The approach with the extension function for Kotlin classes using associateBy is more flexible because it can be applied to almost all existing classes which somehow represent properties. It’s only drawback is the little lengthy constructor call TargetClass::class.by(map); maybe one of my readers finds a way to write it as TargetClass.by(map) or similar, which does not need access to the target class source code.\n💡 Did you know that in Kotlin you can have default argument values and named arguments? E.g. FullAddressDC(city = \u0026quot;Canine Town\u0026quot;, zipCode = \u0026quot;K9T\u0026quot;, planet = \u0026quot;Earth\u0026quot;) These two features make object creation look similar to JavaScript and combine very well when dealing with properties in general.\nThis example also showed that Kotlin can do awesome things and Kotlin code can be almost as terse as JavaScript code. Try this with Java!\nThe source code can be found in my github repository.\nYou can also reply on Twitter.\nReferences on kotlinlang.org:\nDelegated Properties Extension Functions associateBy  Software-Development Kotlin kotlin object spread operator extension functions es6 javascript","date":"13 July 2019","externalUrl":null,"permalink":"/en/blog/2019/2019-07-13-an-object-spread-operator-for-kotlin/","section":"Blog","summary":"This article demonstrates how at least some uses cases for an object spread operator can be implemented with Kotlin.","title":"An Object Spread Operator for Kotlin","type":"blog"},{"content":"","date":"9 January 2019","externalUrl":null,"permalink":"/en/tags/Allgemein/","section":"Tags","summary":"","title":"Allgemein","type":"tags"},{"content":"","date":"9 January 2019","externalUrl":null,"permalink":"/en/tags/Haskell/","section":"Tags","summary":"","title":"Haskell","type":"tags"},{"content":"","date":"9 January 2019","externalUrl":null,"permalink":"/en/tags/Project-Euler/","section":"Tags","summary":"","title":"Project Euler","type":"tags"},{"content":"Recently I, mostly used to object oriented programming languages like C++ and Java) started learning Haskell to get some untainted impression of the functional programming style. Of course, I’ve used some functional programming in Java 8, JavaScript and Kotlin, but I wanted it pure to get new insights.\nAbout Haskell # Haskell[1] is a purely functional, lazily evaluated, strongly typed programming language with high use of type inference. In Haskell there are no explicit loops, no \u0026ldquo;gotos\u0026rdquo; and no modifiable variables (strict immutability[2]). Sounds rigid? That’s the challenge!\nAbout Project Euler # Then I’ve found Project Euler[3], a collection of programming problems ideal to learn new programming languages. So I started and implemented its first challenge using Haskell:\nProblem 001: Sum of Multiples[4] # If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.\nFind the sum of all the multiples of 3 or 5 below 1000!\n{-# LANGUAGE ExtendedDefaultRules #-} -- the pragma above is for some automatic type conversions module Main where import Data.List (nub) -- keeps only the first occurrence of each element import Text.Printf import System.IO -- found this solution by accident while looking up a function for \u0026#39;unique\u0026#39; in Haskell: simple_euler_001 = (sum . nub) ([3,6..999] ++ [5,10..999]) -- ======================================================== -- and here my solution: -- -------------------------------------------------------- javagil_euler_001 :: Int -\u0026gt; Int-\u0026gt; Int -\u0026gt; Int javagil_euler_001 n m below = sum $ nub $ (multiplesOfBelow n below) ++ (multiplesOfBelow m below) where multiplesOfBelow i below = [i,(2*i)..(below-1)] -- ======================================================== main :: IO () main = do printf \u0026#34;javagil Euler 001: for 3, 5 below 10) = %d (expected: 23)\\n\u0026#34; (javagil_euler_001 3 5 10) printf \u0026#34;javagil Euler 001: for 3, 5 below 1000) = %d\\n\u0026#34; (javagil_euler_001 3 5 1000) printf \u0026#34; simple Euler 001: for 3, 5 below 1000) = %d\\n\u0026#34; simple_euler_001 Isn’t the simple solution amazingly simple?\nSpeaking about my solution: Seems I over engineered by using a parameterized function instead of constants. Ok, for an excuse, I was not yet used to how Project Euler means it’s problem descriptions ;-=)\nLet’s point it out: The core of the solution, not counting the frame of the program like import and the printf is just 1 line of optional[5] type declaration and 3 lines of code.\n[1]: Haskell: https://www.haskell.org/\n[2]: Application state can only be kept either on the stack or outside of the Haskell world which is accessible through IO functions.\n[3]: Project Euler Problems: https://projecteuler.net/archives\n[4]: Project Euler Problem 1: https://projecteuler.net/problem=1\n[5]: Te type of javagil_euler_001 could be inferred automatically as done for simple_euuler_002.\n Allgemein Haskell Project Euler project euler problem 1 problem 001 haskell functional programming","date":"9 January 2019","externalUrl":null,"permalink":"/en/blog/2019/2019-01-09-project-euler-problem-001-with-haskell/","section":"Blog","summary":"Project Euler Problem 001 with Haskell. An exercise in Functional Programming.","title":"Project Euler Problem 001 with Haskell","type":"blog"},{"content":"","date":"1 December 2018","externalUrl":null,"permalink":"/en/tags/Best-Practices/","section":"Tags","summary":"","title":"Best Practices","type":"tags"},{"content":"","date":"1 December 2018","externalUrl":null,"permalink":"/en/tags/Book-Review/","section":"Tags","summary":"","title":"Book Review","type":"tags"},{"content":"\u0026ldquo;Java by Comparison\u0026rdquo; leads the reader through 70 examples into software-craftship. Each example compares a snipped of typical \u0026ldquo;unclean\u0026rdquo; code with a \u0026ldquo;clean\u0026rdquo; replacement, including a short discussion about the reasoning.\nWhat it is about? # This book is a great practical guide for Java developers comprising many aspects from the classical books \u0026ldquo;Clean Code\u0026rdquo; [1], \u0026ldquo;Refactoring\u0026rdquo; [2] and \u0026ldquo;Effective Java\u0026rdquo; [3]. With a length of only about 200 pages it is short enough, not to give any excuse for not having read it as a Java developer. The examples follow a didactically well done learning curve, and the explanations are written in an easy language [4]. The book ends with a chapter about general practices for contemporary software development like static code analysis, build automation and continuous integration.\nWho is it for? # I strongly recommend the book for:\nEverybody, who is learning Java programming and wants to learn it the professional way,\njunior Java programmers who want to take their next step and become a software craftsperson,\nsenior Java developers as a guide for code reviews and to lead junior Java developers,\nJava instructors as a foundation for a great curriculum.\nCan I recommend it? # Yes! To begin with, in general I agree with all the presented rules. Whenever there are multiple opinions out in the field or exceptions depending on the context, such is also discussed by the authors. I wished these topics were part of college curricula; unfortunately, at least in Germany, this is usually not the case. Great to learn about this extraordinary exception.\nEven for me, with \u0026gt;30 years of programming- and over 20 years of Java-experience, there were still plenty new details to learn.\nHighlights # One special highlight for me was the chapter \u0026ldquo;Let Your Data Flow\u0026rdquo;. Here the authors offer great rules for using the Java 8 functional programming features Lambdas and great rules about the use cases for Java 8 Optional.\nI also liked a lot that ADRs (architecture decision records) were mentioned: ADRs are schema for recording decisions about software architecture and their reasoning. I also like using this format for design decisions right in the code, as depicted in the chapter \u0026ldquo;Document Implementation Decisions\u0026rdquo;.\nExcursion: Exception handling in Java Streams # One example was keeping me busy for a while was \u0026ldquo;Avoid Exceptions in Streams\u0026rdquo;. I had not realized before how ugly Java streams can become if exceptions have to be handled and resources be closed as in Java’s \u0026ldquo;try with resources\u0026rdquo;. The authors show how such can be handled in a half-way decent way. If Java had an Error Handling Monad (compare [5]), it could look somehow like this:\nclass LogBooks { static List\u0026lt;LogBook\u0026gt; getAll() throws IOException { return Try.with(Files.walk(Paths.get(\u0026#34;/var/log\u0026#34;))) .filter(Files::isRegularFile) .filter(LogBook::isLogbook) .flatMap(path -\u0026gt; Try.of(new LogBook(path))) .onException( IOException.class, (exception, path) -\u0026gt; Try.empty()) .collect(Collectors.toList()); } } Unfortunately, there is no such standard API for Try/catch in standard Java-Streams, just in libraries like VAVR [6].\nWhat could be improved or is missing? # In general the code formatting was fine, other than stated in on books review on Amazon [7]. But yet, on my eBook readers (Android Kindle app on Samsung Galaxy Note 12 tablet and Kindle Oasis reader) the lines were sometimes too long. Therefore, such lines continued at the beginning of the next line which makes some code snippets hard to read. Explicit line breaks would have helped.\nDoes the book contain a complete list of rules for clean code? Of course, it does not, it cannot, and I was not expecting such, there are simply too many of such (compare [8]) Yet, I was missing a few principles which I consider very important:\na chapter about access rights restriction (private/protected/package/public), encapsulation, coherence and false abstractions,\nthe SOLID principles: Single responsibility, Open/closed, Liskov substitution, Interface segregation, Dependency inversion\nand even though SLA (level of abstraction) is mentioned in a few examples, I think it deserves a chapter of its own.\nI also suggest applying the stated rules in later chapters, e.g. using final more often. Too bad it’s not the default in Java anyway.\nMaybe these are some ideas for a 2nd edition?\nInspiration # While reading the book, I was wondering how the rules apply to other programming languages, e.g. Kotlin or TypeScript. As both type systems are more advanced, some rules simply become irrelevant, but for sure, other rules could come up. Especially in purely functional programming languages a set of such rules would be quite different.\nSummary # In total, this book is a must-read for every Java developer to go beyond the junior status, and a great reference for those who have already advanced. With a length of only about 200 pages, there is also no excuse for not having read it.\nDisclaimer: I got a free copy for review.\nReferences # [1] Clean Code: A Handbook of Agile Software Craftsmanship, 1st Edition, Robert C. Martin, 2008, Prentice Hall\n[2] Refactoring: Improving the Design of Existing Code, 2nd Edition, Martin Fowler, 2018, Addison-Wesley Professional\n[3] Effective Java, Third Edition, Joshua Bloch, 2017-12-27, Addison-Wesley Professional\n[4] http://www.hemingwayapp.com/\n[5] https://de.slideshare.net/mariofusco/monadic-java/39\n[6] http://www.vavr.io/\n[7] https://www.amazon.de/gp/customer-reviews/R1CB66452UHK9Y/ref=cm_cr_arp_d_rvw_ttl?ie=UTF8\u0026ASIN=1680502875\n[8] http://principles-wiki.net/\n Software-Development Software-Craftship Java Book Review Best Practices","date":"1 December 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-11-24-Book-review-Java-by-Comparison/","section":"Blog","summary":"“Java by Comparison” leads the reader through 70 examples into software-craftship. Each example compares a snipped of typical “unclean” code with a “clean” replacement, including a short discussion about the reasoning.","title":"Book Review: Java by Comparison: Become a Java Craftsman in 70 Examples","type":"blog"},{"content":"","date":"1 December 2018","externalUrl":null,"permalink":"/en/tags/Software-Craftship/","section":"Tags","summary":"","title":"Software-Craftship","type":"tags"},{"content":"","date":"26 May 2018","externalUrl":null,"permalink":"/en/tags/Linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"26 May 2018","externalUrl":null,"permalink":"/en/tags/Shell/","section":"Tags","summary":"","title":"Shell","type":"tags"},{"content":"Ever had a long-running find/xargs pipe running in a Linux bash shell and you wished you could have a look inside how far it has progressed? Here is a little bash shell code snipped makes this possible.\nThe Story Behind # Up until recently I still had one quite old external HDD as my main backup storage, other than the other two backup storages for my 3-2-1 backup strategy, it did also include videos, photos and my MP3 collection and thus it was pretty huge. But also different from the others, it was not yet encrypted. So, I used rsync to copy all these files to a new external HDD with encryption. To be honest, I did not trust myself if I had used rsync correctly and wanted a second \u0026lsquo;opinion\u0026rsquo; whether the copy is really complete.\nThus, I started some naive little script to compare all files from the old HDD with their counterpart of the new HDD - it ran endless (days!), and it was still logging out more file names, thus still running. I just had no idea how far it had progressed and how much time I still have to wait. Thus, I interrupted it, to find a more efficient way for the task. This time, I also wanted to watch the progress.\nThe Problem # Wouldn’t it be cool having something like this?\nfind ... | xargs --show-progress-bar ... output\n78.0 B 0:00:27 [2.98 B/s] [==============\u0026gt; ] 51% ETA 0:00:25 Unfortunately this option does not exist in xargs [1]. And I was really surprised not finding a workaround on the Internet. Bits and pieces, yes, also some solutions for special cases, but which I was not able to use. Thus, I did put together these bits and pieces.\nThe Solution # I came up with something resembling the following little bash shell script snippet:\nENTRIES=`mktemp --tmpdir entries.XXXXX`; echo \u0026#34;entries file: $ENTRIES\u0026#34; ERRORS=`mktemp --tmpdir errors.XXXXX`; echo \u0026#34;errors file: $ERRORS\u0026#34; RESULT=`mktemp --tmpdir result.XXXXX`; echo \u0026#34;result file: $RESULT\u0026#34; echo -n \u0026#34;collecting files ...\u0026#34; find {YOUR-CRITERIA} -print0 \u0026gt;$ENTRIES let COUNT=`tr \u0026#39;\\0\u0026#39; \u0026#39;\\n\u0026#39; \u0026lt;$ENTRIES | wc -l` echo -e \u0026#34;\\b\\b\\bfound $COUNT entries, processing:\u0026#34; sort -z \u0026lt;$ENTRIES | xargs -r0 -L{YOUR-CHUNKSIZE} sh -c \u0026#39;{YOUR-COMMAND} \u0026#34;$@\u0026#34; \u0026gt;\u0026gt;\u0026#39;$RESULT\u0026#39; 2\u0026gt;\u0026gt;\u0026#39;$ERRORS\u0026#39;; printf %$#s\u0026#39; arg0dummy | pv --progress --timer --eta --rate --bytes --size $COUNT \u0026gt;/dev/null cat $ERRORS \u0026gt;\u0026amp;2 cat $RESULT rm $ENTRIES $ERRORS $RESULT (I give this code to the public domain. Links to my website michael.hoennig.de or mentioning my Twitter handle @javagol are welcome.)\nPlease notice the parameterization (replace including the braces, these are placeholders, not variables):\n{YOUR-CRITERIA}: any find criteria you need to apply {YOUR-CHUNKSIZE}: the number of entries to be processed at once {YOUR-COMMAND}: whatever you need to process\n(receives the entries chunk-by-chunk as arguments) An Executable Example # Wanna see it in action? Then open your bash, change to a directory which contains some thousand files and execute these commands:\nENTRIES=`mktemp --tmpdir entries.XXXXX`; echo \u0026#34;entries file: $ENTRIES\u0026#34; ERRORS=`mktemp --tmpdir errors.XXXXX`; echo \u0026#34;errors file: $ERRORS\u0026#34; RESULT=`mktemp --tmpdir result.XXXXX`; echo \u0026#34;result file: $RESULT\u0026#34; echo -n \u0026#34;collecting files ...\u0026#34; find . -type f -perm -g+r -print0 \u0026gt;$ENTRIES let COUNT=`tr \u0026#39;\\0\u0026#39; \u0026#39;\\n\u0026#39; \u0026lt;$ENTRIES | wc -l` echo -e \u0026#34;\\b\\b\\bfound $COUNT entries, processing:\u0026#34; sort -z \u0026lt;$ENTRIES | xargs -r0 -L100 sh -c \u0026#39;md5sum \u0026#34;$@\u0026#34; \u0026gt;\u0026gt;\u0026#39;$RESULT\u0026#39; 2\u0026gt;\u0026gt;\u0026#39;$ERRORS\u0026#39;; printf %$#s\u0026#39; processing | pv --progress --timer --eta --rate --bytes --size $COUNT \u0026gt;/dev/null cat $ERRORS \u0026gt;\u0026amp;2 cat $RESULT rm $ENTRIES $ERRORS $RESULT It will calculate the MD5 checksums of all the files in the directory while showing a progress bar.\nHow it Works - A Summary # After some initialization it uses find to create a list of entries to process and stores these in a file. You could also use an environment variable, but in my case the environment space was exceeded (I got \u0026ldquo;Argument list too long\u0026rdquo; on subsequent commands), thus I used a temporary file.\nThe next step is to determine the number of entries to process to get some sensible progress bar. If the time to process a single item has a huge variation, like in my case due to the size of the processed files, the progress might still not be totally accurate, but as long as there are not very few files taking most of the processing time, this should be a good compromise.\nNow comes the main step: The list of entries is processed in chunks using xargs. I chose a chunked approach, because if I had passed all or too many arguments at once, the progress bar might just go from 0% to 100% in a glimpse at the end. Pay attention to the quoting in this line, it is very important, otherwise the variables with the file names cannot be resolved properly.\nThe last step is to print any errors and the processing result and to clean up.\nThis is still a bit overwhelming? No problem, I am going to explain it step by step.\nHow it Works - In Detail # Initialization # ENTRIES=`mktemp --tmpdir entries.XXXXX`; echo \u0026#34;entries file: $ENTRIES\u0026#34; ERRORS=`mktemp --tmpdir errors.XXXXX`; echo \u0026#34;errors file: $ERRORS\u0026#34; RESULT=`mktemp --tmpdir result.XXXXX`; echo \u0026#34;result file: $RESULT\u0026#34; This creates three unique files in the systems temp-file folder (usually /tmp), one for the list of entries to be processed, one for the error output of the actual processing and one of the processing result. The processing errors and results are not printed immediately because that would destroy the progress bar visualization. But as the snipped prints the filenames at the beginning, you can always peek into from another shell (tail -f ...)\nCollecting the Entries to Process # echo -n \u0026#34;collecting files ...\u0026#34; find {YOUR-CRITERIA} -print0 \u0026gt;$ENTRIES This line creates a list of entries to processed (file and directory names) and stores these in a temporary file. It separates each entry with a null-character to avoid any problems with whitespace in file names.\nAs this find alone can take quite some time, and echo statements informs the user that some pre-processing is going on. The \u0026ldquo;-n\u0026rdquo; means not to end with a line break, just for some little effect in the next step.\nBecause it’s not known how many entries there are to be processed, no progress bar could be shown for this step anyway. One improvement could show some other kind of indicator, like a spinning wheel, but for new I’ve skipped this optimization.\nDetermining the Number of Entries to Process # let COUNT=`tr \u0026#39;\\0\u0026#39; \u0026#39;\\n\u0026#39; \u0026lt;$ENTRIES | wc -l` echo -e \u0026#34;\\b\\b\\bfound $COUNT entries, processing:\u0026#34; The command wc (word count) is used to determine the number of entries to process. The option \u0026ldquo;-l\u0026rdquo; means to count just the lines, but as the entries are separated by null-characters and not by newlines, the null-characters have to be converted to line breaks first by using tr.\nNext the number of entries to be processed is printed. The \u0026ldquo;-e\u0026rdquo; makes echo interpret the \u0026ldquo;\\b\u0026rdquo; (backspace) to remove the three dots from the previous echo statement.\nProcessing the Entries in Chunks # sort -z \u0026lt;$ENTRIES | xargs -r0 -L{YOUR-CHUNKSIZE} sh -c \u0026#39;{YOUR-COMMAND} \u0026#34;$@\u0026#34; \u0026gt;\u0026gt;\u0026#39;$RESULT\u0026#39; 2\u0026gt;\u0026gt;\u0026#39;$ERRORS\u0026#39;; printf %$#s\u0026#39; arg0dummy | pv --progress --timer --eta --rate --bytes --size $COUNT \u0026gt;/dev/null The sort step is optionally, I added it because it’s a bit tricky if you need the input sorted. In my special case I actually needed it because I wanted to compare two runs of such a process on two distinct directory trees which resembled each other (original and copy). The \u0026ldquo;-z\u0026rdquo; means not to sort lines, but null-characters separated entries.\nNext, the entries are processed using xargs chunk by chunk, each with the given size. Depending on IO-buffering and how your command works, chunking is necessary to give some minimum granularity for the progress bar. Maybe your processing command even needs a chunk size of 1 because it can only take one entry at the time.\nNext we need a trick to show progress chunk-by-chunk and also do the actual processing chunk-by-chunk. In my case, I could easily use the output of my actual processing as base for progress because it was 1:1 for each input entity. But to have a more general solution, this snipped does not rely on such special conditions. Instead it passes the chunk entities go en embedded shell script which does two things:\nit executes your actual processing command and it’s out\nit prints the as many spaces as there are entities in the chunk\n(keep in mind, the last chunk might be shorter than the chunk size)\nThe arg0dummy at the end of the shell command is used as $0 by the subshell. It’s not actually used, but if we omit it, the first entry of each chunk would be used as such and thus neither get processed nor count for the progress bar.\nFinally, and this is the core of the whole thing, a progress bar is displayed based on the output of xargs. It receives the number of entries as a size to process via the \u0026ldquo;--size\u0026rdquo; option and stores the actual stdout and stderr to files to not to destroy the progress bar with other output. Look up the meaning of the other options with man pv if you are interested.\nThe pv output looks like this example:\n`78.0 B 0:00:27 [2.98 B/s] [===============\u0026gt; ] 51% ETA 0:00:25 It means, it has already processed 87 entries and is running for 27 seconds so far. In average, it processed 2.98 entries per second, so far. It has already processed 51% of the entries, and it estimates to run for another 25 seconds. The value for the estimated remaining time can go up once in a while if some entries need much more time than others so far.\nYou can replace --eta by --fineta to the pv command to show the estimated end time (according to the timezone of your environment) instead of the estimated remaining duration.\nFinishing Up # cat $ERRORS \u0026gt;\u0026amp;2 cat $RESULT rm $ENTRIES $ERRORS $RESULT Finally, we print out the errors and results from the actual processing and remove the temporary files.\nDownsides, Alternatives and Comments # I see some downsides of this solution:\nIf your computer suspended during the process, the bad news is that the estimated remaining duration is usually way off.\nEncapsulating this snipped into a reusable script would make the quoting even more complicated, thus I did not bother doing so for this blog article. But if somebody has frequent use for it, it’s doable for sure.\nIt still lacks some progress indicator for the time needed to collect the entries to process.\nIn some cases, an alternative could be GNU Parallel [3], which has a progress bar included. But its main job is parallel execution of tasks, which is sometimes not wanted. In many other cases you can use \u0026lt;PV [2] just as it is and don’t need these tricky fixture.\nAny comments? Then please drop me a note by email or on Twitter!\nReferences # [1] https://www.gnu.org/software/findutils/manual/html_node/find_html/Invoking-the-shell-from-xargs.html\n[2] http://www.ivarch.com/programs/pv\n[3] https://www.gnu.org/software/parallel/\n Linux Shell linux shell snipped script find xargs progress-bar progress","date":"26 May 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-05-26-showing-a-progress-bar-for-a-long-running-find-xargs-linux-bash-shell-pipe/","section":"Blog","summary":"Wouldn’t it be cool having something like find ... | xargs -L100 --show-progress-bar ...? Unfortunately this feature does not exist in xargs. This article explains how the same result can be achieved by other means.","title":"Showing a progress bar for a long running find/xargs Linux bash shell pipe","type":"blog"},{"content":"","date":"1 May 2018","externalUrl":null,"permalink":"/en/tags/GDPR/","section":"Tags","summary":"","title":"GDPR","type":"tags"},{"content":"While amending my website for GDPR compliance, I noticed how dated it really is. Thus in the background I am working on a new solution.\nThe Status-Que: Dated and Rotten # My current website is based on WordPress and while the software itself has a great update system and thus the software is up-to-date, my template is not. This template is based on some ancient WordPress version and as I had it highly customized, most current plugins wont work anymore, e.g. the Cookie-banner (but as I removed all Cookies, this is not a real problem).\nWorse is that modern code highlighting plugins don’t work, the CSS simply screws up. Layout is a big problem anyway, as the site is not very responsive, thus not mobile friendly. Also the layout of articles is really bad, e.g. the interleaf lead is often completely off or code lines are getting too long.\nFurthermore, since my GDPR related changes, in some parts the layout broke completely and I had to turn off some side banners. Also the navigation broke and to be honest, I just don’t want to maintain this old crap anymore.\nAnd last but not least, I don’t like writing articles in HTML anymore. AsciiDoc would be so much nicer.\nWhat’s Growing in the Background # Therefore I am about to give up on WordPress and already working on a new implementation, based on JBake with AsciiDoc and Thymeleaf templates and rather write less articles for a while to get some more time for the new implementation.\n WordPress GDPR Blog","date":"1 May 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-05-01-working-on-renewal-of-my-dated-website-and-blog/","section":"Blog","summary":"While amending my website for GDPR compliance, I noticed how dated it really is. Thus in the background I am working on a new solution.","title":"Working on Renewal of my Dated Website and Blog","type":"blog"},{"content":" Imprint # Michael Hönnig\nBürgermeister-Bolt-Straße 50\nD-26424 Hage\nGermany\nTelephone: +49 1522 1001122\nE-mail: michael@hoennig.de\nVAT ID: DE217249198\nContact and professional profiles # Best reached by e-mail: michael@hoennig.de.\nProfessional profiles: LinkedIn, Xing, GitHub, X.\nThe legally binding version of this imprint is the German one.\n","date":"29 April 2018","externalUrl":null,"permalink":"/en/imprint/","section":"Michael Hönnig","summary":"Legal information (imprint) for this website, contact and professional profiles.","title":"Imprint","type":"page"},{"content":"Wow, this amazing 34 hours video course Spring Framework 5: Beginner to Guru (with SpringBoot 2.0) is really quite massive! Learning almost full time, it took me a little over 2 weeks to work through. Ok, I admit, I only really completed ~99% because I skipped a few cookbook goodies which are currently completely irrelevant for me.\nWhat\u0026rsquo;s contained? # The main part of this course covers the following Spring modules:\nSpring Boot 2 with Spring Framework 5 Spring Data JPA with Hibernate, MongoDB and DataJpaTest Spring MVC with Thymeleaf + MockMVC + WebTestClient Spring WebFlux (Reactive Programming) RESTful Webservices with Spring MVC + Spring WebFlux\ninkl. using Swagger for REST API documentation Spring Security Additonally there are plenty of extras and cookbook lessons for:\nMySQL, Postgres, MariaDB, DB2, Oracle MongoDB, Redis, Cassandra, Neo4J Content Negotiation in Spring REST API JMS Messaging, RabbitMQ and RabbitMQ Project Lombok and MapStruct using SpringBoot with Maven + Gradle Docker, also used for the databases and message systems For the most part John (the author) is using Test Driven Development (TDD) with JUnit 4 and Mockito and even uses Continuous Integration Builds (with CircleCI). And the source code of all lessons is available on his GitHub account. Many lessons end with Little Quizzes to repeat the learned topics.\nWhat\u0026rsquo;s missing or could be improved? # There are still one or two guys from a tech support team who answer questions, but John did not answer any questions anymore since October 2017. Considering the low price, especially on an Udemy sale, I can well understand that. Unfortunately that leaves some \u0026ldquo;why\u0026rdquo; questions open, which are often impossible to answer by others. But there is also a private Slack channel, where John is active (invitation in the course). Also many students answered questions of co-students, as I did as well. I think that\u0026rsquo;s also a good reflection about what I know and and what I\u0026rsquo;ve missed.\nFor most features John first shows how something works, then there is an assignment where the students have to try it by themselves. The solution is in almost all cases completely analogous to what was presented right before. For unexperienced developers this might be a great opportunity - unless you don\u0026rsquo;t simply do copy+paste+amend ;-) But for experienced developers this can become a bit lengthy and boring. What I prefer instead is to solve problems, e.g. get a failing JUnit test and fix the code or add a little feature by own research. But this might be a personal preference.\nBy the way, you learn a lot more if you apply the learned topics right away in a little side project instead of an artificial teaching project. See also my blog article about Learning software technologies (and others topics) with Udemy. But there is also some risk in early application of newly learned topics in a side project - I might write ab blog article about that risk too in the future.\nWhat have I learned? # Having used (the \u0026ldquo;original\u0026rdquo;) JEE for the last years and being out of Spring for about 7 years (my last version was Spring 2.5 with which annotation configuration was introduced), this course was a great refresh of the core and some of the modules and a good introduction to the other Spring modules as well as some additional technology. Most of the Spring Framework is really easy to grasp with JEE experience.\nThis course it will also remain a great source to look up if need comes up in real life projects. For sure, I am not as good with Spring yet as with JEE, but working through it made me confident to be able to get to the same level quite soon after actually using it on a daily basis.\n Software-Development Java","date":"2 April 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-04-02-course-review-spring-framework-5-beginner-to-guru-udemy/","section":"Blog","summary":"Wow, this amazing 34 hours video course Spring Framework 5: Beginner to Guru (with SpringBoot 2.0) is really quite massive! Learning almost full time, it took me a little over 2 weeks to work through. Ok, I admit, I only really completed ~99% because I skipped a few cookbook goodies which are currently completely irrelevant for me.","title":"Course Review \"Spring Framework 5: Beginner to Guru\" (Udemy)","type":"blog"},{"content":"For my new freelance project I need to carrying my notebook PC around to and from the clients project office. Unfortunately my business notebook PC is too clumsy (16\u0026quot;), thick (4cm) and heavy (~2.2kg) for such. Thus, I was looking for a new ultra light notebook PC which is still well usable for software development (mostly Java with Eclipse IDE or IntelliJ IDEA). I came up a wish-list and did some research.\nWhat I would like to get: # about 15\u0026quot; display, non-glare/matt, 4:3 aspect ratio Intel i7 CPU or similar, Quad-Core 16 GB RAM \u0026gt;500 GB SDD Ethernet, HDMI, USB 3.0 and USB C, min. 802.11n WiFi centered main keyboard (rather no numeric block),\nsuited for fast typing, ideally with backlight single-touch pad (multi touch is still a horror with Linux) Windows 10 Pro (which I need for this project)\nbut ideally also capable of running Linux silent and low pitch, ideally fan-less Kensington Lock mount \u0026lt;1.5 kg \u0026lt;2000€ What I don\u0026rsquo;t need: # I do not need:\nan ultra HD screen resolution a touch screen a gaming graphic chip a long lasting internal (external extra battery would be great) a numeric keypad an internal optical drive (of cause not for that weight requirement) What I found: # I found few gadgets which came at least close:\nLenovo ThinkPad T470\nunfortunately just 14\u0026quot; display Asus ZenBook Pro 15,6\u0026quot; Ultra-HD\na bit too heavy (1.8kg) and just mediocre keyboard Dell XPS 15 Infinity Edge 9560-4575 39,62 cm (15,6 Zoll FHD)\na bit too heavy (1.8kg) and unpleasant fan noise Acer Swift 3 SF315-51G-73UX 39,6 cm (15,6 Zoll Full-HD IPS) Ultrabook\na bit too heavy (1,9 kg), glossy display and not sure about the keyboard Not being sure if I will be able to use an external monitor and keyboard in the clients office, I was not yet able to make a final decision.\nWhat I am using now: # Luckily I was able to postpone my decision: In parallel to my research about a new notebook PC, I was trying to reactivate my somewhat aged Samsung 900X4C (15\u0026quot; matt, 1.5cm, 1.6kg) - unfortunately with just 8 GB RAM and Dual-Core, an old 256 GB SDD and a broken hinge, still usable on a stable surface though. I successfully updated it from Windows 8 Pro to Windows 10 Pro. Seems I can stay with this gadget for at least some weeks in my new project and thus give me some time for a proper decision. If you know about a gadget which I\u0026rsquo;ve missed and which matches my criteria, please drop me a note by email or on Twitter!\n Allgemein","date":"30 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-30-my-wish-list-for-a-lightweight-but-powerful-notebook-pc/","section":"Blog","summary":"For my new freelance project I need to carrying my notebook PC around to and from the clients project office. Unfortunately my business notebook PC is too clumsy (16\"), thick (4cm) and heavy (~2.2kg) for such. Thus, I was looking for a new ultra light notebook PC which is still well usable for software development (mostly Java with Eclipse IDE …","title":"My Wish-List for a Lightweight but Powerful Notebook PC","type":"blog"},{"content":"Recently I ran across a missing method=\u0026quot;post\u0026quot; in a \u0026lt;form ...\u0026gt; tag within a Thymeleaf template which was working despite an @PostMapping annotation of the SpringFramework on the controller method. I was wondering how this could happen and did some investigation. Hopefully this article gives a better chance for the next one who runs into this problem.\nThe method looked like this:\n@PostMapping @RequestMapping(\u0026#34;something\u0026#34;) public String saveOrUpdate(@ModelAttribute SomethingCommand command){ SomethingCommand savedCommand = [...]; return \u0026#34;redirect:/something/\u0026#34; + savedCommand.getId() + \u0026#34;/show\u0026#34;; } By the way, even after adding the method=\u0026quot;post\u0026quot; to the form, the method was still called.\nFinding the Cause # First I verified that there there no filter configured which adds this attribute, but the HTML source in the browser did not contain this attribute either. Then I checked the variables in the call stack to the controller method in a debugger and I could clearly see, that it was really called with a GET request. Next, I had a look at the JavaDocs, but I could not find anything relevant. Of cause I also did a web search and still no result.\n@PostMapping is actually a shortcut for @RequestMapping( method = RequestMethod.POST, ...), but which was also annotated at the same method and thus caused confusion.\nHow it Actually Works # Then I tried various annotations on the saveOrUpdate() method and found the results shown in the comments:\n// becomes effectively a mapping for \u0026#34;something\u0026#34; with any submit method, // thus a @RequestMapping after a @PostMapping gets precedence over the @PostMapping @PostMapping @RequestMapping(\u0026#34;something\u0026#34;) public String saveOrUpdate(@ModelAttribute SomethingCommand command){ long id = callSomethingService(command); return \u0026#34;redirect:/something/\u0026#34; + id + \u0026#34;/show\u0026#34;; } // becomes effectively a mapping for \u0026#34;somethingReverse\u0026#34; with any submit method, // thus, @PostMapping after a @RequestMapping has no effect. // If same path is used as in the previous method, Spring notices the double mapping and complains: // \u0026#34;java.lang.IllegalStateException: Ambiguous mapping.\u0026#34; @RequestMapping(\u0026#34;somethingReverse\u0026#34;) @PostMapping(\u0026#34;otherthingReverse\u0026#34;) public String saveOrUpdateReverse(@ModelAttribute SomethingCommand command){ long id = callSomethingService(command); return \u0026#34;redirect:/something/\u0026#34; + id + \u0026#34;/show\u0026#34;; } // maps only POST requests, is not called for other submit methods, // just to demonstrate that the submit method really makes a difference @PostMapping(\u0026#34;something\u0026#34;) public String saveOrUpdateByPost(@ModelAttribute SomethingCommand command){ long id = callSomethingService(command); return \u0026#34;redirect:/something/\u0026#34; + id + \u0026#34;/show\u0026#34;; } // maps only GET requests, is not called for other submit methods, // just to demonstrate that the submit method really makes a difference @GetMapping(\u0026#34;something\u0026#34;) public String saveOrUpdateByGet(@ModelAttribute SomethingCommand command){ long id = callSomethingService(command); return \u0026#34;redirect:/something/\u0026#34; + id + \u0026#34;/show\u0026#34;; } For the investigation I used this JUnit test utilizing Mockito and MockMvc:\n[...] import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; public class SomethingControllerTest { private SomethingController controller; private MockMvc mockMvc; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); controller = new SomethingController(); mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @Test public void testPostRequest() throws Exception { SomethingCommand command = new SomethingCommand(); mockMvc.perform(post(\u0026#34;/something\u0026#34;) // vary post/get and path here .contentType(MediaType.APPLICATION_FORM_URLENCODED) .param(\u0026#34;id\u0026#34;, \u0026#34;\u0026#34;) .param(\u0026#34;title\u0026#34;, \u0026#34;some title\u0026#34;) ) .andExpect(status().is3xxRedirection()) // vary the fake IDs to find out which method was actually called: .andExpect(view().name(\u0026#34;redirect:/something/4711/show\u0026#34;)); } } Conclusion # If @RequestMapping is used along with any of it\u0026rsquo;s shortcuts like @PostRequest etc. at the same controller method then @RequestMapping gets preference and the specialized annotations are silently ignored.\nIf multiple mappings on different methods exist, which are effectively the same, then Spring complains at startup.\n Software-Development Java how @postmapping @getmapping etc. behave if applied along with @requestmapping(method=requestmethod.post ...) to spring controller methods.","date":"23 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-23-form-submit-method-mapping-with-requestmapping-postmapping-etc-in-spring/","section":"Blog","summary":"Recently I ran across a missing method=\"post\" in a \u003cform ...\u003e tag within a Thymeleaf template which was working despite an @PostMapping annotation of the SpringFramework on the controller method. I was wondering how this could happen and did some investigation. Hopefully this article gives a better chance for the next one who runs into this problem.","title":"Form submit method-mapping with @RequestMapping / @PostMapping etc. in Spring","type":"blog"},{"content":"Working on a web application with SpringBoot and want your changes made in IntelliJ IDEA to be visible in the browser right away? Then this is for you:\nSecurity Hint # Before you install the following plugins to your browser, be aware that these could spy on your browser activity. To mitigate risk, I suggest that you use separate operating system account/profile for critical and non-critical tasks and only install such plugins in the browsers of the non-critical account/profile.\nInstalling the LiveReview plugin in Firefox # Open the Plugin Settings Page of your Firefox browser. Type \u0026ldquo;livereload\u0026rdquo; in the search field in the top right area. Select and install the plugin \u0026ldquo;LiveReload 20.2.1\u0026rdquo; (*) - see screenshot.\nIt\u0026rsquo;s the Web extension based version of LiveReload. Supports Firefox 57 (Firefox Quantum) by Todd Wolfson. Activate the plugin.\nNow you see an icon which looks like a circle made of two arrows around a small circle in your Firefox toolbar - see screenshot. To activate the live reload functionality, click the icon to connect.\nIt should turn from black to blue when connected, but if not, also hover with your mouse pointer over the icon to see it\u0026rsquo;s status. (* At the time of writing this article, I am using Firefox Quantum 58.0.2 64bit on Ubuntu Linux 17.10), if you use a different version or get newer plugin versions, the details might vary.)\nInstalling the LiveRewiew plugin in Google Chrome # Open the Google Chrome WebStore with your Chrome browser. Type \u0026ldquo;lifereload\u0026rdquo; in the search field on the top left. Add the plugin \u0026ldquo;LiveReaload\u0026rdquo; supplied by livereload.com - see screenshot. To activate the live reload functionality, click the icon to connect.\nIn my Chrome browser it does not turn blue when connected, but one can also hover with the mouse pointer over the icon to see it\u0026rsquo;s status. Add the spring-boot-devtools Dependency # For Maven, add this dependency to your pom.xml (given the parent-pom is effectively spring-boot-starter-parent with proper version number).\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-devtools\u0026lt;/artifactId\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; For Gradle, also given you have a spring-boot-starter configuration, add the dependency like this:\ndependencies { compile(\u0026#34;org.springframework.boot:spring-boot-devtools\u0026#34;) } In either case re-import your dependency config into IntelliJ IDEA (if auto-import is not activated anyway).\nEnable Auto-Rebuild in IntelliJ IDEA # After you made and changes in your source code, you usually have to run \u0026ldquo;Build Project\u0026rdquo; to rebuild your code and restart SpringBoot. But in \u0026ldquo;File / Settings / Compiler\u0026rdquo; you can activate \u0026ldquo;Build project automatically\u0026rdquo; - see screenshot:\n⚠ The previous step has to be done for each IntelliJ IDEA project again!\nUnfortunately, as you can see in the hint of this setting, this does not work while an application is running. We can change this in the IntelliJ IDEA Registry, though.\nOpen the Action popup (Windows and Linux: \u0026ldquo;Ctrl-Shift-A\u0026rdquo;) and type \u0026ldquo;Registry\u0026rdquo; and select the matching entry - see screenshot:\nThen activate \u0026ldquo;compiler.automake.allow.when.app.running\u0026rdquo; and press the \u0026ldquo;close\u0026rdquo; button. Be careful with the other options in the Registry, though!\nSee LiveReload at Work # Finally run your SpringBoot Web project and open it in the Browser. Then change something in the source code, be it Java or HTML or anything else.\nNow the build should automatically run, SpringBoot reload the files and resources of your app and shortly after the browser also reload and reflect the changes. If it does not work, try re-loading IntelliJ IDEA - in some cases this seemed to be necessary. Also one initial manual reload of your app in the browser might be necessary if the config was not yet active when it got initially loaded.\nHow it Works in SpringBoot # The whole turnaround usually only takes a few seconds - given that the app itself is small enough. How can it be faster than a startup of SpringBoot with your app?\nSpringBoot applies a nice trick, it has one ClassLoader for external libraries (including SpringBoot, SpringFramework etc.) and one for your apps classes and resources; and it only reloads the second ClassLoader for a live reload.\nAcknowledgement # Thanks to John Thompson for his great video course Spring Framework 5: Beginner to Guru, where he explains this live reloading trick. You can also find some of his tutorials on his YouTube channel.\nTo repeatedly apply it, it\u0026rsquo;s just often easier to have it in text form, thus I wrote this summary.\n Software-Development Java Maven","date":"19 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-19-livereload-in-chrome-and-firefox-with-springboot-and-intellij-idea/","section":"Blog","summary":"Working on a web application with SpringBoot and want your changes made in IntelliJ IDEA to be visible in the browser right away? Then this is for you:","title":"LiveReload in Chrome and Firefox with SpringBoot and IntelliJ IDEA","type":"blog"},{"content":"","date":"19 March 2018","externalUrl":null,"permalink":"/en/tags/Maven/","section":"Tags","summary":"","title":"Maven","type":"tags"},{"content":"Recently I\u0026rsquo;ve worked through the video course \u0026ldquo;The Complete Microservices with Spring Course\u0026rdquo; by MleTech Academy, LLC, which is an introduction to the most important Spring modules for building microservices.\nWhat\u0026rsquo;s contained? # This course covers these Spring Cloud modules:\nSpring Cloud Config Spring Could Netflix OSS with Eureka for Service Registry \u0026amp; Discovery Hytrix for Circuit Breaking Ribbon for Load Balancing Zuul Edge service for Proxying, Routing and Filtering) Spring Cloud Security with OAuth for Authentication and Authorization The course comprises 5.5 hours of video material, but is quite fast paced. I actually needed over 20 hours to code along, deal with problems etc.; this is much more time than I usually need for a course of that length, which shows how densely packed it is.\nAll coding is done using Spring Tool Suite (STS) and Maven is used for dependency system where the Spring Boot Starter packages are utilized. There are completely independent basic examples for each of the building bricks (Eureka, Hystrix etc.). Postman is used to manually example REST services.\nThe course ends with an example project, which puts all building blocks together to create a small application based on microservices.\nSource Code for each lesson is available in a Zip-File, but other than the usual before+after code, it\u0026rsquo;s mostly code from somewhere in the middle of the lessons which makes it a bit hard to get back into sync while coding along.\nWhat\u0026rsquo;s missing? # One general problem with heavily configuration-based approaches like in Spring is, that a typo can easily lead to \u0026ldquo;does not work\u0026rdquo; without any hint about what\u0026rsquo;s wrong. The course almost only covers the \u0026ldquo;happy path\u0026rdquo; and gives little hints what to do if it does not work.\nAlso the course author does not answer questions, which I can well understand when considering the low price, especially on an Udemy sale. But still, once something does not work, you are on your own. I gave few hints about the problems I had to co-students, hopefully it helps someone.\nOnce in a while a diagram would have helped to visualize how the parts play together, especially for the example project at the end. So, I did it:\n(Click on the diagram leads you to PlantUML - try it!)\nAutomated testing is unfortunately not covered by this course. Also does the example project still use host+port where Eureka could have been utilized. Looks like an interesting exercise to build a more elaborate example!\nWhat have I learned? # This course gave me a good overview about how to build microservices with Spring Cloud and how quickly the skeleton for a microservice can be set up using the spring-cloud-starter-* Maven artifacts.\nBecause either by a mistake of mine or because the attached sources of the example project did not really match what was used in the video, I got the chance to try it on my own to get it to work. I did, and have added my results to the Q\u0026amp;A section of the course, due to unknown license I can\u0026rsquo;t publish it, though.\n Software-Development Java Maven","date":"14 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-14-course-review-the-complete-microservices-with-spring-course-udemy/","section":"Blog","summary":"Recently I’ve worked through the video course “The Complete Microservices with Spring Course” by MleTech Academy, LLC, which is an introduction to the most important Spring modules for building microservices.","title":"Course Review \"The Complete Microservices with Spring Course\" (Udemy)","type":"blog"},{"content":"A few months ago I\u0026rsquo;ve ran across an erasable notebook called infiniteBook (by EcoBook). I find it quite useful if I want sketch ideas on some piece of \u0026ldquo;paper\u0026rdquo; and be able to easily erase and correct parts like on a whiteboard at the wall.\nIt is available in A6 (\u0026lt;10€), A5 (\u0026lt;15€) and A4 (\u0026lt;20€) and comes with a black pen including an attached eraser. You can also buy a fitting 4 color pen set Staedtler Lumocolor 305F WP4-1, also with erasers. Additionally, I found out that you can use the 7 color pen set Staedtler Lumocolor non-permanent 316 WP8 as well, these pens just lack the erasers.\nFor small corrections these erasers are fine; just make sure the ink is dry before you use them, otherwise it smears horribly. For larger corrections you can also use wet microfiber cloths and if you want to wipe the whole page, just put a drop of water on it and you can wipe it clean with a paper cloth.\nHappy sketching!\n Allgemein","date":"13 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-13-infinitebook-the-portable-personal-whiteboard-notebook/","section":"Blog","summary":"A few months ago I’ve ran across an erasable notebook called infiniteBook (by EcoBook). I find it quite useful if I want sketch ideas on some piece of “paper” and be able to easily erase and correct parts like on a whiteboard at the wall.","title":"infiniteBook - the Portable Personal Whiteboard Notebook","type":"blog"},{"content":"","date":"6 March 2018","externalUrl":null,"permalink":"/en/tags/Learning/","section":"Tags","summary":"","title":"Learning","type":"tags"},{"content":" Why Udemy? # Udemy is an online platform where everybody can publish their video courses. You\u0026rsquo;ll find a wide variety of topics and instructors who mostly teach in English, but also e.g. in German, Spanish, Chinese or Arabic. There are very many software technology related courses, but also about self-improvement, human languages or management practices. Most of the courses which I have found so far are not academic, but geared to real-world. And of cause, according to the broad variety of instructors, there is also some variety of quality, yet the majority of courses is of good or even great quality.\nThe business model does not urge you into a subscription, like many similar services do, but is based on a one-time fee for each course up to several hundred US$/€. There are frequent sales where you can get many courses for just about US$/€ 11 to US$/€ 20. And you get lifetime access for this fee, even on the sales, thus you can always stop and come back later to a topic, which is pretty great.\nEven better, you can download most (maybe even all) courses to your Android tablet or iPad and watch offline, e.g. on a train, on your couch or with headphones in a library. Theoretically it also works on smart-phones, but their screen is too small to actually use it. It\u0026rsquo;s possible to vary the playback speed from factor 0.5 to 2, thus you can rush through parts which you already know from other sources.\nMany courses also have quizzes or exercises attached. A few even offer exercises where you can upload your solutions for peer evaluation by co-students. Of cause, for that price no one can expect the instructors to check your exercises. There is also a Q\u0026amp;A section to each lesson and many of the instructors do answer your questions, some even super quick, also co-students can answer, of cause.\nHow to select courses? # First, find out what you want to learn: What do you need for your career? What are you intrinsically interested in? Maybe you have some have side-project in mind, then try to combine these two areas! Then use the search on Udemy and find available courses, apply filters (e.g. for language) and appropriate order, e.g. sort by rating.\nAlso decide, in which depth you need to learn this topic:\njust technology overview =\u0026gt; 1-5 hours some foundation =\u0026gt; 5-15 hours severere workshop =\u0026gt; 15-30 hours or boot-camp =\u0026gt; over 30 hours I often take 2 or even 3 courses about a single topic: The 1st one as quick a intro/overview, the 2nd one to code along or create my own little project, and sometimes a 3rd one I feel that some issues had been missed in the others.\nAs another criterion, check preview lessons, you can \u0026ldquo;expand all\u0026rdquo; and scroll further down to see available previews. Does the instructor talk along showing just slides or doe they show you how it works in real-life? I prefer to check lessons deeper into the topic for a decision as in many cases courses start with overview slides, but later on really get into the stuff. I think it\u0026rsquo;s not a good idea if authors just give preview for their intro, but some do and that\u0026rsquo;s their decision.\nThen pay attention to the voice; if you don\u0026rsquo;t like the voice, your learning will be severely impeded. Also try higher speeds, some voices are just not made for faster playback, they become unintelligibly snatchy.\nChecking the code examples, if you can access them, is also a good idea. The coding style might tell you something about the qualification of the instructor. But just because it\u0026rsquo;s not clean code, too often it is not, you still can learn a lot about the technology the course is about - at least unless it\u0026rsquo;s getting too bad. I really love it when the instructors work a lot with automated tests. This way you can much easier explore the technology and you learn how to test it.\nA the last but not least criterion is to read the reviews from other students, especially the bad ones. Maybe the criticism does not apply to your need (e.g. \u0026ldquo;too fast\u0026rdquo; if that\u0026rsquo;s exactly what you prefer, or \u0026ldquo;lacks some intro into \u0026hellip;\u0026rdquo; if you are already experienced with \u0026ldquo;\u0026hellip;\u0026rdquo; anyway); in such cases even a 3.x rating could be a great course for you, because it fits your needs, and that\u0026rsquo;s what counts.\nHow to learn? # First off, limit your parallel learning topics. I sometimes have up to 5 Udemy courses \u0026ldquo;in progress\u0026rdquo; at the same time, but in that case, multiple of them are about the same topic, otherwise it\u0026rsquo;s at max 3 for me. If you try more, you won\u0026rsquo;t make any progress anywhere, feel frustrated and might even learn less. Of course, also what you do else where goes into this limit.\nUse fast playback (I usually use factor 1.5) to watch appropriates parts on a train or on your couch, maybe even on a beach if your screen is bright enough ;-) On the other hand, some lessons need you to be done at your computer. In that case, I often pause while coding-along, so I make slower progress. Depending on the topic, it turns out that I need about exactly the stated time for the course up to almost 2 times if I need a lot of hand-on work.\nAlso do some extra research and tinker around with the technology to find out how it really works. This is the actual learning! Do the exercises, but only if not too boring, then it\u0026rsquo;s a waste of time. Better alternatives for this case are: amending the lessons code to try other cases, write tests, refactor the example code!\nThe Q\u0026amp;A sections attached to each lesson are also a valuable source for deeper insights. Can you answer your co-students questions? Then do so! You can\u0026rsquo;t? But maybe you can find it out, so do some research! That is actual learning and can be a lot of fun!\nOptionally you could create flashcards (spaced repetition is king), I also fill my #swDevTermOfTheDay project with terms I\u0026rsquo;ve ran across.\nAfter taking the course, write a review or even an article in your blog to reflect about what you\u0026rsquo;ve learned. I regret that I have not done that from the beginning. Don\u0026rsquo;t make the same mistake!\nThen, create your own little side-project using the newly learned technology; if spaced repetition is king then exercise is queen. Later, come back to the topic repeatedly, e.g. use your flash cards or integrate the previously learned into future side-projects again. Best of course would be you could apply it in your job, right away. These are really the key to learning: actually apply what you have learned and repeat in elongated periods.\n[added on 2018-03-07]\nOn last important hint: In most technology courses you need to install specific software at the beginning. I strongly recommend to try to use the same versions, or as close as possible, otherwise you mights soon run into severe problems following the instructions. This is especially a problem if you use default (current) versions with npm and other package management tools. Also pay attention to versions in GUI screens, maybe by now the default has to changed to something way too recent which does not match the video anymore. You can always later try a new project with current versions or try to upgrade your test projects; that\u0026rsquo;s a good exercise by the way!\nWhat are the limits? # You will learn what you can generally do with the technologies you\u0026rsquo;ve learned about, often that\u0026rsquo;s already enough and you can come back to it later when you really need the details and the how to\u0026rsquo;s.\nYou can also keep this knowledge; unfortunately only if you do some sort of spaced repetition, otherwise your memory is going to fade.\nBut unfortunately, you can only remember most of the \u0026ldquo;how to\u0026rsquo;s\u0026rdquo; if you do apply what you have learned, repeatedly, over and over again. On the other hand, what you\u0026rsquo;ve learned once, you can quickly re-learn, thus, this is often optional.\nNow, get on learning!\nReferences # Udemy Homepage My Udemy Profile\n(shows all purchased courses as \u0026rsquo;learning\u0026rsquo;, even if not yet started) List of My Finished and Current Online Courses Disclaimer: I have no relationship to UDemy, at the time of writing this article not even published any course on that platform.\n Software-Development Learning","date":"6 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-06-learning-software-technologies-and-others-topics-with-udemy/","section":"Blog","summary":"The business model does not urge you into a subscription, like many similar services do, but is based on a one-time fee for each course up to several hundred US$/€. There are frequent sales where you can get many courses for just about US$/€ 11 to US$/€ 20. And you get lifetime access for this fee, even on the sales …","title":"Learning software technologies (and others topics) with Udemy","type":"blog"},{"content":"Having mostly used Java 7 in the last years and after some month of coding only with TypeScript, JavaScript and Solidity, I\u0026rsquo;ve now worked through the video course „Become at ease with Java 8“ by Stijn De Mulder. My motivation was to give me a refresh on Java 8 features, which I only knew from magazine articles and \u0026ldquo;playing around\u0026rdquo;.\nWhat\u0026rsquo;s in and what\u0026rsquo;s not in the course? # The course focuses on these major languages extensions which came with Java 8:\nstatic and default methods on interfaces as well as rules for multiple inheritance lambdas, method references and the Function interface Optional with its methods like get(), orElse...(), map() and flatMap() streams including its methods like map(), flatMap(), reduce(), collect() the new It does not cover extended type annotations, improved type inference, method parameter reflection, new security features of the JVM, JavaFX and may more changes in the APIs and toolset. For a complete list see What\u0026rsquo;s New in JDK 8.\nWhat was good and what could be improved? # The course is generally well done and explains all covered topics in a decent speed. I especially liked the format of the video course: Each chapter starts with an introduction followed by easy to follow explanations of the new features, with diagrams and code examples and ends with a prepared set of JUnit tests which demonstrated the new Java features with real code. This gave me the chance to watch the most part of the course on the train (with about 1.5x playback speed). Afterwards I took about another 2 hours at my desk to make all the unit tests green, mostly by myself, with some sneaking into the video material again.\nUnfortunately Stijn tripped over his tongue a few times and at least some of these glitches I would have cut out or recorded the scene again. Also some of the code from the git repository did not match the video anymore and some tests even got wrong. But that\u0026rsquo;s about all criticism I can come up with. So, I rate this course with 4.5 of 5 stars.\nWhat I\u0026rsquo;ve learned from it? # Most of the new Java 8 features, like the static and default methods for interfaces as well as Optional look pretty easy and straight forward for me. Having used the Joda date time library for over 10 years, there was not much new here except the with...() methods and TemporalAdjusters. And most of the lambda an streaming features, I already knew from JavaScript, where I loved to make use of this programming style. Thus, in total, I\u0026rsquo;ve mostly learned that I am ready for a Java 8 project, but that\u0026rsquo;s fine!\n Software-Development Java","date":"2 March 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-03-02-course-review-become-at-ease-with-java-8-udemy/","section":"Blog","summary":"Having mostly used Java 7 in the last years and after some month of coding only with TypeScript, JavaScript and Solidity, I’ve now worked through the video course „Become at ease with Java 8“ by Stijn De Mulder. My motivation was to give me a refresh on Java 8 features, which I only knew from magazine articles and “playing around”.","title":"Course Review „Become at ease with Java 8“ (Udemy)","type":"blog"},{"content":"","date":"19 February 2018","externalUrl":null,"permalink":"/en/tags/Blockchain/","section":"Tags","summary":"","title":"Blockchain","type":"tags"},{"content":"I\u0026rsquo;ve just completed \u0026ldquo;Ethereum and Solidity: The Complete Developer\u0026rsquo;s Guide\u0026rdquo;. It\u0026rsquo;s really a great course about Solidity/Ethereum smart contract development which enables the learner not just to write their own smart contract on the Ethereum blockchain, but also how to test it (manually and automated) and how to integrate it into a web application.\nCourse Overview # The course starts with an introduction of Blockchain technology and specially Ethereum. You learn how Blockchains work in general and which role accounts, private and public keys, as well as what ether, wei and transactions mean for Ethereum. Then the course goes \u0026ldquo;hands on\u0026rdquo; and you learn how you can connect your browser to the Ethereum network using MetaMask. The introduction ends with an explanation of smart contracts and a small example for an Ethereum smart contract written in Solidity, which is then deployed and executed on one of the Ethereum test networks using Remix, an online IDE for Solidity.\nFollowing chapters are about setting up a local development, build and test environment using NodeJS, Mocha, Web3 and the Truffle framework with Ganache suite. This development framework is then used to implement a Lottery DAPP (decentralized app) using Solidity.\nThe main part of the course is an implementation of a DAPP similar to \u0026ldquo;Kickstarter\u0026rdquo;, more precisely a kind of approval system for payout requests. This application is not production ready, which would be too much for such a course, but it covers all aspects: The smart contract in solidity, how it can be tested via JavaScript and a decent browser-frontend written in JavaScrip and employing React and SemanticUI. It even has an extra chapter for an introduction to React, taken from one of the other courses of the author.\nBy the way, all code of the course is available on Github.\nWhat Could be Improved? # Speaking about the latter, I fully understand why the author Stephen Grider took React into the game and I agree, it made sense. It also made sense to include this React introduction (which by the way give the impression that this other course is also great). But yet, I had to drop half a point off the maximum rating because by my perception, React+SemanticUI took too much place in the other chapters, after all. I would rather have seen some more detail about what to pay attention for on the Solidity side, some examples:\nwhat can be done with Solidity and what not (in comparision to \u0026ldquo;normal\u0026rdquo; programming environments tips and tricks for saving gas cost on the EVM (e.g. tradeoff between storage and operations) what to do and what to avoid in Solidity in comparison to off-chain programming (e.g. how to avoid loops by moving code to originating transactions) how data on the blockchain is actually stored and later accessed by the EVM (that would just be an extra bonus, I really have to research about that) Maybe these topics for an advanced course?\nOther Critical Voices # Some more criticism I\u0026rsquo;ve read about in this courses reviews on Udemy were: \u0026ldquo;too much testing\u0026rdquo; and \u0026ldquo;too slow paced\u0026rdquo;.\nSpeaking about \u0026ldquo;too much testing\u0026rdquo;: I think it was about right for a this kind of course. The author showed how all aspects of the contract can be tested via JavaScript and Mocha and what the difficulties are (side effects). A real project, for sure, would have many more tests; but too much testing, no, by my opinion I loved the testing part of this course a lot! Actually, I am wondering if there is a possibility for real unit tests of Solidity code?\nAnd speaking about \u0026ldquo;too slow paced\u0026rdquo;: Yes, it was slow paced and therefore easy to follow. Udemy has a cool feature, though, speeding up the video playback up to the factor of 2. And that\u0026rsquo;s what I did in vast parts of this course, mostly ran it at factor 1.5 to 1.75. On the other hand, I stopped it while I coded along or tried out things.\nConclusion # I rated this course with 4.5/5 points - and I never gave a 5/5. Overall that was one of the best courses I\u0026rsquo;ve ever taken, thanks to Stephen Grider!\nWhat I\u0026rsquo;ve learned # Maybe the most important part of learning was realizing how careful one has to be when writing smart contracts. Once they are out on the blockchain, no defect can be fixed anymore. Yes, you can destroy or disable your contract, if you\u0026rsquo;ve thought about it before deploying, but you have to consider what happens with the account balances, you really might lose money.\nAnd as in each transaction, each operation costs real money (in ether), it\u0026rsquo;s interesting to see how different you have to design your code. For example, to avoid loops like hell - at least in transactions. Or to move code, which you normally would run in subsequent transactions, to the originating transaction, for the benefit of moving the cost to the originator of the initial transaction instead of making subsequent transactions more and more expensive. This was not deeply discussed in the course, though.\nAlso I learned quite some Solidity programming and which limitations it has due to where and how it\u0026rsquo;s executed. But I doubt I could become a fan of Solidity, it looks too inconsistent for me, e.g. modifiers for instance variables are placed between the type and the name, for functions it\u0026rsquo;s between the parameter lists and the return value. Also the effect which the modifiers \u0026ldquo;storage\u0026rdquo; and \u0026ldquo;memory\u0026rdquo; work, is a bit strange.\nI also learned how to write automated tests for Solidity contract, which I like a lot, and how to use it from within a browser app written in JavaScript. In this context, I\u0026rsquo;ve also understood that DAPP development is way more than just smart contracts and the actual smart contract is probably in most cases just a tiny fraction of the overall code. This goes along with the fact, that Solidity is fast to learn for somebody who already knows JavaScript, Java or Python, despite it having some gotchas.\nAn not to forget, I\u0026rsquo;ve learned some basics of React, which seems to have gained quite some traction. React looks quite productive, but I still not a big fan of the mixing of HTML tags into JavaScript code.\nWhat Comes Next for Me # The main example from the course gave me a good kickoff for some idea I\u0026rsquo;d been full with for a while: A crowdfunding platform for features of free open source software (see this Tweet of mine) where arbitrary people can contribute funding, arbitrary developers can pull the work items and, after a review of the work results, the funders can veto the approval of the payout with the weight of their funding. More is about to come later \u0026hellip;\nCritical Points Regarding Blockchain Technology # I must point out two more critical issues, though not about the course but about Blockchains in general: Their electricity consumption and high volatility.\nThe hunger for electricity comes from mining new \u0026ldquo;coins\u0026rdquo; using Proof of Work which is also crucial for the security. Proof-of-Work is is used by Bitcoin as well as Ethereum.\nEthereum has not yet solved this problem, but they are at least thinking about it and is moving towards a hybrid approach by partly employing Proof-of-Stake.\n[added on 2018-02-19 17:41 UTC+1]\nThe high volatility is also a problem for many applications. One problem is that in many tax systems (as in Germany) you needed to pay earnings tax each time to pay with a cryptocurrency which increased in price since you bought it. Another problem is that any asset which goes up fast, can also go down fast. Some applications, like crowdfunding or even more loans, need at least some reliability about the value of the asset. Among current cryptocurrencies, I see no solution for this problem yet.\n Software-Development JavaScript Blockchain","date":"19 February 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-02-19-course-review-ethereum-and-solidity-the-complete-developers-guide-udemy/","section":"Blog","summary":"I’ve just completed “Ethereum and Solidity: The Complete Developer’s Guide”. It’s really a great course about Solidity/Ethereum smart contract development which enables the learner not just to write their own smart contract on the Ethereum blockchain, but also how to test it (manually and automated) and how to integrate it into a web application.","title":"Course Review \"Ethereum and Solidity: The Complete Developer's Guide\" (Udemy)","type":"blog"},{"content":"","date":"19 February 2018","externalUrl":null,"permalink":"/en/tags/JavaScript/","section":"Tags","summary":"","title":"JavaScript","type":"tags"},{"content":"Some weeks ago I talked to some other IT guys about what kind of qualifications software developers really need. Employers and recruiters are usually looking for practical experience in certain technologies or with specific products; but these change quickly over time. Thus it could easily be that somebody got employed for his (or her) experience with e.g. Java EE from JPA to JSF, and just a year later, what is actually needed for the job is now Spring with Angular, even vue.js/node.js or whatever comes next. Even worse, in that talk, I had mentioned that I have the impression, that it\u0026rsquo;s hard keeping up with all these new which terms come up for any period of time. And I meant just to learn what all these terms mean, far from having any deeper knowledge about all these new topics.\nThat\u0026rsquo;s how I got the idea to tweet one technical term, abbreviation or product name from the software development industry each day including a short definition, so I learn something new every day or at least give some old knowledge a refresh. And of cause, anybody who follows me on my Twitter account can learn along with me. Or you can find them all directly on Twitter.\nOk, but now, what\u0026rsquo;s the qualification software developers really need? Of cause, it\u0026rsquo;s learning! And not just permanently learning about new technologies, but also about the business domain they are working in as well as methodologies for effective work organization including soft skills. Being a software developer means intense lifelong learning. And to be honest, that\u0026rsquo;s awesome!\nP.S. Don\u0026rsquo;t forget to follow me on Twitter!\nLatest Tweets about #swDevTermOfTheDay # See directly on Twitter.\n Software-Development","date":"12 February 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-02-12-my-swdevtermoftheday-campaign-on-twitter/","section":"Blog","summary":"Some weeks ago I talked to some other IT guys about what kind of qualifications software developers really need. Employers and recruiters are usually looking for practical experience in certain technologies or with specific products; but these change quickly over time. Thus it could easily be that somebody got employed for his (or her) experience with e.g. Java EE from …","title":"My #swDevTermOfTheDay Campaign on Twitter","type":"blog"},{"content":"","date":"3 February 2018","externalUrl":null,"permalink":"/en/tags/Architecture/","section":"Tags","summary":"","title":"Architecture","type":"tags"},{"content":"In my experimental side-project, Gleanius [1], a microlearning app, I am using Event-Sourcing [2] and CQRS [3, 4]. This turned out to be an extremely good decision because several times I later found out that new features needed more information in my read model. And the great thing was that in most cases the information was already available in the Event-Store [5].\nAnd as was using my own app as soon as possible in early development, this way I was able to access this information not only fore new data, entered after knowing about the new requirement, but even for existing data. All I needed to do, is to extract the newly needed data from the existing events, while rebuilding the read model; which by the way is no code for one-time use, as many import functions are, but needed anyway for building the read-model.\nIn some cases, though, a new feature needed new data which was not in my events. But in all cases, the data was available in the actual originating command, I just wasn\u0026rsquo;t smart enough to store it along with the event because at that time I had no use for this data.\nExamples # When I wanted to improve the Spaced Repetition [6] algorithm, I needed the information how long the last and longest period of remembering a topic was and for which length of period the learner had failed to remember in the past and how often this ha happened. The Event-Store already contained each event of the learner answering the quizzes, including whether it was correct or wrong and a time stamp, thus all I had to do, was to extend the read-model-builder to extract this information on a per-topic level.\nAnother day I want to extend a multiple choice game where I wanted to confront the learner with terms which he had confused in earlier exercises. The app once knew about which answers were given wrong but I did not store this data, as I had no use for it at that time. Thus, the information was lost for past events; all I could do was to start storing this data along with new events.\nConclusion # Event-Sourcing is a great preparation for new requirements, but only if you are smart enough to store all data along with the events, which belong to the business case from which it stems. Just not needing the data at that time should not be a reason, not to store it. You can omit it in your read-models as long as it\u0026rsquo;s not needed, but once it is needed, you can simply add it to the reducers - et voilá - your application can use it.\nLegal Note # One warning, though: Data privacy laws might prohibit storing data which is not actually needed (at the time of storing the data). his principle is called \u0026ldquo;data avoidance\u0026rdquo; or \u0026ldquo;data minimization\u0026rdquo; (in German: \u0026ldquo;Datensparsamkeit\u0026rdquo;). But that only applies to personal data and in most cases can be legalized by putting the rules into your terms of usage. Ask your lawyer for more details!\nComments? # If you like, you can leave a comment on Twitter, thanks!\n[1] https://gleanius.com\n[2] Event Sourcing is an Software Pattern where all changes to the state of a software system are recorded as events as a primary data source in a way that it\u0026rsquo;s possible to re-create any past state of the software system.\n[3] CQRS (Command query responsibility segregation) is a design principle which uses separate data-structures to update and and modify data.\n[4] To be honest, my usage of this pattern in Gleanius is limited to the app internals so far, as I\u0026rsquo;ve not yet implemented any server-side queries except a full restore.\n[5] An Event-Store is a software module which stores immutable events in the context of Event-Sourcing.\n[6] Spaced Repetition is a learning technique that applies increasing intervals of time between subsequent review of previously learned material in order to make learning more efficient.\n Software-Development Architecture","date":"3 February 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-02-03-eventsourcing-be-complete-with-your-events/","section":"Blog","summary":"In my experimental side-project, Gleanius [1], a microlearning app, I am using Event-Sourcing [2] and CQRS [3, 4]. This turned out to be an extremely good decision because several times I later found out that new features needed more information in my read model. And the great thing was that in most cases the information was already available in the …","title":"Event-Sourcing - Be complete with your events!","type":"blog"},{"content":"","date":"22 January 2018","externalUrl":null,"permalink":"/en/tags/Angular/","section":"Tags","summary":"","title":"Angular","type":"tags"},{"content":"You know these ugly ES6/TypeScript import statements with endless sequences of \u0026ldquo;../../..\u0026rdquo; like in this example:\nimport { Topic} from \u0026#39;../../../services/domain/topic\u0026#39;; import { Rating } from \u0026#39;../../../services/domain/rating\u0026#39;; import { SmartAudio } from \u0026#39;../../../providers/smart-audio/smart-audio\u0026#39;; import { ENGLISH_GERMAN_DEFAULTS } from \u0026#39;../../../data/english-german\u0026#39;; [...] And even worse, once you move your code to another directory, the number of \u0026ldquo;..\u0026rdquo; in these relative paths does not match the target anymore? I wanted to get rid of these in my current project, looked for a solution and found some good hints on the web: [1], [2] and [3]. Thanks to you guys! In this article I\u0026rsquo;d like to show how I applied these to my TypesScript 2.4 / Angular 5 / Ionic 3 project:\nwebpack.config.ts # Ionic v3 brings it\u0026rsquo;s own webpack.config.js in node_modules/@ionic/app-scripts/config/. So, you cannot just create your own, but you have to patch the existing one. So I copied this file to my project root (you could also create a subdirectory for such configurations), and amended as show here - see [HERE] tags:\nfunction getProdLoaders() { [...] } // [HERE] a helper function for alias definitions function asPath(srcSubDir) { return path.join(__dirname, \u0026#34;src\u0026#34;, srcSubDir); } var devConfig = { [...] resolve: { extensions: [\u0026#39;.ts\u0026#39;, \u0026#39;.js\u0026#39;, \u0026#39;.json\u0026#39;], // [HERE] ./src is for getting rid of endless ../../.. in imports // see also tsconfig.json modules: [path.resolve(\u0026#39;./src\u0026#39;), path.resolve(\u0026#39;node_modules\u0026#39;)], // [HERE] some aliases for imports, // tsconfig.json also needs to be maintained alias: { \u0026#34;$data\u0026#34;: asPath(\u0026#39;services\u0026#39;) } }, [...] And the same for prodConfig = !\nTo be able to easily re-apply these changes once Ionic changed their webpack.config.js, you can create a diff file using this command (Linux):\ndiff -Naur node_modules/@ionic/app-scripts/config/webpack.config.js \\ webpack.config.js \u0026gt;webpack.config.js-patch And re-apply it with:\ncp node_modules/@ionic/app-scripts/config/webpack.config.js webpack.config.js patch \u0026lt;webpack.config.js-patch package.json # To make the Ionic build system use your patched webpack.config.js, you need to amend your package.json file as follows:\n\u0026#34;config\u0026#34;: { \u0026#34;ionic_source_map\u0026#34;: \u0026#34;source-map\u0026#34;, \u0026#34;ionic_copy\u0026#34;: \u0026#34;./config/copy.config.js\u0026#34;, \u0026#34;ionic_sass\u0026#34;: \u0026#34;./config/sass.config.js\u0026#34;, // [HERE] make Ionic use our patched webpack.config.js \u0026#34;ionic_bundler\u0026#34;: \u0026#34;webpack\u0026#34;, \u0026#34;ionic_webpack\u0026#34;: \u0026#34;webpack.config.js\u0026#34; }, tsconfig.json # For TypeScript source code you also need to defines these in your tsconfig.json file:\n\u0026#34;compilerOptions\u0026#34;: { [...] // getting rid of endless ../../... in imports \u0026#34;baseUrl\u0026#34;: \u0026#34;src\u0026#34;, // and some neat aliases for imports \u0026#34;paths\u0026#34;: { // webpack.config.js also needs to be maintained \u0026#34;~data/*\u0026#34;: [ \u0026#34;services/*\u0026#34; ] } }, the result # Now you can write these imports as follows:\nimport { Topic} from \u0026#39;services/domain/topic\u0026#39;; import { Rating } from \u0026#39;services/domain/rating\u0026#39;; import { SmartAudio } from \u0026#39;providers/smart-audio/smart-audio\u0026#39;; import { ENGLISH_GERMAN_DEFAULTS } from \u0026#39;$data/english-german\u0026#39;; [...] As you can see, there are actually two unrelated changes:\nautomatically resolve \u0026lsquo;.src\u0026rsquo; for absolute imports definition of an alias \u0026lsquo;$data\u0026rsquo;, where the \u0026lsquo;$\u0026rsquo; is just to avoid name conflicts - you can define as many aliases as you like Comments welcome on Twitter # You are welcome to contact me Me on Twitter or send your reply to this article via Twitter.\nReferences: # [1] https://decembersoft.com/posts/say-goodbye-to-relative-paths-in-typescript-imports/\n[2] https://stackoverflow.com/questions/41512923/how-to-extend-default-webpack-config-in-ionic-v2\n[3] https://stackoverflow.com/questions/43107233/configuration-resolve-has-an-unknown-property-root\n Software-Development JavaScript Ionic Angular TypeScript ionic ionicframework ionic 3 angular angular 5 typescript typescript 2.4 es6 webpack.config.js tsconfig.json import resolver root alias","date":"22 January 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-01-22-getting-rid-of-endless-in-es6-typescript-imports-of-ionic-projects/","section":"Blog","summary":"You know these ugly ES6/TypeScript import statements with endless sequences of “../../..” like in this example:","title":"Getting rid of endless \"../../..\" in ES6/TypeScript imports of Ionic projects","type":"blog"},{"content":"For quite a while I\u0026rsquo;ve not published any blog entry, even though I\u0026rsquo;ve started some, but yet never published. One reason is, that I consider myself a Coder not a Blogger. I really love to create solutions to problems, but once I figured it out, it all to often looks too simple to me to publish, at least on the Internet with all those experts for each area.\nIt\u0026rsquo;s strange though, in a multi team project with many people working on similar topics with often very special technologies, I often wished I could run a blog. In that case I knew, it would be helpful for colleagues. But that\u0026rsquo;s always a fight just to get a server for such. Too bad!\nOkay, anyway, now I want to give it another try, and if it\u0026rsquo;s just for the records of my own findings. I\u0026rsquo;ll also switch to English, not only is it the language of IT and international business, I also can reach many more readers this way ;-) Additionally, I enjoy working in international teams!\n Software-Development Blog","date":"22 January 2018","externalUrl":null,"permalink":"/en/blog/2018/2018-01-22-giving-my-blog-another-try/","section":"Blog","summary":"For quite a while I’ve not published any blog entry, even though I’ve started some, but yet never published. One reason is, that I consider myself a Coder not a Blogger. I really love to create solutions to problems, but once I figured it out, it all to often looks too simple to me to publish, at least on the …","title":"Giving my blog another try","type":"blog"},{"content":"","date":"22 January 2018","externalUrl":null,"permalink":"/en/tags/Ionic/","section":"Tags","summary":"","title":"Ionic","type":"tags"},{"content":"","date":"22 January 2018","externalUrl":null,"permalink":"/en/tags/TypeScript/","section":"Tags","summary":"","title":"TypeScript","type":"tags"},{"content":"Bericht einer Vortragsveranstaltung der PMI agile CoP, Hamburg am 19. Mai 2014\nProjekte mit bis zu 400 Software-Entwicklern oder gar ganze Unternehmen agil zu führen, das könne nur mit gut durchdachten Organisationsframeworks wie z.B. SAFe [1] gelingen. Dies mag für ängstliche Organisationen durchaus ein gangbarer Weg sein, doch für mutige und innovative Organisationen hat uns Stefan Rook, Berater und Geschäftsführer unseres Gastgebers it-agile [2], aufgezeigt, wie es auch ganz alleine mit agilen Mitteln möglich ist. Schon Toyota hatte bereitwillig Mitarbeitern ihrer Wettbewerber aus der ganzen Welt ihr erfolgreiches Toyota-Produktionssystem gezeigt, war ihnen doch sehr wohl bewusst, dass mit einer Blaupause ihres Unternehmens kein anderes erfolgreicher sein könne als sie selbst. Organisation erfolgreicher Unternehmen ist keine statische Struktur, sondern ein dynamischer Prozess, der gerade am Beginn des Weges auf der speziellen Kultur der Organisation basieren muss.\n„Culture eats strategy for breakfast“\n– Peter Drucker [4] –\nIm Laufe des Vortrags erfuhren wir, wie ein einzelner Product Owner auch bei einem noch so großen Produkt seiner Hauptaufgabe der Priorisierung nachkommen kann, dass „an einem Strang ziehen“ (Alignment) und Autonomie kein Widerspruch sein müssen, wie eine Teamstruktur agil zusammen mit der Kultur der Weiterentwicklung des Produktes und/oder der Organisation wachsen und sich den neuen Anforderungen entsprechend anpassen kann, wie Infrastrukturkomponenten über CoP (Communities of Practice) in eine fachlich ausgerichtete Teamstruktur integriert und Schnittstellen und Priorisierungen zwischen Teams festgelegt werden können, wie Abstimmungs- und Redundanzkosten minimiert werden sowie warum Gemba Walks für das Management wichtiger sind als Zahlen in Reports.\nUnter #PMIagileHH [5] finden sich weitere Impressionen von Teilnehmern, und da während des Vortrags fleißig fotografiert wurde, hoffe ich, dass auch noch einige dieser Fotos unter diesem Hashtag hochgeladen werden. Die Folien zum Vortrag sind unter [6] verfügbar. Wer an Details des etwa einstündigen Vortrags eines Tages von Stefan Rook interessiert ist, dem möchte ich dann die Kontaktaufnahme zu it-agile, oder den Besuch einer unserer kommenden Stammtische [7] empfehlen.\nMichael Hönig\np.s. Dieser Artikel erschien ursprünglich im Forum der XING Gruppe der PMI agile CoP, Hamburg [8]\n[1] http://scaledagileframework.com/\n[2] http://www.it-agile.de/\n[3] http://de.wikipedia.org/wiki/Toyota-Produktionssystem\n[4] http://de.wikipedia.org/wiki/Peter_Drucker\n[5] z.B. https://x.com/search?q=%23PMIagileHH\u0026amp;src=hash\n[6] http://de.slideshare.net/roock/agile-skalierung-ohne-blaupause-pmi-agile-hamburg-19052014\n[7] https://www.xing.com/net/pmiagilehh\n[78] http://xing.to/pmiagilehh20140519\n Software-Development Agile","date":"May 20, 2014","externalUrl":null,"permalink":"/de/blog/2014/2014-05-20-agil-skalieren-nach-mass/","section":"Blog","summary":"Projekte mit bis zu 400 Software-Entwicklern oder gar ganze Unternehmen agil zu führen, das könne nur mit gut durchdachten Organisationsframeworks wie z.B. SAFe [1] gelingen. Dies mag für ängstliche Organisationen durchaus ein gangbarer Weg sein, doch für mutige und innovative Organisationen hat uns Stefan Rook, Berater und Geschäftsführer unseres Gastgebers it-agile [2], aufgezeigt, wie es auch ganz alleine mit agilen …","title":"agil skalieren nach Maß","type":"blog"},{"content":"Da kauft Facebook WhatsApp und viele wechseln zum angeblich sicheren Threema. Doch ist Threema wirklich sicherer? Und wie sähe ein wirklich sicher Messenger aus?\nDie Probleme der Verschlüsselung bei Closed Source # Jeder Messenger, wie auch Threema, dessen Quelltext nicht verfügbar ist (Closed Source), kann theoretisch viel mehr oder ganz anderes machen, als er vorgibt. Wenn er dies jetzt nicht tut, dann vielleicht, nachdem dessen Hersteller ebenfalls verkauft wurde. Nur bei Open Source Quellcode, welcher verifzizierbar in die aus den App-Stores installierte App compliliert, kann man relativ sicher sein, dass die App nur das tut, was sie zu tun vorgibt.\nDies ist so, weil die App funktionsbedingt die Nachrichten im Klartext erhält und die der Kommunikationsteilnehmer im Klartest anzeigen muss, hat die Verschlüsselung einen Bruch. Denn, um es noch einmal zu betonen, der Programmcode der App steht unter voller Kontrolle des Herstellers. Es wäre z.B. möglich, jede Nachricht nicht nur mit dem privaten Schlüssel des Empfängers, sondern zudem mit einen Zweitschlüssel entschlüsselbar zu versenden bzw. jede Nachricht bzw. selektiv Nachrichten zusätzlich mit einer zweiten Verschlüsselung zu versenden. Wenn auch nicht jetzt, dann vielleicht nachdem ein Geheimdienst den Hersteller dazu gezwungen hat, dies zu tun.\nSicherheit ist also immer nur ein Versprechen und nicht etwa systembedingt vorhanden, wenn der Vorgang der Verschlüsselung und der Datenübertragungsendstellen nicht nachweislich aus einem bestimmten Open Source Quellcode compliliert wurde. [Nachtrag 21.02.2014] Genau genommen gilt dies schon für das Betriebsystem (iOS, Android etc.), da auch dieses Hintertüren enthalten kann, wenn es nicht vollständig in Open Source vorliegt.\nKontakte/Adressbuch # Der Zugriff einer Messenger App auf das Adressbuch mag praktisch nützlich sein, so können z.B. die Profilbilder zu den Nachrichten angezeigt werden. Andererseits ist auch, wie bei WhatsApp, unsicher, was damit geschieht. Selbst wenn die App die Daten heute nicht überträgt: Wer garantiert, dass sie dies auch in Zukunft nicht tut, sobald sie Millionen von Anwendern hat und vielleicht den Eigentümer wechselt?\nWenn man auf den Luxus des Adressbuch-Zugriffs nicht verzichten möchte, sollten mindestens zwei Versionen der App angeboten werden: eine mit und eine ohne dieses Zugriffsrecht (zumindest unter Android mit seinem ganz-oder-garnicht-Rechtemanagement).\nSchlüsselgenerierung # Üblicherweise wird ein Messenger, wie auch E-Mail, asynchrone Verschlüsselung verwenden. Diese basiert auf einem Paar aus öffentlichem und privatem Schlüssel. Nachrichten, die mit dem öffentlichen Schlüssel verschlüsselt wurden, können nur mit dem privaten Schlüssel entschlüsselt werden. Zur Anwendung kommt dabei eine mathematische Falltürfunktion, die nur mit extremem Rechenaufwand umgekehrt werden kann.\nTatsächlich wird aber i.d.R. nicht die Nachricht selbst so verschlüsselt, sondern nur ein einmalig generierter symmetrischer Schlüssel; und dieser wird mit übertragen. Dies wird zum einen gemacht, weil asynmetrische Ver- und Entschlüsselung größerer Datenmengen langsam ist, zum anderen kann somit der symmetrische Schlüssel jeweils mit dem privaten Schlüssel mehrerer Empfänger verschlüsselt werden, und jeder der Empfänger, aber niemand anders, kann diese Nachricht entschlüsseln.\nDie Schwachstelle ist hierbei die Kenntnis der privaten Schlüssel. Wenn die Messenger App diese selbst generiert, könnte sie auch die privaten Schlüssel heimlich mit übertragen und somit doch wieder jede Nachricht entschlüsseln. Das Schlüsselpaar muss also unbedingt unabhängig von der Messenger App generiert werden und die privten Schlüssel dürfen zumindest einem Closed-Source Messenger niemals bekannt werden. Wie das aufzulösen ist, wird weiter unten beschrieben.\nZudem müssen auch die öffentlichen Schlüssel der Kommunikationspartner auf sicherem Wege zwischen diesen ausgetauscht werden. Denn ansonsten könnte ein Man in the Middle seinen Schlüssel dazwischen schieben und jede Nachricht zunächst mit seinem privaten Schlüssel entschlüssen und mitlesen und dann zur Weiterleitung mit dem echten privaten Schlüssel des Empfängers weiterleiten - theoretisch sogar verändert.\nMan in the Middle # Unter Man in the Middle versteht man üblicherweise ein Computerprogramm, welches sich in den Kommunikationsweg einschaltet und zu beiden seiten so tut, als wäre es der eigentliche Kommunkikationspartner, um Nachrichten mitzulesen oder gar zu verfälschen/einzuschleusen. Bei einer Messenger-App mit einem zentralen Server-Betreiber kann dieser aber auch selbst der Man in the Middle sein. Zumindest hier ist bekannt, wer wann mit wem kommuniziert - selbst wenn die Nachrichten selbst verschlüsselt sind, um sie zuzustellen, muss schließlich der Empfänger bekannt sein.\nWünschenswert wäre hier ein dezentraler Dienst, bei dem jeder seinen eigenen Kommunikationsserver verwenden könnte. Damit könnten Gruppen untereinander ihre Kommunikation ganz einem zentralen Dienst entziehen. Ein Man in the Middle auf dem Übertragungsweg müsste mindestens eine Verschlüsselung brechen, um auch nur an die konkreten Kommunikationspartner heran zu kommen.\nLösungsansatz: Trennung Messenger App von Verschlüsselung+Übertragung # Was ich mir daher wünschte, wäre ein Messenger mit folgenden Eigenschaften:\nEine Messenger Frontend-App, welche die Benutzerschnittselle implementiert. Diese müsste entweder im Open Source vorliegen oder keinerlei besondere Rechte benötigen, außer auf die folgende Dienst-App zugreifen zu können. Eine Messenger Dienst-App, zur Verschlüsselung und Datenübertragung, die separat installiert werden kann und über standardisierte Schnittstellen mit dem Messenger-Frontend kommuniziert. Ein standardisiertes Datenübertragungs- und -vermittlungsprotokoll, welches von Open Source Messenger Dienst-Apps sowie Open Source Servern implementiert werden kann. Es müsste zudem dezentral sein, d.h. das Untergruppen nur über ihre eigenen Server kommunizieren können, die dann externe Kommunikation vermitteln. XMPP wäre ein solcher Kandidat, allerdings fehlt eine Optimierung für Mobildevices und Integration in die Push-Dienste der Smartphone-Betriebssysteme. Die beiden Apps könnten durchaus in einer einzigen zusammengefasst sein, diese müsste dann aber komplett Open Source sein. Viele Open Source Apps sind andererseits nicht besonders bedienungsfreundlich und nicht schick designed. Bei einer Trennung der beiden Programmteile Benutzeroberfläche und Verschlüsselung-/Übertragung könnten schicke und benutzerfreundliche kommerzielle Apps mit einbezogen werden. Zudem wäre die Validierung des verbleibenden Open Source Programmcodes einfacher, weil wesentlich kürzer.\nTradeoff: Geschäftsmodelle vs. Sicherheit # Ein Messenger-System ohne zentralen allmächtigen Anbieter bietet kein so profitables Geschäftsmodell. Es tut sich also die Frage auf, wer ein solches System bezahlen und bewirbt. Das müssen wir tun, diejenigen, die Sicherheit wollen. Bei Diensten, die wir nicht bezahlen, sind wir nicht Kunde sondern Ware!\nKleinere Geschäftsmodelle ergeben sich durch das Angebot benutzerfreundlicher und schicker Frontend-Apps sowie den Betrieb zuverlässiger und schneller Server, was sich auch kombinieren lässt. Mit den bisherigen Programmierschnittstellen der Smartphone-Betriebssysteme ist allerdings Werbefinanzierung ohne Internet-Zugriffsrecht meines Wissens nach nicht möglich, dafür bräuchte es spezieller Werbe-Programmierschnittstellen. Und das Recht des direkten Internet-Zugriffs müss aus Sicherheitsgründen für Messenger-Apps nach diesem Sicherheitskonzept tabu sein.\nTradeoff: Benutzerfreundlichkeit vs. Sicherheit # Externe Schlüsselgenerierung, sichere Schlüsselübertragung, die Installation zweier Apps statt nur einer, ggf. Konfiguration des Kommunikationsservers - wie wir sehen, hat Sicherheit einen stark negativen Einfluss auf die Benutzerfreundlichkeit. Hierzu gibt es leider keine gute Lösung. Ein Gewinn an Benutzerfreudlichkeit wird oft auf Kosten der Sicherheit gehen.\nDies stellt leider auch gleichzeitig eine Bremse in der Verbreitung eines solchen Messenger-Systems dar. Was schwieriger in Betrieb zu nehmen ist, wird von weniger Menschen verwendet und somit gibt es weniger Kommunikationspartner. Und genau darauf kommt es bei einem Messenger-System an: dass ich möglichst viele meiner Kontakte darüber erreichen kann. Dies ist außerdem auch der Grund, aus dem ich selbst noch bei WhatsApp bin, auch wenn ich mich sehr über Googles Ignoranz ändere, dass man unter Android einzelnen Apps nicht den Zugriff auf die Kontakte verwehren kann ohne ganz auf diese Apps zu verzichten.\nWindows/MacOS/Linux und Co. # Nicht unerwähnt bleiben soll, dass all diese Gedanken über Sicherheit von Messenger-Apps mit herkömmlichen Anwendungsprogrammen unter Betriebssystemen wie Windows für PCs, Mac OS sowie Linux und Co ad absurdum geführt sind. Denn dort hat üblicherweise jedes Anwendungsprogramm nicht nur Zugriff auf alle Daten seines Benutzers sondern auch freien Internet-Zugang. Erst neuerdings oder durch Einsatz besonderer Sicherheitsoftware können die Rechte der Anwendungsprogramme eingeschränkt werden.\nUnter Open Source Betriebssystemen, wie Linux, ist dabei zumindest die Wahrscheinlichkeit recht hoch, dass solche Hintertüren in Open Source Anwendungsprogrammen entdeckt werden können. Die Komplexität der Systeme ist aber viel zu groß als dass dies wirklich ausreichen würde.\nZusammenfassung # Ob ein Messenger sicher ist oder nicht, hängt also ganz offensichtlich nicht alleine von der verwendeten Verschlüsselung ab. Mir ist auch, Stand heute, keine Messenger App bekannt, der systembedingt wirklich sicher wäre. Falls ein Messenger Hersteller, z.B. der von Threema, anderer Meinung ist, möge er dies nachvollziehbar aufzeigen - eine auf Versprechen basierende Sicherheit zählt hier leider nicht.\nZudem geht Sicherheit immer auch zu Lasten der Benutzerfreundlichkeit und des Geschäftsmodells, beides hemmt die Verbreitung des Systems und damit auch seines Nutzens.\nEin Ansatz für eine Lösung wurde hier skizziert und je mehr über solche Ideen nachgedacht wird und je mehr sich das Bewusstsein verbreitet, desto größer die Chance, dass wir eines Tages einen systembedingt sicheren Messenger verwenden können.\nEin großer Schritt ist jedoch bereits getan, wenn sich viele Menschen diesen Problemen bewusst werden, auch wenn es absolute Sicherheit kaum geben wird.\n Allgemein","date":"February 21, 2014","externalUrl":null,"permalink":"/de/blog/2014/2014-02-21-sichere-messenger-apps/","section":"Blog","summary":"Da kauft Facebook WhatsApp und viele wechseln zum angeblich sicheren Threema. Doch ist Threema wirklich sicherer? Und wie sähe ein wirklich sicher Messenger aus?","title":"Der Fall #Facebook/#WhatsApp und wie ein sicherer Messenger aussähe","type":"blog"},{"content":"Bei einem Abendvortrag der Firma OOSE stellten Axel Scheithauer und Dr. Marcus Winteroll heute SEMAT vor, eine Platform, die es ermöglicht, bestehende und zukünftige Praktiken, Muster und Methoden des Software-Engineerings zu beschreiben, so dass diese zusammengestellt, nachvollzogen, angewendet, verglichen, beurteilt, gemessen und gelehrt sowie wissenschaftlich untersucht werden können. (vgl. SEMAT Vision)\nStruktur\nSEMAT bedient die Arbeitsbereiche: Praxis (Practice Area), Theorie (Theory Area) und Gemeinschaft (Community Area), die sich um eine Essence drehen. Diese Essence besteht aus dem Kernel, der Kernbereichen benennt, die in jedem Software-Entwicklungs-Projekt beachtet werden müssen, sowie eine Meta-Sprache, mit der die Ausgestaltung dieser Kernbereiche in konkreten Prozessen beschrieben werden kann. Beispiele für solche Kernbereiche sind die Requirements, das Software-System (Produkt), die Entwicklungstätigkeiten, das Team und die Arbeitsweise.\nNutzen\nÜber dieses Paar aus Meta-Sprache und Kernbereichen werden Software-Entwicklungsprozesse vergleichbar. So können z.B. agile mit Phasen-orientierten Entwicklungsprozessen verglichen, Prozess-Tailorings beschreiben sowie die die Konseqzenzen von Prozess-Tailoring und Prozess-Wechseln beurteilt werden. Ich könnte mir auch gut vorstellen, mittels dieses Modells zu erläutern, wie agile Vorgehensmodelle die verschiedenen Aufgaben der Prozesschritte klassicher phasenorientierter Vorgehensmodelle abdecken. Dies könnte vielleicht ein guter Weg sein, agile Vorgehensmodelle denjenigen zu erklären, die durch jahrelange Erfahrung in den Denkmustern phasenorientierter Vorgehensmodelle gefangen sind, und die häufig vorkommende Angst nehmen, agil bedeute Chaos.\nAlphas\nAus den Kernbereichen werden für jedes Prozessmodell sogenannte Alphas konkretisiert, Abstract-Level Progress Health Attributes. Dies sind Eigenschaften eines laufenden Projektes, anhand der der Projektstatus bewertet werden kann und gleichzeitig die nötigen Maßnahmen für den weiteren Fortschritt abgelesen werden können. Dazu wurde von Ivar Jacobsen ein Kartensystem entwickelt, welches vom OOSE Referenten Axel Scheithauer zu einem Status-Brett ausgebaut wurde, auf dem der Projektstatus ganzheitlich auf einen Blick darstellbar ist. Als Alphas kommen dabei auf der Kundenebene die Opportunity (geschäftlicher Nutzen) und Stakeholder, auf der Lösungs-Ebene die Anforderungen und das Software-System (Produkt) sowie auf der Unternehmungs-Ebene die Tätigkeiten (Work), das Team sowie die Arbeitsweise (Way of Working) vor. Für jeden dieser Alphas sind Stufen definiert, z.B. beim Team könnten dies die altbekannten forming, storming, norming, und performing sein, wobei im Vortrag eine leicht abgewandlete Skala vorgestellt wurde.\nUnterstützer\nAls ich die Liste der Unterstützer (Signatories) überflog, kam es mir vor wie ein Rundflug über die Landschaft von herausragenden Software-Engineers, die mir in meinem Berufsleben begegnet sind, z.B. Ivar Jacobsen (Use Cases / UML), Capers Jones (Function Points and Cost Estimation), Robert Martin Uncle Bob (Software Craftsmenship), Bertrand Meyer (OOP/OOD), Ken Schwaber (Scrum), Ian Sommerville (Software Engineering), Ed Yourdon (SA/SD, OOA/OOD, Death March), Erich Gamma (Design Patterns) und Tom Gilb (Software Metrics, Software Inspection) - um nur eine kleine Auswahl zu nennen. In Anbetracht dieser Schwergewichte ist SEMAT vielleicht eine Unternehmung, die man im Auge behalten sollte.\n Software-Development Software-Engineering Agile","date":"August 21, 2013","externalUrl":null,"permalink":"/de/blog/2013/2013-08-21-semat/","section":"Blog","summary":"Kurzbericht von einem Abendvortrag bei OOSE zu Praktiken, Mustern und Methoden zur Analyse, zum Vergleich, zur Lehre sowie wissenschaftlichen Untersuchung des Software-Engineerings.","title":"SEMAT (Software Engineering Method and Theory)","type":"blog"},{"content":"","date":"August 21, 2013","externalUrl":null,"permalink":"/de/tags/Software-Engineering/","section":"Tags","summary":"","title":"Software-Engineering","type":"tags"},{"content":"","date":"May 10, 2013","externalUrl":null,"permalink":"/de/tags/Database/","section":"Tags","summary":"","title":"Database","type":"tags"},{"content":"","date":"May 10, 2013","externalUrl":null,"permalink":"/de/tags/Datenbank/","section":"Tags","summary":"","title":"Datenbank","type":"tags"},{"content":"","date":"May 10, 2013","externalUrl":null,"permalink":"/de/tags/Oracle-SQL/","section":"Tags","summary":"","title":"Oracle SQL","type":"tags"},{"content":"Standardmäßig zeigt der Oracle SQL Developer für DATE Spalten nur das Datum, nicht aber die Uhrzeit an, obwohl diese auch gespeichert ist. Auch die Voreinstellung Menü Extras/Voreinstellungen/Datenbank/NLS hat (zumindest bei mir) keine Wirkung.\nAber eine direkte Einstellung im SQL-Arbeitsbatt funktioniert:\nalter SESSION set NLS_DATE_FORMAT = \u0026#39;DD-MM-YYYY HH24:MI:SS\u0026#39; Diese muss leider für jede Session neu eingestellt werden.\n Software-Development Datenbank Database Oracle SQL","date":"May 10, 2013","externalUrl":null,"permalink":"/de/blog/2013/2013-05-10-oracle-sql-developer-und-datums-spalten-mit-uhrzeit-darstellung/","section":"Blog","summary":"Wie man im Oracle SQL Developer DATE Spalten mit Uhrzeit anzeigen kann.","title":"Oracle SQL Developer und Datums-Spalten mit Uhrzeit-Darstellung","type":"blog"},{"content":"","date":"May 8, 2013","externalUrl":null,"permalink":"/de/tags/EclipseLink/","section":"Tags","summary":"","title":"EclipseLink","type":"tags"},{"content":"","date":"May 8, 2013","externalUrl":null,"permalink":"/de/tags/Hibernate/","section":"Tags","summary":"","title":"Hibernate","type":"tags"},{"content":"","date":"May 8, 2013","externalUrl":null,"permalink":"/de/tags/JEE/","section":"Tags","summary":"","title":"JEE","type":"tags"},{"content":"","date":"May 8, 2013","externalUrl":null,"permalink":"/de/tags/JPA/","section":"Tags","summary":"","title":"JPA","type":"tags"},{"content":"EclipseLink macht es richtig aber sinnbefreit und Hibernate macht es falsch aber sinnvoll, so könnte man das Verhalten dieser beiden JPA Provider beschreiben, wenn es um Bean Validation geht.\nNach der JSR 317, Java (TM) Persistence API, Version 2.0 final vom 10. November 2009, kann die automatisch Bean Validation in den Events pre-persist, pre-update und pre-remove stattfinden, also in EntityManager.persist() für pre-persist, EntityManager.flush() für pre-update und EntityManager.remove() für pre-remove. Bereits hier ist eine Assymmetrie zu erkennen, denn während vorab existierende und veränderte Entitäten am Anfang von EntityManager.flush() validiert werden, also unmittelbar bevor sie in die Datenbank-Transaktion geschrieben werden. werden neue Entitäten bereits in EntityManager.persist() validiert statt erst kurz bevor sie in die Datenbank-Transaktion geschrieben werden.\nSo ist es zumindest in der JPA Spezifikation beschrieben. Nach JSR 317, Seite 100:\n\u0026ldquo;Automatic validation using these constraints is achieved by specifying that Java Persistence delegate validation to the Bean Validation implementation upon the pre-persist, pre-update, and pre-remove entity lifecycle events described in Section 3.5.2.\u0026rdquo;\nErstaunlich ist auch, dass JPA 2.0 kein pre-insert Event kennt, sondern pre-update auch für neue Entitäten gilt. Hier warnt die Spezifikation daher auch vor implementationsabhängigen Verhalten, dass nämlich neue Entitäten durchaus auch noch im flush, nämlich beim pre-update validiert werden können, aber nicht validiert werden müssen. So schreibt JSR 317, Seite 101:\n\u0026ldquo;In the case where an entity is persisted and subsequently modified in a single transaction or when an entity is modified and subsequently removed in a single transaction, it is implementation dependent as to whether the pre-update validation event occurs. Portable applications should not rely on this behavior.\u0026rdquo;\nEclipseLink in der getesteten Version 2.4.2-RC1 folgt auch völlig der Spezifikation, indem es die Validierung neuer Entitäten nur in EntityManager.persist() durchführt, nicht aber im optionalen Entitymanager.flush(). Dies kann dazu führen, dass invalide Änderungen an neuen Entitäten, die zwischen persist und flush durchgeführt werden, in die Datenbank geschrieben werden. Constraints auf der Datenbank können selbstverständlich korrupte Daten verhindern, aber nicht alle Bean Validierungen können sinnvoll auch als Datenbank-Constraints implementiert werden - zudem sind die Fehlermeldungen schwieriger bis gar nicht in Benutzer-lesbarer Form darstellbar.\nHibernate in der getesteten Version 4.2.1-Final dahingegen bricht mit der Spezifikation, als dass es im EntityManager.persist() keine Validerung durchführt. Allerdings validiert es auch neue Entitäten in EntitiyManager.flush() und verhindert somit invalide Daten an der Datenbank weiter zu reichen. Auch wenn Hibernate hier gegen die Spezifikation verstößt, halte ich das Verhalten von Hibernate für sinnvoller. Sowohl im pre-persist als auch im pre-update zu validieren hat schließlich auch Performance-Nachteile.\nIm Anhang fand sich ein Maven-Projekt (beanvalidation-test.zip), mit dem das hier beschriebene Verhalten nachvollzogen werden konnte; die Datei ist nicht mehr verfügbar.\n Software-Development Java JEE JPA EclipseLink Hibernate","date":"May 8, 2013","externalUrl":null,"permalink":"/de/blog/2013/2013-05-08-bean-validation-in-jpa2-0/","section":"Blog","summary":"Über einen kleinen aber wesentlichen Unterschied zwischen Hibernate und EclipseLink bei der Validierung neuer Entitäten.","title":"Tücken der Bean Validation (JSR#303) in JPA 2.0","type":"blog"},{"content":"Eine übliche Situation für einen Software-Entwickler: Es sollen drei Felder in ein Formular eingebaut werden. So etwas kann doch nicht lange dauern! Drei Felder, das schreibt man doch eben mal zwischendurch hin! Länger als 20min darf man für so etwas doch als Entwickler nicht brauchen, oder?\nDoch wenn man vor der Implementation konkrete Akzeptanzkritieren definiert, merkt man schnell, dass die Dinge oft nicht so einfach sind, wie sie zunächst erscheinen. So wird man beim Erstellen der Akzeptanzkriterien feststellen, was man alles braucht, z.B. dass\nDas zweite und dritte Feld nur dann sichtbar sein sollen, wenn das erste einen Inhalt hat. Das zweite und dritte Feld nach bestimmten Regeln validiert werden sollen. Je nach Eingabefehler, ganz bestimmte Fehlermeldungen erscheinen sollen. Die Validierung aber nicht gelten soll, wenn die Felder aufgrund von 1. gar nicht sichtbar sind. Die Felder natürlich auch in der Datebank gespeichert und wieder daraus geladen werden sollen. Die Inhalte der Felder im Prozess X und im Report Y berücksichtigt werden müssen. \u0026hellip; und so wird es weiter gehen. Mit diesem neu gewonnenen Wissen wird die Schätzung sicherlich schon ganz anders ausfallen, vielleicht 4 Stunden - oder je nach Komplexität der Validierung, z.B. Prüfung gegen Datenbank-Anfragen, auch länger. Und die unter 6. entdeckten weiteren Funktionen sind selbst in dieser Schätzung vermutlich noch gar nicht enthalten.\n Software-Development Agile","date":"July 28, 2011","externalUrl":null,"permalink":"/de/blog/2011/2011-07-28-wie-akzeptanzkritieren-beim-schatzen-helfen/","section":"Blog","summary":"Eine übliche Situation für einen Software-Entwickler: Es sollen drei Felder in ein Formular eingebaut werden. So etwas kann doch nicht lange dauern! Drei Felder, das schreibt man doch eben mal zwischendurch hin! Länger als 20min darf man für so etwas doch als Entwickler nicht brauchen, oder?","title":"Wie Akzeptanzkritieren beim Schätzen helfen","type":"blog"},{"content":"Da ich für ab Mitte August ein neues Projekt suche, studiere ich wieder intensiv Projekt- und Stellenangebote. Viele Projekt- und Stellenangebote lesen sich dabei, würde man sie auf eine Arztstelle übertragen in etwa so:\nIm Zuge des Ausbaus der Unfallabteilung des Klinikums suchen wir für die Chirurgische Abteilung des Krankenhauses Hierunddort, Akademisches Lehrkrankenhaus der Universität Entenhausen, ab sofort eine/-n\nFachärztin/Facharzt für Unfall- und Wiederherstellungschirurgie\nals Oberärztin/Oberarzt\nDas Krankenhaus Hierunddort ist ein modernes, zentral gelegenes blablabla.\nIhre Aufgaben:\nBeurteilung von Behandlungsmethoden bei Gewebe und- Knochenverletzungen Durchführung von Gewebewiederherstellung von Knochenrekonstruktion \u0026hellip; Ihre Qualifikationen:\nErfahrungen in der Unfallchirurgie Sehr gute Anatomie-Kenntnisse Hervorragende Kenntnisse Aufbaus menschlicher Knochen Erfahrung mit dem Umgang mit Skalpellen der Marke Meatcut Zertifizierter Anwender der Röntgengereräte-Reihe X-Beam 2000 Möglichst Zertifiziert für MRT Magnofort 2 oder 3 Bedarf es weiterer Worte?\n Software-Development","date":"July 18, 2011","externalUrl":null,"permalink":"/de/blog/2011/2011-07-18-stellenanzeige-wenn-arzte-software-entwickler-waren/","section":"Blog","summary":"Da ich für ab Mitte August ein neues Projekt suche, studiere ich wieder intensiv Projekt- und Stellenangebote. Viele Projekt- und Stellenangebote lesen sich dabei, würde man sie auf eine Arztstelle übertragen in etwa so:","title":"Stellenanzeige, wenn Ärzte Software-Entwickler wären","type":"blog"},{"content":"Anfang April findet in Hamburg ein eintägiger Workshop zu TDD unter meiner Moderation und im Anschluss ein zweitägiges Seminar advanced TDD mit Johannes Link (u.a. Autor von \u0026ldquo;Softwaretests mit JUnit\u0026rdquo;) statt.\nZielgruppe sind Java-Entwickler, die sich bereits mit TDD beschäftigt haben. Im Workshop möchte ich die nötige Software auf den Arbeitsrechnern einrichten, und durch eine gesunde Mischung aus Theorie und Praxis zu JUnit und TDD Praktiken einen einheitlichen Wissensstand schaffen. Praktisch TDD erfahren werden wir mit Pair Programming in einem Coding-Dojo.\nDas dann anschließende Seminar zu fortgeschrittenen TDD Praktiken befasst sich mit der Frage, warum TDD überhaupt funktioniert, erklärt Doubles, Stubs, Mocks und Co, erläutert Testbarkeit und gutes Design, Responsive Design und Sufficient Design, sowie das Arbeiten mit Legacy Code. Test Data Builders, Testen grafischer Oberflächen und nebenläufiger Programme sind genauso Thema, wie auchd die Rolle von Integrationstests, TDD mit dynamischen Sprachen, JUnit Rules, Hamcrest, Behaviour-Driven Development und Continuous Testing.\nDie Kosten betragen 1059,10€ inkl. USt. (netto 890€) für alle drei Tage inkl. gemeinsamem Mittagessen und Getränken. Je nach Teilnehmeranzahl kann der Preis nachträglich noch um ca. 150 Euro je Teilnehmer fallen. Alternativ Festpreis von 999,60€ inkl. USt. (netto 840 Euro).\nAnmeldung bitte bis zum 18. März 2011 über das XING-Event oder per Fax bzw. Brief. Mit Anmeldung geht die Verpflichtung zur Zahlung des o.g. Betrages einher.\nDie Anzahl der Teilnehmer ist auf 12 begrenzt.\n Software-Development Java Agile","date":"February 16, 2011","externalUrl":null,"permalink":"/de/blog/2011/2011-02-16-4-6-april-tdd-workshop-und-advanced-tdd-seminar-in-hamburg/","section":"Blog","summary":"Anfang April findet in Hamburg ein eintägiger Workshop zu TDD unter meiner Moderation und im Anschluss ein zweitägiges Seminar advanced TDD mit Johannes Link (u.a. Autor von “Softwaretests mit JUnit”) statt.","title":"4.-6. April: TDD Workshop und advanced TDD Seminar in Hamburg","type":"blog"},{"content":"Ich stand gerade vor dem Problem, von einem (virtualisierten) Linux System (hiddenhost) aus, HTTP im Internet nutzen zu müssen, obwohl von diesem System aus kein Verbindungsaufba nach außen möglich ist, sehr wohl allerdings eine Intranet-SSH-Verbindung zu diesem System. Doch auch dieses Problem hat eine Lösung:\nAuf einem Linux-System im Intranet, welches ins Internet kommt (openhost), wird z.B. Privoxy als einfacher HTTP-Proxy auf Port 8118 installiert:\n# apt-get install privoxy Und ebenfalls dort ein reverse SSH-Tunnel gestartet:\n# ssh -nNT -R 8118:localhost:8118 user@hiddenhost Auf dem abgeschotteten System wird dann der Proxy z.B. so konfiguriert:\n$ echo \u0026#39;export http_proxy=localhost:811\u0026#39; \u0026gt;\u0026gt;~/.bashrc $ source ~/.bashrc Für apt-get kann man den Proyx am besten so konfigurieren:\n# echo \u0026#39;acquire::http::Proxy \u0026#34;http://openhost:3128\u0026#34;;\u0026#39; \u0026gt;/etc/apt/apt.conf.d/proxy  Allgemein Linux","date":"August 19, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-08-19-http-proxy-durch-einen-reverse-ssh-tunnel/","section":"Blog","summary":"Ich stand gerade vor dem Problem, von einem (virtualisierten) Linux System (hiddenhost) aus, HTTP im Internet nutzen zu müssen, obwohl von diesem System aus kein Verbindungsaufba nach außen möglich ist, sehr wohl allerdings eine Intranet-SSH-Verbindung zu diesem System. Doch auch dieses Problem hat eine Lösung:","title":"HTTP Proxy durch einen Reverse SSH Tunnel","type":"blog"},{"content":"Wenn man einen Datenträger nicht auf einem anderen als dem Zielsystem bootfähig gemacht hat, passiert es oft, dass beim booten auf dem Zielsystem die folgende Fehlermeldung erscheint: kernel must be loaded before booting.\nUrsache ist, dass der Kernel - das eigentliche Betriebssystem - aufgrund einer anderen Gerätezuordnung nicht gefunden werden kann. Als Lösung kann man einmalig den Kernel mit dem Bootloader grub manuell booten, z.B. so:\nroot (hd0,0) kernel /boot/vmlinuz-2.6.32-22-generic ro root=/dev/sda1 initrd /boot/initrd-2.6.32-22-generic.img boot hd0,0 bedeutet dabei, dass von der ersten Partition der Festplatte gebootet werden soll, was ggf. angepasst werden muss. /boot/vmlinuz-2.6.32-22-generic ist ein Beispiel-Dateiname für einen Kernel. Hier am besten einfach /boot/vmlinuz- gefolgt von TAB eingeben, dann werden alle Kernel auf dem zuvor eingestellten Gerät aufgelistet. /dev/sda1 ist dann nach Linux Device-Filesystem-Namenskonvention wieder dasselbe wie zuvor schon hd0,0, evtl. muss hier auch /dev/hda1 stehen, oder eine ganz andere Partition - eben je nachdem von welcher Partition auf welchem Gerät man booten möchte. /boot/initrd-2.6.32-22-generic.img ist dann wieder beispielhaft das Init-Filesystem, passend zum vorher gewählten Kernel; auch hier kann wieder TAB helfen.\nWenn das System dann neu gebootet ist, kann man grub veranlassen, diese Werte fest in die Boot-Konfiguration zu schreiben:\nsudo grub-install --recheck /dev/sda Hier muss natürlich /dev/sda wieder durch das Gerät ersetzt werden, auf welchem grub installiert werden soll.\nBeim nächsten Neustart sollte nun ohne manuellen Eingriff von dem Datenträger booten.\n(Insbesondere da diese Anleitung leider von den konkreten Geräte-, Partitons- und Kernelnamen abhängig ist, kann ich leider keine Haftung übernehmen. Da jedoch lediglich der Bootloader und die grub-Config neu geschrieben wird, kann es unter normalen Umständen auch keinen echten Datenverlust geben.)\n Linux","date":"July 19, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-07-19-linux-trotz-grub-fehlermeldung-kernel-must-be-loaded-before-booting-manuell-booten/","section":"Blog","summary":"Wenn man einen Datenträger nicht auf einem anderen als dem Zielsystem bootfähig gemacht hat, passiert es oft, dass beim booten auf dem Zielsystem die folgende Fehlermeldung erscheint: kernel must be loaded before booting.","title":"Linux trotz grub Fehlermeldung \"kernel must be loaded before booting\" manuell booten","type":"blog"},{"content":"","date":"May 23, 2010","externalUrl":null,"permalink":"/de/tags/Eclipse/","section":"Tags","summary":"","title":"Eclipse","type":"tags"},{"content":"Unit-Tests sind in den meisten Java-Projekten schon selbstverständlich. Doch womit testet man eigentlich seine Unit-Tests? Die Code-Coverage alleine ist kein guter Maßstab, besagt diese doch noch nichts über die Wahrscheinlichkeit aus, mit der fehlerhafte Ergebnisse erkannt werden. Sollen doch sogar schon Unit-Tests ganz ohne Asserts geschrieben worden sein! Die Unzulänglichkeit von Unit-Tests kann mit so genannten Mutationstests aufgedeckt werden. Einige Werkzeuge für solche Tests werden hier kurz vorgestellt.\nMutationstests verändern die \u0026ldquo;Class under Test\u0026rdquo; (CUT) auf verschiedene Art und Weise. Z.B. werden \u0026ldquo;+\u0026rdquo; durch \u0026ldquo;-\u0026rdquo; ausgetauscht, \u0026ldquo;\u0026lt;\u0026rdquo; durch \u0026ldquo;\u0026lt;=\u0026rdquo; oder aus einem numerischen Literal 1 wird 0. Gute Unit-Tests sollten bei diesen Mutanten fehlschlagen. Je nachdem wie viele Mutationen erkannt werden entsteht so ein Qualitätsmaßstab für die Unit-Tests. Nicht geprüft wird dabei selbstverständlich, ob von den Unit-Tests überhaupt alle Anforderungen abgedeckt sind.\nAn Werkzeugen für Mutations-Tests habe ich folgende gefunden:\nName/URL Lizenz Mutation Anmerkungen JesTer BSD-ähnlich Source-Code Judy proprietär Source-Code µJava ? Source-Code nur bis Java 1.4, Eclipse-Plugin vorhanden (muJava) Jumble GPL2 Byte-Code auch Java 6, JUnit4, Eclipse-Plugin vorhanden Am interessantesten erscheint mir Jumble, welches zum einen auch die aktuelle Java- und JUnit- Version unterstützt (wenn auch derzeit nur selbstgebaut aus dem Subversion-Repository), und zum anderen sehr schnell ist, weil es Mutationen auf der Eben des JVM Byte-Code erstellt. Zu Jumble wird es daher in Kürze einen konkreteren Artikel geben.\n Software-Development Java Eclipse","date":"May 23, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-05-23-mutationstests-fur-junit-tests/","section":"Blog","summary":"Unit-Tests sind in den meisten Java-Projekten schon selbstverständlich. Doch womit testet man eigentlich seine Unit-Tests? Die Code-Coverage alleine ist kein guter Maßstab, besagt diese doch noch nichts über die Wahrscheinlichkeit aus, mit der fehlerhafte Ergebnisse erkannt werden. Sollen doch sogar schon Unit-Tests ganz ohne Asserts geschrieben worden sein! Die Unzulänglichkeit von Unit-Tests kann mit so genannten Mutationstests aufgedeckt werden. Einige …","title":"Mutationstests für JUnit-Tests","type":"blog"},{"content":"Auf einem Kundenserver hatte ich vor kurzem das Problem, kein sudo zum User postgres machen zu können.\nsudo -s -u postgres \u0026gt; sudo: unable to change to runas uid: Resource temporarily unavailable Ursache des Problems war ein zu geringer nproc Eintrag in der /etc/system/limits.conf, so dass für den User keine weiteren Prozesse - auch keine Shell - mehr gestartet werden konnten.\n Linux","date":"April 28, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-04-28-linux-sudo-unable-to-change-to-runas-uid-resource-temporarily-unavailable/","section":"Blog","summary":"Auf einem Kundenserver hatte ich vor kurzem das Problem, kein sudo zum User postgres machen zu können.","title":"(Linux) sudo: unable to change to runas uid: Resource temporarily unavailable","type":"blog"},{"content":"Nachdem ich einen XHTML Codeblock von einer Seite auf eine andere verschoben hatte, gab es plötzlich die folgende Exception in meiner JSF (MyFaces+Tomahawk+RichFaces) Anwendung:\njava.lang.IllegalArgumentException: \u0026#34;\u0026#34; at javax.faces.component.UIComponentBase. findComponent(UIComponentBase.java:576) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:249) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. createOutputLabelMap(HtmlMessageRenderer.java:264) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. getOutputLabelMap(HtmlMessageRenderer.java:231) at org.apache.myfaces.renderkit.html.ext.HtmlMessageRenderer. findInputLabel(HtmlMessageRenderer.java:163) Der Fehler trat auf, sobald Validierungs-Fehlermeldungen angezeigt werden sollten, die sich in einem \u0026lt;a4j:outputPanel\u0026gt; befanden. Der verschobene Codeblock war ebenfalls eine \u0026lt;a4j:outputPanel\u0026gt;. Ob dieser Umstand allerdings mit zur Fehlfunktion beitrug, habe ich nicht weiter analysiert.\nAusgehend von der letzten lauffähigen Version habe ich dann durch teilweises Hereinnehmen des verschobenen Codes herausgefunden, dass die Ursache ein \u0026lt;t:outputLabel\u0026gt; war, welches keinem Eingabefeld zugeordnet war. Dieses befand sich innerhalb des verschobenen Codeblocks innerhalb eines \u0026lt;a4j:outputPanel\u0026gt;.\nDie Lösung war, dieses gegen ein \u0026lt;t:outputText\u0026gt; auszutauschen, welches in dem Fall sowieso das richtige Tag war. Irritierend war nur eben, dass der Code in der anderen Seite anstandslos funktionierte.\n Java","date":"April 27, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-04-27-jsf-java-lang-illegalargumentexception-at-uicomponentbase-findcomponent/","section":"Blog","summary":"Nachdem ich einen XHTML Codeblock von einer Seite auf eine andere verschoben hatte, gab es plötzlich die folgende Exception in meiner JSF (MyFaces+Tomahawk+RichFaces) Anwendung:","title":"JSF: java.lang.IllegalArgumentException: \"\" at UIComponentBase.findComponent()","type":"blog"},{"content":"Das UNIX-Programm socat ist ein mächtiges Werkzeug, mit dem Daten zwischen verschiedensten Protokollen umgesetzt werden können. In diesem Beispiel, um zwei serielle Schnittstelle über einen TCP-Tunnel zu verbinden.\nauf beiden Maschinen socat installieren, mit Debian und ähnlichen Distributionen z.B. so:\n# apt-get install socat auf der einen Tunnel-Maschine socat als Server starten, z.B.:\n\u0026gt; socat TCP4-LISTEN:7777,bind=SERVERIP,reuseaddr,fork /dev/ttyS0,raw,echo=0,crnl Dabei ist SERVERIP die zu verwendende IP-Nummer bzw. der Hostname dieser ersten Tunnel-Maschine.\nauf der anderen Tunnel-Maschine socat als Client starten, z.B.:\n\u0026gt; socat /dev/ttyS1,raw,echo=0,crnl TCP4:SERVERIP:7777 Das im Beispiel an /dev/ttyS0 der ersten Maschine angeschlossene serielle Gerät steht nun unter /dev/ttyS1 auf der zweiten Maschine zur Verfügung. D.h. ein dort angeschlossenes serielles Gerät kommuniziert über den TCP-Tunnel mit dem an der ersten Maschine angeschlossenen seriellen Gerät.\n Allgemein Linux","date":"January 25, 2010","externalUrl":null,"permalink":"/de/blog/2010/2010-01-25-linux-serielle-schnittstelle-durch-tcp-tunneln/","section":"Blog","summary":"Das UNIX-Programm socat ist ein mächtiges Werkzeug, mit dem Daten zwischen verschiedensten Protokollen umgesetzt werden können. In diesem Beispiel, um zwei serielle Schnittstelle über einen TCP-Tunnel zu verbinden.","title":"Linux: Serielle Schnittstelle durch TCP tunneln","type":"blog"},{"content":"Oft kommt man in Projekten in die Verlegenheit, Benutzer-definierte mathematische Funktionen zu berechnen, z.B. für einen Funktionsplotter. Solches lässt sich ohne große Umstände mit BeanShell erledigen:\nimport bsh.*; /// Calculates y=f(x) with x, y € double public class Function { Interpreter interpreter = new Interpreter(); { try { interpreter.eval( // add some shortcuts for math functions \u0026#34;sin(a) { return Math.sin(a); } \\n\u0026#34; + \u0026#34;cos(a) { return Math.cos(a); } \\n\u0026#34; + \u0026#34;tan(a) { return Math.tan(a); } \\n\u0026#34; + \u0026#34;atan(a) { return Math.atan(a); } \\n\u0026#34; ); } catch (Exception e) { // this can not happen in production code e.printStackTrace(); } } private String function; /// example: new Function(\u0026#34;sin(x)+cos(x/3)/2\u0026#34;) public Function(String function) { this.function = function; } /// calculates y=f(x) using the function from the constructor double y(double x) throws EvalError { interpreter.set(\u0026#34;x\u0026#34;, x); interpreter.eval(\u0026#34;y=\u0026#34; + function); return (Double) interpreter.get(\u0026#34;y\u0026#34;); } /// example which prints y=f(x)=atan(x) from x in 0...9 public static void main(String[] args) throws EvalError { Function f = new Function(\u0026#34;atan(x)\u0026#34;); for ( double x = 0; x \u0026lt; 10; ++x ) { System.out.println(\u0026#34;f(\u0026#34;+x+\u0026#34;) = \u0026#34; + f.y(x) ); } } } In Maven2-basierten Build-Umgebungen kann BeanShell direkt aus einem Repository eingebunden werden, z.B. (auf aktuelle Version achten):\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.apache.maven\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;maven-script-beanshell\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.2.1\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;  Software-Development Java Maven","date":"November 17, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-11-17-mathematischer-funktions-parser-mit-beanshell/","section":"Blog","summary":"Oft kommt man in Projekten in die Verlegenheit, Benutzer-definierte mathematische Funktionen zu berechnen, z.B. für einen Funktionsplotter. Solches lässt sich ohne große Umstände mit BeanShell erledigen:","title":"mathematischer Funktions-Parser mit BeanShell","type":"blog"},{"content":"Zur Visualisierung von Ereignissen, z.B. Warnungen - oder in meinem aktuellen Projekt zur Anzeige eines sich wiederholenden, zeitgesteuerten Ereignisses - kann man den Hintergrund von Java-Swing Fenstern ganz einfach kurz aufblinken lassen:\nimport java.awt.*; import javax.swing.*; public class Flasher { private Component component; private int flashMS; private Color flashColor; /** Create a flasher for \u0026#39;component\u0026#39; which flashes * in \u0026#39;color\u0026#39; for \u0026#39;flashMS\u0026#39; milliseconds. */ public Flasher(Component component, Color color, int flashMS) { this.component = component; this.flashMS = flashMS; this.flashColor = color; } /// flash the specified window in \u0026#39;delayMS\u0026#39; milliseconds public void trigger(final int delayMS) { new Thread() { @Override public void run() { Color normalColor = component.getBackground(); try { sleep(delayMS); setBackground(flashColor); Thread.sleep(flashMS); } catch (InterruptedException e) { // ignore } finally { setBackground(normalColor); } } }.start(); } private void setBackground(final Color color) { SwingUtilities.invokeLater( new Runnable() { @Override public void run() { component.setBackground(color); } }); } /// just sample code public static void main(String[] args) throws InterruptedException { JFrame frame = new JFrame(\u0026#34;Flasher\u0026#34;); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setSize(600, 400); frame.setVisible(true); Flasher flasher = new Flasher(frame.getContentPane(), Color.RED, 100); flasher.trigger(3000); } }  Software-Development Java","date":"November 11, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-11-11-java-swing-fensterhintergrund-blinken/","section":"Blog","summary":"Zur Visualisierung von Ereignissen, z.B. Warnungen - oder in meinem aktuellen Projekt zur Anzeige eines sich wiederholenden, zeitgesteuerten Ereignisses - kann man den Hintergrund von Java-Swing Fenstern ganz einfach kurz aufblinken lassen:","title":"Java Swing: Fensterhintergrund blinken","type":"blog"},{"content":"","date":"November 9, 2009","externalUrl":null,"permalink":"/de/tags/%C3%96konomie/","section":"Tags","summary":"","title":"Ökonomie","type":"tags"},{"content":"Unsere neue Bundesregierung bringt ein Wachstumsbeschleunigungsgesetz auf den Weg, das im Kern aus Steuererleichterungen besteht.\nGezieltes Senken der Steuerlast bedeutet der Theorie nach in der Tat Wirtschaftswachstum. Allerdings um so mehr, je größer die marginale Konsumquote derjenigen ist, die diese Steuersenkungen erhalten. D.h. Steuersenkungen bei mittleren Einkommen (auf kleine Einkommen entfallen eh keine Steuern) nützen am meisten, weil dort im Schnitt eben diese marginale Konsumquote am höchsten ist. Im andern Extrem verpuffen in dem Sinne Steuersenkungen für große Kapitaleinkünfte zum größten Teil. (so die Theorie - auch wenn sie nicht jedem gefallen mag)\nZudem muss die Summe der wachstumsfördernden Effekt (inkl. dem der der Steuersenkung) größer sein als die wachstumssenkenden Effekte (z.B. aus Demographieentwicklung und Weltkonjunkturlage), um zu positivem Wachstum zu führen.\nWas für ein Wachstum brauchen wir überhaupt? Hätten wir nach dem Wirtschaftswunder ein reales BIP-Wachstum von 3% p.a. gehabt, hätte das so ausgesehen:\nD.h. das schaffen wir offenbar seit ca. 35 Jahren nicht mehr. Und wenn es nun plötzlich doch 3% wären, dann sähe es so aus:\nOk, mit 1,5% sehen die Kurven schon etwas besser aus, aber reichen 1,5% p.a.? Oder sollte vielleicht mal darüber nachgedacht werden, warum Nichtwachstum gleich in die Katastrophe führt?\nLesen wir doch mal, was so schlaue Leute wie Prof. Paul Krugman, Prof. Willem Buiter, Prof. Ulrich van Suntum und Prof. Greg Mankiw) dazu zu sagen haben.\nIst also ein (gut versteckter) \u0026ldquo;Mindestpreis\u0026rdquo; der Grund für den Wachstumszwang und verantwortlich für das scheinbare Versagen der Marktwirtschaft falls eben dieses Wachstum für längere Zeit ausbleibt?\n(Quelle der Daten aus den Diagrammen: www.fgn.unisg.ch, sowie für aktuelle Entwicklung Wikipedia, Stichproben mit Daten vom Statistischem Bundesamt verglichen)\n Ökonomie","date":"November 9, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-11-09-wachstumsbeschleunigungsgesetz/","section":"Blog","summary":"Unsere neue Bundesregierung bringt ein Wachstumsbeschleunigungsgesetz auf den Weg, das im Kern aus Steuererleichterungen besteht.","title":"Wachstums­beschleunigungsgesetz","type":"blog"},{"content":"Unter Systemkritik versteht man meist die kommunistische, die also das System „Marktwirtschaft“ kritisiert. In diesem Text möchte ich mit einigen lose zusammenhängenden Gedanken aufzeigen, an welcher Stelle unser System gerade nicht Marktwirtschaftlich ist, und eben durch eine fehlende Marktfreiheit unsoziale Tendenzen hat.\nDie Volkswirtschaftstheorie sieht Geldwirtschaft als Schleier über der Realwirtschaft, d.h. das Geld bildet angeblich nur reale Vorgänge ab. Kann es sein, dass diese Abbildung im derzeitigen System mathematisch unvollständig ist? Inflation wird heute üblicherweise mithilfe von Neuverschuldung \u0026ldquo;gemacht\u0026rdquo;. Dies ist aber nicht die einzige Möglichkeit, sondern man könnte (nur ein Gedankenmodell kein Lösungsvorschlag!) auch einfach Geld drucken und pro Kopf verteilen. Der Unterschied wäre, dass in ersterem Fall die Schulden steigen, in zweitem Fall Schulden (und Guthaben) praktisch teil-entwertet würden. Konjunkturprogramme werden heute über Neuverschuldung finanziert und steigern somit die Nachfragemenge nach Kredit, was logischerweise das Zinsniveau erhöht. Viele Ökonomen bezweifeln, dass das Zusatz-Wachstum diesen Nebeneffekt überhaupt übersteigt und lehnen daher auch zu Recht derartige Konjunkturprogramme ab. Inflation wirkt zudem auf das Realzinsniveau: Wenn man bei z.B. 3% Inflation auf sein Sparbuch 1,5% Zinsen erhält, hat man eigentlich -1,5% Zinsen (minus!)[1]. Negative Renditen sind auch von der Volkswirtschaftstheorie gar nicht bestritten. Im Gegenteil, sie dienen auch dazu, um Überkapazitäten (hier also mehr Kreditangebot als Kreditnachfrage) abzubauen. So bekommen Stromerzeuger z.B. auch nachts ggf. kein Geld für ihre Einspeisung, sondern müssen dafür zahlen. Und ein Landwirt, der 100.000 Euro in eine Apfelplantage investiert und dann auf einen übersättigten Markt trifft, macht auch ggf. 50.000 Euro Verlust (also negativen Gewinn). Geldbasis (also z.B. Euro-Scheine) ist nominal immer zu 0% verzinst (man bekommt für einen Schein in der Tasche keine Zinsen und zahlt dafür keine). Aber auch auf Bargeld (bzw. jede Geldbasis) wirkt die Inflation, so dass bei positiver Inflation der Realzins auf Geldbasis negativ ist. In Rezessionen ist aber meist kaum Inflation vorhanden, bzw. sogar Deflation. D.h. in Zeiten, in denen das sonstige Zinsniveau niedrig ist, ist der Zins auf Geldbasis plötzlich sogar relativ hoch. Das bezeichne ich als die \u0026ldquo;Geld-Anomalie\u0026rdquo;. Denn hier definiert unser Geldsystem praktisch einen Mindestpreis von etwa 0% Zinsniveau (unter der Annahme das bei Rezession eben die Inflation ca. bei 0% ist). Unser BIP wächst seit ca. 60 Jahren inflationsbereinigt in etwa linear, sogar leicht unterlinear. D.h. mathematisch nimmt das prozentuale (jeweils auf das Vorjahr bezogene) BIP-Wachstum seit 60 Jahren laufend ab. Und das trotz aller Tricks, das BIP zu steigern (Neuverschuldung jeder Regierung). Wie wahnsinnig (oder mathematisch unbedarft) muss man eigentlich sein, um zu glauben, dass ohne Krieg oder sonstige Realkapitalzerstörung wieder 3% erreichbar wären? Brauchen wir also ein Wirtschaftssystem, welches auch mit Nullwachstum klar kommt und muss das Geldsystem hier unterstützen? Und was ist mit größeren Konjunkturschwankungen? Bei einem Wachstum von z.B. 5% könnte es mal einen Einbruch auf \u0026ldquo;nur\u0026rdquo; 2% geben, aber das wäre immer noch ein Wachstum. Doch was ist in einer älteren Volkswirtschaft wie wir sie haben, bei einer Basis von nur noch 1% Wachstum? Ein solcher Einbruch würde dann -2% \u0026ldquo;Wachstum\u0026rdquo; bedeuten - und schon brauchen wir ein Wirtschafts- und Geldsystem, welches auch (zumindest zeitweilig) schrumpfende Wirtschaft abbilden kann. Interessant auch, wenn man mal das BIP-Wachstum und das Bevölkerungswachstum vergleicht. Denn das oft fast Hand in Hand! Ob das Zufall ist? Und: Kann die Weltbevölkerung so weiterwachsen? Was passiert, wenn sie sogar schrumpft? Kann unser Geldsystem dieses abbilden? Zins als Ausgleich für Konsumverzicht (Zeitpräferenz in der Gegenwart), ok. Aber was, wenn gerade mehr Menschen für die Zukunft sparen wollen als Kredit aufnehmen wollen? Was passiert in einer alternden Bevölkerung, in der fast alle fürs Alter sparen wollen? Könnte es dann nicht Situationen geben, in denen man sogar dafür Geld erhalten müsste, wenn man jetzt konsumiert und dafür später leistet? Also die Zeitpräferenz in der Zukunft liegt, und somit ein negatives Gleichgewichtszinsniveau entstehen würde? Wenn man all das zusammen nimmt, dürfte klar sein, dass eine Wirtschaftspolitik ohne grundlegende Änderungen der Zentralbankgesetzgebung gar nicht funktionieren kann. Das aber wurde nun über ein halbes Jahrhundert lang versucht - mit logischerweise immer weiter steigenden Staatsschulden. Damit will ich aber nun nicht sagen, dass der Staat das Geld nach Gutdünken kontrollieren sollte. Lediglich sollte auch eine schrumpfende Wirtschaft von unserem Geldsystem abbildbar sein. Lösungsansätze gibt es viele. Doch das WIE würde ich auf später verschieben, dann wenn man dort ansetzt, streitet man jahrelang über den richtigen Weg. Wichtig ist zunächst einmal, zu verstehen, was in unserer Marktwirtschaft falsch läuft, zu erkennen, dass das was den Markt angeblich unsozial macht, genau genommen eine falsche Regulierung des Marktes ist. Dann müssten wir diese „Regulierung“ entweder fallen lassen (liberaler Ansatz) oder mit vermutlich enormen Kräften gegen den Markt kämpfen (sozialistischer Ansatz). Mir persönlich wäre der liberale Ansatz lieber.\nDer Artikel als PDF zum Download und Ausdrucken.\n[1] Genau genommen stimmt auch das nicht ganz exakt, weil sich bei Inflation die Maßeinheit verschiebt. Bei relativ geringer Inflation/Deflation ist der Unterschied jedoch minimal, das Vorzeichen bleibt in jedem Fall erhalten.\n Ökonomie","date":"September 30, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-09-30-gedanken-zur-geldtheorie-und-zum-gleichgewichtszinsniveau/","section":"Blog","summary":"Unter Systemkritik versteht man meist die kommunistische, die also das System „Marktwirtschaft“ kritisiert. In diesem Text möchte ich mit einigen lose zusammenhängenden Gedanken aufzeigen, an welcher Stelle unser System gerade nicht Marktwirtschaftlich ist, und eben durch eine fehlende Marktfreiheit unsoziale Tendenzen hat.","title":"Gedanken zur Geldtheorie und zum Gleichgewichtszinsniveau","type":"blog"},{"content":"Clean Code - Refactoring, Patterns, Testen und Techniken für sauberen Code von Martin, Robert C. Martin ist ein Programmierlehrbuch der anderen Art, denn es lehrt seinen Programmcode sauber schreiben, so dass er leicht testbar, leicht lesbar und leicht änderbar ist. Endlich einmal setzt ein Buch den Fokus auf das, was jede Software im Endeffekt ausmacht: den Programmcode – und warum dieser sauber sein muss und wie man dieses erreichen kann.\nMeiner Meinung nach sollte das Buch Pflichtlektüre eines jeden Programmierers sein, gleich nachdem man Programmieren an sich gelernt hat! Aber auch für erfahrene Programmierer bietet das Buch eine gute Zusammenfassung vieler Prinzipien und Praktiken, teils als Erinnerung, teils als neue Einsichten. Sicherlich ist es nicht nur produktiver, sonst macht auch viel mehr Spaß, mit Kollegen zusammen zu arbeiten, die dieses Buch gelesen haben.\nDie Beispiele, in Java codiert, zeigen für fast alle Codierungs-Prinzipien ein „suboptimales“ Beispiel, dass dann bezüglich des jeweils erklärten Prinzips bezüglich Testbarkeit, Lesbarkeit und Änderbarkeit optimiert wird. Dass die Beispiele zumeist real existierender OpenSource-Software entnommen wurden, verdeutlicht den Praxisbezug.\nDie deutsche Übersetzung ist weitestgehend sehr gut lesbar, eigentlich fand ich nur die ersten Seiten etwas arg holprig, was aber auch am Original liegen könnte - oder ich habe mich an den Stil gewöhnt. Einige Kapitel sind von Fremdautoren, diese weichen auch teilweise in der Qualität etwas ab.\nInhaltlich geht es nach einer allgemeinen Abhandlung über „sauberen Code“ u.a. um:\nNamensgebung (zweckbeschreibend, eindeutig, aussprechbar, suchbar, typflexibel, der Problem- bzw. Lösungsdomäne entsprechend, kontextfrei), dem Aufbau von und Verteilung auf Funktionen (Top-Down-Lesbarkeit, das Eine-Aufgabe-Prinzip, Funktionsargumente, Vermeidung von Nebeneffekten, Wiederholungen und Output-Parametern, der Verwendung von Exceptions etc.), Kommentare (oft Hinweis auf Verbesserungspotentiale, gute + schlechte Kommentare, Schnittstellendokumentation wie z.B. JavaDoc, Vermeidung von auskommentiertem Code etc.), Formatierung (vertikale und horizontale Anordnung und Dichte, Zeitungs-Metapher, Einrückung, Team-Regeln), Objekte- und Datenstrukturen (Datentransfer-Objekte, echte Objekte, Law-of-Demeter etc.), Fehler-Behandlung (besser Exceptions als Rückgabe-Codes, Try-Catch-Blöcke, Unchecked Exceptions, Anforderungen des Aufrufers beachten, Null-Werte etc.), Schnittstellen – er nennt es „Grenzen“ (Umgang mit Drittanbieter-Code, nicht existierendem Code), Unit-Tests (Test-Driven-Development, saubere Tests, domänenspezifische Testsprache, ein Konzept pro Test, das F.I.R.S.T.-Prinzip: Fast, Independent, Repeatable, Self-Validating, Timely), Klassen (Aufteilung auf Klassen, Skalierbarkeit, Factories und Dependency-Injection, Cross-Cutting-Concerns, Proxies AOP, Verwendung Standards), Nebenläufigkeit (falsche Vorstellungen, Herausforderungen, Testbarkeit, Single-Responsibility-Prinzip, Umgang mit Daten und Datenaustausch, Thread-Sicherheit, Deadlocks, Synchronisation, Testbarkeit etc.), Code-Smells und Heuristiken (hier folgt ein Regelwerk, angelehnt an das Buch Refactoring von Martin Fowler, das mich auch stark an die Checklisten der Evo-Methode von Tom und Kai Gilb erinnert), Erfreulich undogmatisch auch, dass der Autor nicht der Meinung ist, man müsse mit allen seinen Prinzipien einverstanden sein. Bei mir waren es allerdings wenige, doch sollte sich da jeder seine eigene Meinung bilden ohne das Ziel aus den Augen zu verlieren: Testbaren, lesbaren und änderbaren Programmcode. Nicht nur die nachfolgenden Kollegen werden es danken, auch sehr bald die Kunden und das Controlling.\nISBN 9783826655487 (deutsche Ausgabe), ISBM 9780132350884 (englische Originalausgabe)\n Software-Development Java CCD","date":"September 3, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-09-03-buchempfehlung-und-rezension-clean-code-von-robert-c-martin/","section":"Blog","summary":"Meiner Meinung nach sollte das Buch Pflichtlektüre eines jeden Programmierers sein, gleich nachdem man Programmieren an sich gelernt hat! Aber auch für erfahrene Programmierer bietet das Buch eine gute Zusammenfassung vieler Prinzipien und Praktiken, teils als Erinnerung, teils als neue Einsichten. Sicherlich ist es nicht nur produktiver, sonst macht auch viel mehr Spaß, mit Kollegen zusammen zu arbeiten, die dieses …","title":"Buchempfehlung und -Rezension: Clean Code von Robert C. Martin","type":"blog"},{"content":"","date":"September 3, 2009","externalUrl":null,"permalink":"/de/tags/CCD/","section":"Tags","summary":"","title":"CCD","type":"tags"},{"content":"Ein Echtzeitsystem garantiert, innerhalb einer definierten Zeit auf Signale oder Zustandswechsel zu reagieren. Ob diese Maximalzeit nun Mikrosekunden, Millisekunden, Sekunden oder gar Minuten sind, ist irrelevant. Doch kann man in Java-Programmen eine solche Maximalzeit garantieren?\nDie Probleme bei Java für Echtzeit-Systeme liegen in mehreren Bereichen, z.B.:\nDer Garbage-Collector könnte jederzeit unkontrollierbar die Abarbeitung verzögern. Der Hotspot-Compiler könnte ebenfalls unkontrollierbar die Abarbeitung verzögern. Das Java Threading-Modell kennt zwar Prioritäten, garantiert diese aber nicht. Es gibt spezielle Realtime-JVMs, welche diese Probleme beheben, diese sind jedoch auch teuer und erfordern meist das Erlernen neuer Sprachelemente (heute oft in Form von Annotationen). Bei harten Realtime-Bedinungen wird man aber nicht ohne solche auskommen, und vor allem auch ein Realtime-Betriebssystem benötigen. Wobei harte Realtime-Bedingungen bedeuten, dass eine zu späte Reaktion einen Fehler darstellt (z.B. überhitzter Kernreaktor, der zu spät abgeschaltet wird).\nBei weichen Realtime-Bedingungen sieht das schon anders aus. Unter weiche Realtime-Bedingungen fällt entweder der Wert der Reaktion nach Überschreitung der Maximalzeit, bis hin zur Wertlosigkeit, aber das Gesamt-System kann noch sinnvoll darauf reagieren - z.B. indem Messwerte verworfen werden. In einem solchen Umfeld reicht es also aus, jeweils zu wissen, ob die Reaktion schnell genug war oder nicht. Bis hinunter in den Millisekunden-Bereich ist eine herkömmliche JVM für solche Systeme durchaus eine Alternative.\nOft muss bei Realtime-Systemen auf Interrupt-Requests reagiert werden. Diese werden üblicherweise von einem Modul direkt im Betriebssystem-Kernel mit einem Interrupt-Handler angenommen und ggf., z.B. über den Datenstrom eines Gerätetreibers an eine Anwendung weitergeleitet. Hier müsste die Zeit zwischen dem Annehmen des Interrupts und der fertigen Bearbeitung in der (Java-) Anwendung gemessen werden.\nIm Interrupt-Handler könnte man so als erstes einen Zeitstempel speichern:\nstatic struct timespec timestamp; static irqreturn_t mydriver_irq_handler(int irq, void *requested_devid) { ... // check if irq is really for us getnstimeofday(\u0026amp;timestamp); ... wake_up_interruptible(\u0026amp;queue); // wake read task. return IRQ_HANDLED; } Wenn es sich nicht um einen Realtime-Kernel handelt, ist allerdings zu beachten, dass schon der Interrupt-Handler selbst um eine unbestimmte Zeit verzögert sein kann. Falls durch Berücksichtigung zu später Reaktionen Schäden entstehen können, muss also ggf. mindestens ein Realtime-Kernel verwendet werden.\nDieser würde dann z.B. dem Datenstrom zur Anwendung hinzugefügt:\nstatic ssize_t mydriver_read(struct file *file, char *buf, size_t count, loff_t * ppos) { interruptible_sleep_on(\u0026amp;queue); char out[32]; int len = snprintf(out, 32, \u0026#34;IRQ@%ld%09ld\\n\u0026#34;, (long) timestamp.tv_sec, timestamp.tv_nsec); if ( count \u0026lt; len ) { // buffer too small - this should not happen return 0; } copy_to_user(buf, out, len); return len; } In der Java-Anwendung muss nun dieser Zeitstempel (hier in Nanosekunden) mit einem Zeitstempel vergleichen werden, der nach der abgeschlossenen Reaktion angefertigt wurde. Dies könnte z.B. so aussehen (produktiv vermutlich eher in einem Thread):\nstatic final long MAX_NANOSECS_DELAY = 20000000L; public static void main(String[] args) throws IOException { NanoSecondsTimestampProvider timestamper = new NanoSecondsTimestampProvider(); System.out.printf( \u0026#34;Deviation: %dns\\n\u0026#34;, timestamper.getNanoSecondsDeviation() ); LineNumberReader in = new LineNumberReader( new InputStreamReader( new FileInputStream(\u0026#34;/dev/mydevice\u0026#34;) ) ); String line; while ( null != (line = in.readLine()) ) { Result result = process(); // appplication specific long curNanoSecs = timestamper.currentNanoSecondsTimestamp(); long irqNanoSecs = Long.parseLong(line.substring(4, line.length())); long delayNanoSecs = curNanoSecs-irqNanoSecs; if ( delayNanoSecs \u0026gt; MAX_NANOSECS_DELAY ) { logger.warn(\u0026#34;processing too slow ...\u0026#34;); } else { useResult(result); // appplication specific } } } (Wie man in Java einen absoluten Zeitstempel mit Nanosekunden-Auflösung und einer Genauigkeit im Mikrosekunden-Bereich programmieren kann, hatte ich in einem vorherigen Artikel beschrieben.)\nIn dem Java-Teil der Anwendung ist zu beachten, dass bei der Zeitmessung garantiert sein muss, dass die Reaktion wirklich abgeschlossen ist. Wird also z.B. mit Fremdsystemen kommuniziert, muss sichergestellt sein, dass das jeweilige Fremdsystem die Nachricht auch bereits verarbeitet hat - z.B. durch Empfang einer Rückmeldung. Oft ist es besser eine Reaktion zu verwerfen, als eine Reaktion gelten zu lassen, die eigentlich zu spät abgeschlossen war.\nDer Weg vom Interrupt-Handler bis zur Java-Anwendung kostet auf meinem Arbeitssystem (Intel(R) Pentium(R) 4 Dual Core CPU 2.80GHz, Linux 2.6.28-15 unter Ubuntu 9.04 mit Sun Java (R) Version \u0026ldquo;1.6.0_15\u0026rdquo; Java HotSpot(R) Client VM) mit 0,1-05 Millisekunden. Dazu kommt dann eben noch die eigentliche Verarbeitung.\nUm zur anfänglichen Frage zurückzukommen: Zwar kann man mit herkömmlichen JVMs keine harten Realtime-Bedingungen erfüllen, aber weiche Realtime-Bedinungen im Millisekunden-Bereich sind durchaus erfüllbar.\n Software-Development Java","date":"August 24, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-24-java-und-echtzeitsysteme/","section":"Blog","summary":"Ein Echtzeitsystem garantiert, innerhalb einer definierten Zeit auf Signale oder Zustandswechsel zu reagieren. Ob diese Maximalzeit nun Mikrosekunden, Millisekunden, Sekunden oder gar Minuten sind, ist irrelevant. Doch kann man in Java-Programmen eine solche Maximalzeit garantieren?","title":"Java in Echtzeitsystemen","type":"blog"},{"content":"Während meiner aktuellen Arbeit an einem Programm mit weichen Realtime-Bedingungen (soft real-time system) kam ich in die Verlegenheit, wissen zu müssen, wie viel Zeit zwischen dem Auftreten eines Interrupts in einem Linux-Kernel-Modul und seiner Verarbeitung im Java-Programm vergangen ist, weil das Ergebnis eines Zyklus, der über 200ms benötigte, verworfen werden müssen. Java bietet aber nur einen absoluten Zeitstempel in Millisekunden und nur einen bezugslosen Zeitstempel in Nanosekunden.\nAngenommen, der Millisekunden-Wechsel von System.currentTimeMillis(), also dessen Erhöhung um 1, geschieht wirklich auf einer Grenze (restliche 6 Stellen zu Nanosekunden also 000000), dann kann mit folgender Klasse ein absoluter Nanosekunden Zeitstempel ermittelt werden:\nclass NanoSecondsTimestampProvider { private long nanoSecondsOffset, nanoSecondsError; public NanoSecondsTimestampProvider() { long curMilliSecs0, curMilliSecs1, curNanoSecs, startNanoSecs, endNanoSecs; do { startNanoSecs = System.nanoTime(); curMilliSecs0 = System.currentTimeMillis(); curNanoSecs = System.nanoTime(); curMilliSecs1 = System.currentTimeMillis(); endNanoSecs = System.nanoTime(); } while ( curMilliSecs0 == curMilliSecs1 ); nanoSecondsOffset = 1000000L*curMilliSecs1 - curNanoSecs; nanoSecondsError = endNanoSecs - startNanoSecs; } public long getNanoSecondsDeviation() { return nanoSecondsError; } public long currentNanoSecondsTimestamp() { return System.nanoTime() + nanoSecondsOffset; } } Auf meinem Arbeitssystem ergibt sich dabei eine Abweichung von 2-4 Mikrosekunden, d.h. die Genauigkeit der Timestamps wird gegenüber System.currentTimeMillis() um den Faktor 250-500 verbessert.\nFür den Produktivbetrieb sollte evtl. noch der Konstruktor der Klasse in der Laufzeit beschränkt werden (falls aufgrund der Systemumstände ein Millisekunden-Wechsel zu selten ist) und für die Genauigkeit der Kalibration könnte ein Zielwert angegeben werden. Es könnte dann also z.B. 1 Sekunde lang probiert werden, eine Genauigkeit von 1 Mikrosekunde zu erreichen.\nNatürlich steht und fällt die tatsächliche Vergleichbarkeit mit dem echten Zeitstempel, der im Linux Kernel mit getnstimeofday(struct timestamp*) ermittelt werden kann mit der o.g. Annahme über den Wechsel des Millisekunden-Timestamps.\n Software-Development Java","date":"August 21, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-21-absoluter-nanosekunden-zeitstempel-in-java/","section":"Blog","summary":"Während meiner aktuellen Arbeit an einem Programm mit weichen Realtime-Bedingungen (soft real-time system) kam ich in die Verlegenheit, wissen zu müssen, wie viel Zeit zwischen dem Auftreten eines Interrupts in einem Linux-Kernel-Modul und seiner Verarbeitung im Java-Programm vergangen ist, weil das Ergebnis eines Zyklus, der über 200ms benötigte, verworfen werden müssen. Java bietet aber nur einen absoluten Zeitstempel in Millisekunden …","title":"Absoluter Nanosekunden-Zeitstempel in Java","type":"blog"},{"content":"Gerade bin ich dabei das Eclipse auf meinem derzeit recht selten verwendete Homeoffice-Computer auf Galileo (Eclipse 3.5) zu bringen. Da das Upgrade eines solchen Versionssprung wohl erst ab der 3.5 auf höhere Versionen gehen soll, fällt also mal wieder das Nachinstallieren aller Plugins an. Einige davon hatte ich in anderen Blog-Artikeln erwähnt, doch hier mal alle aktuellen Lieblings-Plugins zusammen:\nSubclipse (Website) # Zwar sind in Eclipse 2.5 8Galileo) schon Teile von Subversive enthalten, aber die Connectoren muss man immer noch nachinstallieren - warum auch immer. Mir gefällt nach wie vor Subclipse besser: Subclipse Update Site.\nm2eclipse (Website) # Die Maven-Integration für Eclipse bietet eine hervorragende Integration bei den folgenden Features:\nMaven builds aus der Eclipse IDE heraus starten auf der Maven-pom.xml basierende Eclipse-Abhängigkeitsverwaltung Auflösen von Maven-Abhängigkeiten direkt aus der IDE, ohne ein lokales Maven Repository Automatisches herunterladen der benötigten Abhängigkeiten von remote Maven Repositories Wizards, um Maven-Projekte zu erstellen, oder aus einem Java-Projekt ein Maven-Projekt zu machen Schnelle Suche nach Abhängigkeiten in remote Maven Repositories Unterstützung der Quick-Fixes im Editor durch Maven-Repositories m2eclipse Update-Site\nMoreUnit (Wegbsite) # Hilfreiche Unterstützung beim Arbeiten mit Unit-Tests bietet dieses Plugin:\nMarkieren von Klassen, für die es eine Testklasse gibt Markieren von Methoden, für die es einen Testfall gibt Springen zwischen Produktiv-Klasse und Testklasse via Menü oder Tastenbefehl Umbenennen von Klassen/Methoden beim Refactoring passt auch die Namen der Tests an Verschieben von Klassen in andere Pakete verschiebt automatisch auch die Tests Einfaches Erzeugen von Testmethoden-Stubs via Menü oder Tastenbefehl MoreUnit Update-Site\nEclEmma (Website) # Code-Coverage Ermittlung von JUnit und TestNG Tests direkt aus der Eclipse IDE mit Highlighting des Coverage-Status direkt im Editor und einer übersichtlichen Auswertung mit direkter Sprungmöglichkeit zu den jeweils zugehörigen Sourcen.\nEclEmma Update-Site\n Software-Development Eclipse Maven","date":"August 15, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-15-meine-top-eclipse-plugins/","section":"Blog","summary":"Gerade bin ich dabei das Eclipse auf meinem derzeit recht selten verwendete Homeoffice-Computer auf Galileo (Eclipse 3.5) zu bringen. Da das Upgrade eines solchen Versionssprung wohl erst ab der 3.5 auf höhere Versionen gehen soll, fällt also mal wieder das Nachinstallieren aller Plugins an. Einige davon hatte ich in anderen Blog-Artikeln erwähnt, doch hier mal alle aktuellen Lieblings-Plugins zusammen:","title":"Meine Top-Eclipse-Plugins","type":"blog"},{"content":"Bei Verwendung von Maven ist die Ziel-Versionsnummer des eigenen Programms in der pom.xml festgelegt. Selbstverständlich kann man auch aus dem Programm heraus mit Java darauf zugreifen, denn Maven erzeugt eine pom.properties Datei unterhalb von META-INF, in der u.a. die Versionsnummer und der Zeitstempel des Build steht:\n#Generated by Maven #Fri Jul 31 15:49:26 CEST 2009 version=0.1.14 groupId=de.hoennig.samples artifactId=maven-props Mit dem folgenden Programmcode können diese Informationen ausgelesen werden:\npackage ...; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; /** Access to the Maven pom.properties file. * * The complete path where Maven puts its properties: * /META-INF/maven/GRPUP_ID/ARTIFACT_ID/pom.properties */ public class MavenProperties { private static final String PATH_PREFIX = \u0026#34;/META-INF/maven/\u0026#34;; private static final String PATH_POSTFIX = \u0026#34;/pom.properties\u0026#34;; final String propertiesPath; private String versionTimestamp = \u0026#34;\u0026#34;; private String versionNumber = \u0026#34;\u0026#34;; /** Uses the resource path from classForResource to load the pom.properties of the given groupID and artifactID. */ public MavenProperties(final String groupID, final String artifactID ) { propertiesPath = PATH_PREFIX + groupID + \u0026#34;/\u0026#34; + artifactID + PATH_POSTFIX; try { final InputStream resourceStream = getResourceAsStream(propertiesPath); LineNumberReader reader = new LineNumberReader( new InputStreamReader(resourceStream)); // skip \u0026#34;#Generated by Maven\u0026#34; reader.readLine(); // read \u0026#34;#timestamp\u0026#34; versionTimestamp = reader.readLine().substring(1); // find \u0026#34;version=...\u0026#34; String line; while ( null != ( line = reader.readLine() ) ) { String[] parts = line.split(\u0026#34;[ \\t]*=[ \\t]*\u0026#34;); if ( \u0026#34;version\u0026#34;.equals(parts[0]) ) { versionNumber = parts[1]; break; } } reader.close(); } catch (Exception e) { // can\u0026#39;t load pom.properites =\u0026gt; version information will be unavailable } } /// allows for overriding the resource loading, e.g. in tests protected InputStream getResourceAsStream(String propertiesPath) { return getClass().getResourceAsStream(propertiesPath); } /** Returns the timestamp from the Maven generated pom.properies or \u0026#34;\u0026#34; if not available. **/ public String getVersionTimestamp() { return versionTimestamp; } /** Returns the version number from the Maven generated pom.properies or \u0026#34;\u0026#34; if not available. **/ public String getVersionNumber() { return versionNumber; } } Anzumerken sei, dass der Zeitstempel unstrukturiert als Kommentar gespeichert ist und nicht zu 100% erkannt werden kann. Falls das von Maven generierte Format geändert wird, wird dieser Zugriff leider nicht mehr funktionieren. Wer den Zeitstempel gar nicht braucht, kann natürlich den Stream auch mit einer java.util.Properties-Instanz einlesen.\nDie Methode, welche die Resource lädt, kann überladen werden, was evtl. nötig ist, wenn die Klasse MavenProperties von einem fremden Classloader geladen wurde - auch für einen Unit-Test hatte ich dies benötigt, da sich java.lang.Class nur mit recht \u0026ldquo;üblen\u0026rdquo; Tricks mocken lässt.\n Software-Development Java Maven","date":"August 12, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-12-maven-versionsnummer-aus-pom-xml-und-build-zeitstempel-mit-java-auslesen/","section":"Blog","summary":"Bei Verwendung von Maven ist die Ziel-Versionsnummer des eigenen Programms in der pom.xml festgelegt. Selbstverständlich kann man auch aus dem Programm heraus mit Java darauf zugreifen, denn Maven erzeugt eine pom.properties Datei unterhalb von META-INF, in der u.a. die Versionsnummer und der Zeitstempel des Build steht:","title":"Maven: Versionsnummer aus pom.xml und Build-Zeitstempel mit Java auslesen","type":"blog"},{"content":"Viele Experten empfehlen, besser viele kurze Methoden zu programmieren als wenige lange Methoden. Dieselbe Regel kann auch aus anderen Empfehlungen abgeleitet werden. So führen z..B das Prinzip One level of abstraction und das Single Responsibility Principle zu kurzen Methoden. Und auch die Empfehlung, dass die im Quellcode verwendeten Bezeichner, also auch Methodennamen, ihr Subjekt ausreichend dokumentieren sollten, läuft auf kurze Methoden hinaus.\nEin häufiges Gegenargument ist, die Performance sei aufgrund der vielen Funktionsaufrufe schlechter. Nun, auch hier gibt es wieder eine Empfehlung: Vorsicht vor Optimierungen - insbesondere dann, wenn Optimierungen den Quellcode schwerer lesbar, schwerer testbar oder schwerer änderbar machen, was fast immer der Fall ist.\nRobert C. Martin hat in seinem Buch Clean Code (deutsche Ausgabe im mitp Verlag, 2009) im Kapitel 2 das folgende (hier leicht angepasste) Programmbeispiel, einmal in einer Methode:\nprivate static String formatStatistics(char candidate, int count) { String number; String verb; String pluralModifier; if ( count == 0 ) { number = \u0026#34;no\u0026#34;; verb = \u0026#34;are\u0026#34;; pluralModifier = \u0026#34;s\u0026#34;; } else if ( count == 1 ) { number = \u0026#34;one\u0026#34;; verb = \u0026#34;is\u0026#34;; pluralModifier = \u0026#34;\u0026#34;; } else { number = Integer.toString(count); verb = \u0026#34;are\u0026#34;; pluralModifier = \u0026#34;s\u0026#34;; } return String.format(\u0026#34;There %s %s %s%s.\u0026#34;, verb, number, candidate, pluralModifier); } Und einmal aufgeteilt auf mehrere Methoden.\nstatic class StatisticsFormatter { char candidate; int count; String number; String verb; String pluralModifier; public StatisticsFormatter(char candidate, int count) { this.candidate = candidate; this.count = count; createPluralDependentMessageParts(count); } private final void createPluralDependentMessageParts(int count) { if ( count == 0 ) { thereAreNoLetters(); } else if ( count == 1 ) { thereIsOneLetter(); } else { thereAreMultipleLetters(); } } private final void thereAreNoLetters() { number = \u0026#34;no\u0026#34;; verb = \u0026#34;are\u0026#34;; pluralModifier = \u0026#34;s\u0026#34;; } private final void thereIsOneLetter() { number = \u0026#34;one\u0026#34;; verb = \u0026#34;is\u0026#34;; pluralModifier = \u0026#34;\u0026#34;; } private final void thereAreMultipleLetters() { number = Integer.toString(count); verb = \u0026#34;are\u0026#34;; pluralModifier = \u0026#34;s\u0026#34;; } public final String format() { return String.format(\u0026#34;There %s %s %s%s.\u0026#34;, verb, number, candidate, pluralModifier); } } Anhand dieses Beispiels habe ich nun getestet, wie groß der Performance-Unterschied mit Java 1.6.14 ist. Das Ergebnis ist erstaunlich. Für 10.400.000 Durchläufe (4000 mal 26 Buchstaben als jeweils mit Count 0-99) mit verschiedenen candidata/count-Kombinationen, aber in beiden Fällen denselben, kommt die 1-Methoden Variante auf 277,893s, und die mehr-Methoden-Variante auf 278,449s, also ein Unterschied in der Größenordnung eines Bruchteil eines Prozents.\nWeniger Schleifendurchläufe führten außerdem zu Messwerten, bei denen mal die 1-Methoden-Variante und mal die mehr-Methoden-Variante schneller war. Fakt ist jedenfalls, die HotSpot-VM leistet gute Arbeit und durch Vermeidung von Funktionsaufrufen, die Performance optimieren zu wollen, ist allenfalls reine Illusion.\n Software-Development Java CCD","date":"August 11, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-11-performance-viele-kurze-methoden-vs-wenige-lange-methoden/","section":"Blog","summary":"Viele Experten empfehlen, besser viele kurze Methoden zu programmieren als wenige lange Methoden. Dieselbe Regel kann auch aus anderen Empfehlungen abgeleitet werden. So führen z..B das Prinzip One level of abstraction und das Single Responsibility Principle zu kurzen Methoden. Und auch die Empfehlung, dass die im Quellcode verwendeten Bezeichner, also auch Methodennamen, ihr Subjekt ausreichend dokumentieren sollten, läuft auf kurze …","title":"Performance: Viele kurze Methoden vs. wenige lange Methoden","type":"blog"},{"content":"Scrum ist ein agiles Projektmanagement-System in dessen Zentrum der mit der eingesetzten Arbeitsleistung geschaffene Geschäftswert steht. Sicherlich sind auch einzelne Elemente aus Scrum oft hilfreich, doch so nützlich eine Klingel und eine Handbremse sein mögen, machen selbst eine Klingel und eine Handbremse zusammen noch kein Fahrrad. Um festzustellen, ob ein Team wirklich nach Scrum arbeitet oder nicht, gibt es den sogenannten Nokia-Test. Dieser gliedert sich in einen Test, ob überhaupt ein iterativer Prozess gelebt wird:\nWird das Projekt in Iterationen mit feststehendem Anfang und Ende von bis zu 6 Wochen Länge durchgeführt?\n(Die Antwort sollte ja sein.) Gibt es am Anfang jeder Iteration funktionierende Software?\n(Die Antwort sollte ja sein.) Gibt es am Anfang jeder Iteration eine vollständige und detaillierte Spezifikation der in der Iteration zu realisierenden User-Stories?\n(Die Antwort sollte nein sein.) Findet das Testing in jeder Iteration parallel zur Entwicklung statt?\n(Die Antwort sollte ja sein.) Und in einen Test, ob dieser iterative Prozess Scrum ist:\nIst eine definierte Person, die für das Produkt verantwortlich ist und den Kunden repräsentiert (Product-Owner), jederzeit für Fragen verfügbar?\n(Die Antwort sollte ja sein.) Gibt es eine vom Product-Owner priorisierte Liste der von ihm benötigten (für ihn wertvollen) User-Stories, deren Größe vom Team laufend geschätzt wird (Product-Backlog)?\n(Die Antwort sollte ja sein.) Gibt es während der Iteration eine Übersicht darüber, welche User-Stories bereits vollständig realisiert sind (Burn-Down-Chart)?\n(Die Antwort sollte ja sein.) Kann sich das Team voll auf die aktuelle Iteration konzentrieren, ohne von Managern oder Kunden unterbrochen zu werden, und gibt es jemanden, der externe Probleme vom Team fern hält (Scrum Master)?\n(Die Antwort sollte ja sein.) Ich selbst hatte noch nicht das Glück, in einem solchen Umfeld arbeiten zu können. Am nächsten kam dem ein Projekt, bei dem es keinen Scrum-Master und keinen dedizierten Product-Owner gab. Das Projekt lief dennoch sehr gut, aber es wurde auch deutlich, dass es so ein einem größeren Projekt nicht mehr funktioniert hätte. Auf jeden Fall ist zu bedenken, dass man daraus, dass einzelne Elemente alleine noch keine große Besserung bringen, kaum auf die Mächtigkeit von Scrum zu schließen ist. Auch eine fehlende Kugel im Kugellager eines Fahrrades macht Fahrradfahren unnötig anstrengend - und bricht bald das ganze Rad.\n Software-Development Agile Scrum","date":"August 9, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-09-der-nokia-test-scrum-oder-nicht-scrum/","section":"Blog","summary":"Scrum ist ein agiles Projektmanagement-System in dessen Zentrum der mit der eingesetzten Arbeitsleistung geschaffene Geschäftswert steht. Sicherlich sind auch einzelne Elemente aus Scrum oft hilfreich, doch so nützlich eine Klingel und eine Handbremse sein mögen, machen selbst eine Klingel und eine Handbremse zusammen noch kein Fahrrad. Um festzustellen, ob ein Team wirklich nach Scrum arbeitet oder nicht, gibt es den …","title":"Der Nokia-Test: Scrum oder nicht Scrum?","type":"blog"},{"content":"","date":"August 9, 2009","externalUrl":null,"permalink":"/de/tags/Scrum/","section":"Tags","summary":"","title":"Scrum","type":"tags"},{"content":"Clean Code Developer (CCD) ist eine Initiative, die es sich zum Ziel gesetzt hat, Prinzipien und Praktiken aufzustellen, die zu besserer Software führen sollen. Die beiden Initiatoren kommen aus der .NET Welt kommen. Die CCD Initiative öffnet sich jedoch immer mehr, und die Prinzipien und Praktiken sind auch zumindest für alle objektorientierten Programmiersprachen gültig. Lediglich die Tools sind logischerweise weitestgehend sprachabhängig.\nCCD fasst viele Prinzipien, wie z.B. DRY (Don\u0026rsquo;t repeat yourself), KISS (Keep it simple, stupid), DI (Dependency Injection), SoC (Separation of Concern) etc. sowie Praktiken, wie z.B. Root Cause Analysis, die Verwendung eines Versionskontrollsystems, Refactoring, Unit-Testing, Continuous Integration and Deployment etc. zu insgesamt 5 Phasen zusammen, die im CCD Grade genannt werden und denen je eine Farbe zugeordnet ist. Ergänzt werden diese 5 Grade durch eine Interessenten-Phase (schwarz) und eine Phase, in der alle Prinzipien und Praktiken berücksichtigt werden (weiß). Wer mit CCD beginnt, wird zunächst mit dem roten Grad beginnen, und sich dann über orange, gelb, grün und blau weiter vor arbeiten. Dann folgt eine Phase, der weiße Grad, in dem für 21 Tage alle zuvor geübten Praktiken und Prinzipien gleichgewichtig in die tägliche Arbeit integriert werden. Um zu verhindern, dass einzelne Prinzipien und Praktiken dabei nach und nach wieder aus der täglichen Arbeit verschwinden, wird vorgeschlagen, nach diesen 21 Tage im weißen Grad wieder von vorne zu beginnen und so auf die jeweiligen Schwerpunkte der einzelnen Grade den Fokus setzen zu können.\nSo gesehen stellen auch die Grade keine Rangfolge dar, schon gar nicht für den jeweiligen Entwickler, der gerade an einem der Grade arbeitet. Mir persönlich hätte daher auch der Begriff \u0026ldquo;Phase\u0026rdquo; besser gefallen. Welche Prinzipien und Praktiken zu welchem Grad gehören, habe ich in Form eines Posters zusammengestellt: ODF Draw (z.B. für OpenOffice.org Draw), PDF (A4), PNG (A2).\nZu CCD gibt es eine Website und eine XING-Gruppe.\nFür mich ist CCD ein interessanter Ansatz, bessere Software zu entwickeln. Durch die strukturierte Gruppierung der verschiedenen Prinzipien und Praktiken, eingebettet in ein Modell, diese Phasen zu durchlaufen, könnte sich mehr entwickeln, als nur die Summe der Einzelteile. Auch könnte CCD ein Baustein bei der Zusammenstellung hochproduktiver Entwickler-Teams sein, denn schon in einem Team von Entwicklern mit gleichermaßen hohen Ansprüchen an ihre Arbeitsergebnisse sind die Reibungsverluste einfach geringer.\n Software-Development Agile CCD","date":"August 8, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-08-08-clean-code-developer-ccd/","section":"Blog","summary":"Clean Code Developer (CCD) ist eine Initiative, die es sich zum Ziel gesetzt hat, Prinzipien und Praktiken aufzustellen, die zu besserer Software führen sollen. Die beiden Initiatoren kommen aus der .NET Welt kommen. Die CCD Initiative öffnet sich jedoch immer mehr, und die Prinzipien und Praktiken sind auch zumindest für alle objektorientierten Programmiersprachen gültig. Lediglich die Tools sind logischerweise weitestgehend …","title":"Clean Code Developer (CCD)","type":"blog"},{"content":"Um es gleich vorweg zu sagen: Einzel-Pin-Steuerung in Java ist nicht sinnvoll möglich, da solche Funktionalität in einen Betriebssystem-Kernel gehört. Auf vielen modernen Betriebssystemen ist ein solcher Zugriff auch gar möglich bzw. spätestens das Interrupt-Handling dem Kernel vorbehalten. Der Lösungsweg soll daher auch genau auf diesem Wege erfolgen, also in Form eines Character-Device-Treibers für Linux 2.6.x. Dieser wird dann von Java ganz normal, wie jedes andere Character-Device auch, über normales File-IO angesprochen.\nGute Einführungen in Linux-Kernel-Treiber sind hier zu finden:\nhttp://www.freesoftwaremagazine.com/articles/drivers_linux http://www.captain.at/programming/rtai/parportint.php\n(benötigt den RTAI-Kernel-Patch) http://www.xml.com/ldd/chapter/book/ch09.html http://www.netobjectdays.org/.../hochberger.pdf Zu beachten ist, dass sich in der 2.6er Kernel-Serie die Schnittstelle zu den Interrupt-Handlern geändert hat und daher einige Anleitungen nicht mehr so ganz passen.\n Software-Development Java","date":"July 28, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-28-einzel-pin-steuerung-der-parallelen-schnittstelle-mit-java/","section":"Blog","summary":"Um es gleich vorweg zu sagen: Einzel-Pin-Steuerung in Java ist nicht sinnvoll möglich, da solche Funktionalität in einen Betriebssystem-Kernel gehört. Auf vielen modernen Betriebssystemen ist ein solcher Zugriff auch gar möglich bzw. spätestens das Interrupt-Handling dem Kernel vorbehalten. Der Lösungsweg soll daher auch genau auf diesem Wege erfolgen, also in Form eines Character-Device-Treibers für Linux 2.6.x. Dieser wird dann von …","title":"Einzel-Pin-Steuerung der parallelen Schnittstelle mit Java","type":"blog"},{"content":"In Unit-Tests werden immer wieder sogenannte Mock-Objekte benötigt, um zum vereinfachte Versionen der Objekte zu haben, mit dem der Test-Kandidat kommuniziert. Diese müssen aber zum einen sinnvoll auf Methoden-Aufrufe reagieren, so dass der Test-Kandidat überhaupt funktionieren kann. Und zum anderen muss teilweise geprüft werden, ob der Test-Kandidat an diesen Objekten bestimmte Methoden mit bestimmten Parametern gerufen hat.\nMit Mockito sieht ein solcher Unit-Test z.B. wie folgt aus:\nimport org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.mockito.Mockito.when; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Matchers.anyInt; class TestCase { // \u0026#34;Model\u0026#34; könnte ein Interface sein, // für das ein Mock-Objekt benötigt wird @Mock Model model; @Before public void before() { // bezieht sich auf obige(s) @Mock MockitoAnnotations.initMocks(this); @Test public void test() { // wenn model.getPeriod() gerufen wird, // soll der Mock 15 zurück liefern when(model.getPeriod()).thenReturn(15); // wenn model.getDelay() mit irgend einem // int-Paramter gerufen wird, // soll der Mock 1 zurück liefern when(model.getDelay(anyInt)).thenReturn(1); // wenn model.run(1) gerufen wird, // soll der Mock 20 zurück liefern when(model.run(1)).thenReturn(20); // der eigentliche Test new TestKandidat(model).run(22); // sollte mindestens einmal // mit irgendeinem Parameterwert // aufgerufen worden sein verify(model, atLeast(1)).setPeriod(anyInt()); // model.run(1) sollte genau einmal aufgerufen worden sein verify(model, times(1)).run(1); // mode.fail() sollte gar nicht aufgerufen worden sein verify(model, never()).fail(); } Ein Vergleich von EasyMock mit Mockito findet sich hier und eine ausführlichere Dokumentation zu Mockito findet sich hier.\nMockito kann außerdem auch über Maven bezogen werden (ggf. auf neue Versionen achten):\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.mockito\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;mockito-all\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.8.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt;  Software-Development Java Maven","date":"July 28, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-28-unit-tests-mit-mockito-mocks/","section":"Blog","summary":"In Unit-Tests werden immer wieder sogenannte Mock-Objekte benötigt, um zum vereinfachte Versionen der Objekte zu haben, mit dem der Test-Kandidat kommuniziert. Diese müssen aber zum einen sinnvoll auf Methoden-Aufrufe reagieren, so dass der Test-Kandidat überhaupt funktionieren kann. Und zum anderen muss teilweise geprüft werden, ob der Test-Kandidat an diesen Objekten bestimmte Methoden mit bestimmten Parametern gerufen hat.","title":"Unit-Tests mit Mockito Mocks","type":"blog"},{"content":"Jemmy ist ein hervorragendes Werkzeug zur Unterstützung von Unit-Tests von Swing-basierten-Anwendungen. Doch durch einen neuen Test, hing ein zuvor durchlaufender Testfall plötzlich tief unter JButtonOperator.clickMouse(), genau genommen in Queue.invokeAndWait().\nAbhilfe schafft dieses Statement bei der Test-Initialisierung:\nJemmyProperties.setCurrentDispatchingModel( JemmyProperties.getCurrentDispatchingModel() - JemmyProperties.getCurrentDispatchingModel() \u0026amp; JemmyProperties.SHORTCUT_MODEL_MASK); Damit wird die AWT Queue auf eine andere Art bedient. Näheres dazu ist auf der Jemmy-Website erläutert.\n Software-Development Java","date":"July 28, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-28-wenn-jemmy-bei-clickmouse-in-invokeandwait-hangt/","section":"Blog","summary":"Jemmy ist ein hervorragendes Werkzeug zur Unterstützung von Unit-Tests von Swing-basierten-Anwendungen. Doch durch einen neuen Test, hing ein zuvor durchlaufender Testfall plötzlich tief unter JButtonOperator.clickMouse(), genau genommen in Queue.invokeAndWait().","title":"Wenn Jemmy bei clickMouse() in invokeAndWait() hängt","type":"blog"},{"content":"Der aktuelle Stacktrace kann in Java ganz einfach mit Thread.currentThread().getStackTrace(); ermittelt werden. Doch darin enthalten ist dann auch die Methode getStackTrace() selbst.\nDiese kleine Hilfklasse schneidet diesen irrelvanten Teil des Stacktraces ab:\n/// convenience class to access the current stack trace public class StackTrace { private static final int classLen = \u0026#34;class\u0026#34;.length(); private static final String clazz = StackTrace.class.toString().substring(classLen+1); private StackTraceElement[] stack; private int offset; /// takes a snapshot of the current stack trace public StackTrace() { stack = Thread.currentThread().getStackTrace(); offset = 0; while ( !clazz.equals(stack[offset].getClassName()) ) { ++offset; } while ( clazz.equals(stack[offset].getClassName()) ) { ++offset; } } /** returns the n-th stack trace element, only counting frames above this class itself **/ public StackTraceElement getStackTrace(int n) { return stack[n+offset]; } } Verwendet wird die Klasse, wie in folgendem kleinen JUnit Test dargestellt:\nimport org.junit.Test; import static org.junit.Assert.*; public class StackTraceTest { @Test public void getStackTraceTest() { test1(); } private static void test1() { test2(); } private static void test2() { StackTrace trace = new StackTrace(); StackTraceElement frame0 = trace.getStackTrace(0); assertEquals( \u0026#34;test2\u0026#34;, frame0.getMethodName().toString() ); StackTraceElement frame1 = trace.getStackTrace(1); assertEquals( \u0026#34;test1\u0026#34;, frame1.getMethodName().toString() ); } }  Software-Development Java","date":"July 22, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-22-aktuellen-stack-frame-ermitteln/","section":"Blog","summary":"Der aktuelle Stacktrace kann in Java ganz einfach mit Thread.currentThread().getStackTrace(); ermittelt werden. Doch darin enthalten ist dann auch die Methode getStackTrace() selbst.","title":"Aktuellen Stack-Frame ermitteln","type":"blog"},{"content":"Wer Perl- und bash-Scripte nach Java umschreiben soll, hat sich sicherlich dabei eine in Java implementierte Sammlung von UNIX-Tools wie grep, diff etc. gewünscht. Fündig wird man unter www.java2s.com.\n Software-Development Java","date":"July 22, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-22-unix-tools-grep-diff-etc-in-java/","section":"Blog","summary":"Wer Perl- und bash-Scripte nach Java umschreiben soll, hat sich sicherlich dabei eine in Java implementierte Sammlung von UNIX-Tools wie grep, diff etc. gewünscht. Fündig wird man unter www.java2s.com.","title":"UNIX Tools (grep, diff etc.) in Java","type":"blog"},{"content":"Gerade traf mich beim Debuggen ein seltsames Verhalten meines Java-Programms unter Eclipse. Die Anwendung ist multithreaded und der wichtigste Thread wurde trotz Breakpoint auf erster Zeile in der run()-Methode oft gar nicht erst betreten, bzw. die Anwendung terminierte während Eclipse auf dieser Zeile angehalten hatte. Kein System.exit() meiner Anwendung wurde dabei durchlaufen.\nUm den \u0026ldquo;Schuldigen\u0026rdquo; zu finden, installierte ich temporär einen eigenen Security-Manager:\n... final SecurityManager securityManager = new SecurityManager() { public void checkPermission(Permission permission) { if (\u0026#34;exitVM\u0026#34;.equals(permission.getName())) { throw new SecurityException( \u0026#34;somebody is calling System.exit()\u0026#34;); } } }; @Before public void init() { System.setSecurityManager(securityManager); } Dazu gleich noch einen Breakpoint auf die o.g. Exception gesetzt und \u0026hellip; erstaunlicherweise funktionierte die Anwendung auf Anhieb wieder, ohne dass aber die obige exit()-Falle überhaupt durchlaufen wird. Dann fiel es mir wie Schuppen von den Augen: Die eigentliche Test-Routine terminierte! Denn diese wartete mit sleep() einen bestimmten Zeitraum, um dann den Thread zu terminieren. Im Debug-Prozess wurde dieser Zeitpunkt erreicht, bevor der Thread überhaupt jemals gestartet war. Nun, bewahrt hilft dieser Beitrag, anderen Entwicklern vor dieser Falle.\n Software-Development Java Eclipse","date":"July 21, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-21-magisches-programmende-beim-debuggen-von-java-threads-mit-eclipse/","section":"Blog","summary":"Gerade traf mich beim Debuggen ein seltsames Verhalten meines Java-Programms unter Eclipse. Die Anwendung ist multithreaded und der wichtigste Thread wurde trotz Breakpoint auf erster Zeile in der run()-Methode oft gar nicht erst betreten, bzw. die Anwendung terminierte während Eclipse auf dieser Zeile angehalten hatte. Kein System.exit() meiner Anwendung wurde dabei durchlaufen.","title":"Magisches Programmende beim Debuggen von Java Threads mit Eclipse","type":"blog"},{"content":" eclemma # Meistens benutze ich für die Messung der Code-Coverage von Tests EclEMMA, da dieses als Eclipse-Plugin die Code-Coverage direkt in Eclipse anzeigt.\nZwei Probleme habe ich allerdings mit EclEMMA festgestellt:\nIn Testmethoden, die eine Exception werfen sollen, kann EclEMMA die Testcoverage nicht korrekt feststellen (siehe dort in der FAQ). In multi-Threaded Tests terminierte Thread.sleep() in einigen Fällen nie (hing sich auf). Wenn man JUnit Tests, die dies betrifft, unter eclemma auslassen will, sie aber beim normalen Unit-Testlauf mit ausführen kann, hilft folgende Zeile in der Testmethode:\nif ( System.getProperty(\u0026#34;java.class.path\u0026#34;).contains(\u0026#34;.eclemma.\u0026#34;) ) return; djunit # djunut, ein weiteres Eclipse-Plugin für Code-Coverage kommt leider auch mit Threads oft nicht klar:\nException in thread \u0026#34;Thread-1\u0026#34; java.util.ConcurrentModificationException Leider habe ich keine Lösung für das Problem. Ohne Threads funktioniert aber djunit auch zufriedenstellend.\ncobertura # Ein sehr schönes weiteres Code-Coverage Werkzeug ist allerdings mit Cobertura verfügbar, welches es auch als Maven 2 Plugin gibt, leider nicht als Eclipse-Plugin. Hier der nötige Ausschnitt aus der pom.xml:\n\u0026lt;project\u0026gt; ... \u0026lt;dependencies\u0026gt; ... \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;cobertura-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;2.2\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; ... \u0026lt;/dependencies\u0026gt; ... \u0026lt;reporting\u0026gt; ... \u0026lt;plugins\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;cobertura-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;formats\u0026gt; \u0026lt;format\u0026gt;html\u0026lt;/format\u0026gt; \u0026lt;format\u0026gt;xml\u0026lt;/format\u0026gt; \u0026lt;/formats\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; ... \u0026lt;/reporting\u0026gt; \u0026lt;/project\u0026gt; Aufgerufen wird Cobertura dann so:\nmvn cobertura:cobertura Dabei führt es dann die Tests unter Cobertura aus und generiert einen Bericht in HTML und XML nach target/target/site/cobertura/, der unter target/site/cobertura/index.html mit einem Browser betrachtet werden kann.\n Software-Development Java Eclipse Maven","date":"July 15, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-07-15-code-coverage-von-junit-tests-mit-eclipse-und-maven/","section":"Blog","summary":"Meistens benutze ich für die Messung der Code-Coverage von Tests EclEMMA, da dieses als Eclipse-Plugin die Code-Coverage direkt in Eclipse anzeigt.","title":"Code-Coverage von JUnit Tests mit Eclipse und Maven","type":"blog"},{"content":"Wie der Spiegel am 24. Juni berichtete pumpt die Europäische Zentralbank mehr als 440 Milliarden Euro in den Markt. Und zwar für 1% Zinsen und Laufzeiten bis zu einem Jahr. Trotzdem geben die Finanzinstitute kaum Kredite an Firmen weiter. Kann ein Zins von 1% noch zu hoch sein?\nZunächst ist natürlich zwischen Nominal- und Realzinsen zu unterscheiden, dann kommt noch das Risiko hinzu, das für Kredite an Nichtbanken.\nBei einer Inflationsrate von 0% (so ging es vor kurzem durch die Presse) ist also der Realzins auf Bargeld derzeit ebenfalls 0%. Bargeld ist ein Kreditverhältnis mit der ZB als Schuldner, das Risiko also minimal. Rechen wir das Risiko relativ zum Risiko der ZB, haben wir also beim Bargeld (und ZB-Guthaben) einen risikobereinigten Realzins von 0%. Wenn aber die Realwirtschaft im Durchschnitt gerade Verluste macht, müsste auch der Gleichgewichtszins für Unternehmenskredite negativ sein. Da für Geldanleger (auch die Geschäftsbanken) aber Geldbasis (ZB-Guthaben und Bargeld) als Substitutionsgut zur Verfügung steht, wird dieses natürlich aufgrund des höheren Zinses gewählt, denn 0% ist höher als jeder negative Realzins für Unternehmenskredite.\nDie paradoxe Situation ist, dass ausgerechnet jetzt, wo die Realwirtschaft im Durchschnitt die geringste, vermutlich negative, Rendite hat, der Realzins auf Geldbasis am höchsten ist. Denn in guten Zeiten, bei einer Inflationsrate von z.B.3,5%, lag dieser mit -3,5% viel tiefer als heute, in schlechten Zeiten.\nEin noch so kleiner Zins auf Geldbasis kann also in der Tat zu hoch sein, solange er selbst dann von den Zentralbanken positiv gehalten wird, wenn der Gleichgewichtszins schon negativ wäre. In der Folge müsste man also dafür sorgen, dass auch Geldbasis einen tieferen Realzins erhält, entweder durch Inflation oder durch \u0026ldquo;Gebühren\u0026rdquo; auf Geldbasis (vergl. auch Aussagen von Prof. Mankiew von der Harvard Uni in den letzten Wochen und).\nErsteres, Inflation, darf man natürlich nicht durch Neuverschuldung (Konjunkturprogramme) erzeugen, weil solch künstliche Kreditnachfrage logischerweise den Zins nur anhebt - und das ist nur scheinbar gut. Zwar ist damit auch die Zinsstruktur wieder im Lot, aber die Realwirtschaft kann dieses erhöhte Zinsniveau gar nicht leisten, es entsteht nur eine neue Blase.\nWenn man schon Inflation erzeugt, müsste man dies also durch \u0026ldquo;Hubschraubergeld\u0026rdquo; machen. Ein solches käme praktisch einer Entwertung der bestehenden Geldbasis-Einheiten gleich, ohne das Zinsniveau wieder künstlich anzuheben. Oder eben die o.g. Gebühr auf Bargeld - bei letzterer Lösung könnte wenigstens das Preisniveau gleich bleiben, was enorme volkswirtschaftliche Preisanpassungs-Kosten sparen würde. Vermutlich gibt es noch viele weitere Lösungen, wenn erst einmal viele Menschen darüber nachdenken.\nAll das passt eigentlich 100% in die heutige Wirtschaftstheorie hinein, nur dass viele Wirtschaftswissenschaftler beim Geld immer noch davon ausgehen, dass 0% eine \u0026ldquo;natürliche\u0026rdquo; Untergrenze für den Nominalzins wäre. Aber Prof. Mankiew, Prof. Buiter und Prof. Illing haben es schon erkannt, dass dem nicht so ist. Wann folgen die anderen? Und wann erkennt die Politik ihren Irrweg?\n Ökonomie","date":"June 26, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-06-26-europaische-zentralbank-pumpt-440-mrd-euro-in-die-markte/","section":"Blog","summary":"Wie der Spiegel am 24. Juni berichtete pumpt die Europäische Zentralbank mehr als 440 Milliarden Euro in den Markt. Und zwar für 1% Zinsen und Laufzeiten bis zu einem Jahr. Trotzdem geben die Finanzinstitute kaum Kredite an Firmen weiter. Kann ein Zins von 1% noch zu hoch sein?","title":"Europäische Zentralbank pumpt 440 Mrd. Euro in die Märkte","type":"blog"},{"content":"Fast täglich, meist beim Debuggen, aber auch für Logfiles, muss ein Java-Entwickler die ursprüngliche Ursache einer Exception herausfinden. So nützt einem die Meldung Can't commit because the transaction is in aborted state, die ein Applikationsserver z.B. nach der Verletzung eines Datenbank-Constraints liefern würde, recht wenig, doch das tief darin verborgene SQLSTATE[23505] ERROR: duplicate key violates unique constraint ... gibt schon einen sehr guten Hinweis darauf, was eigentlich passiert ist.\nHier nur eine Methode, welche versucht, diese Information aus der Kette von Exceptions herauszufinden und sie in einer String-Message darzustellen, die dann z.B. im Log ausgegeben werden könnte:\nimport java.lang.reflect.InvocationTargetException; import java.sql.SQLException; ... public static String extractCauseMessage(Exception e) { if ( e.getMessage() != null ) return e.getMessage(); else if (e instanceof InvocationTargetException ) { String sqlState = null; InvocationTargetException ite = (InvocationTargetException) e; Throwable cause = ite.getCause(); Throwable prev = null; while ( cause != null \u0026amp;\u0026amp; cause != prev ) { prev = cause; cause = prev.getCause(); if ( (cause == null || cause == prev ) \u0026amp;\u0026amp; prev instanceof SQLException ) { SQLException sqlExc = (SQLException) prev; sqlState = sqlExc.getSQLState(); cause = sqlExc.getNextException(); } } if ( cause == null ) cause = prev; if ( cause != null ) return composeMessage( sqlState, cause.getMessage() ); return composeMessage( sqlState, ite.getTargetException().getMessage() ); } return e.getClass() + \u0026#34; without detail message\u0026#34;; } private static String composeMessage( String sqlState, String message) { if ( sqlState != null ) return \u0026#34;SQLSTATE[\u0026#34; + sqlState + \u0026#34;] \u0026#34; + message; return message; }  Software-Development Java","date":"June 18, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-06-18-ursprungliche-ursache-von-exceptions/","section":"Blog","summary":"Fast täglich, meist beim Debuggen, aber auch für Logfiles, muss ein Java-Entwickler die ursprüngliche Ursache einer Exception herausfinden. So nützt einem die Meldung Can’t commit because the transaction is in aborted state, die ein Applikationsserver z.B. nach der Verletzung eines Datenbank-Constraints liefern würde, recht wenig, doch das tief darin verborgene SQLSTATE[23505] ERROR: duplicate key violates unique constraint … gibt schon …","title":"ursprüngliche Ursache von Exceptions","type":"blog"},{"content":"Wie Spiegel.de berichtet, sank die Inflationsrate im Mai 2009 auf 0%. D.h. zum einen, im Schnitt bleiben die Preise stabil. Doch was die wenigsten bedenken, bedeutet es auch, dass der Realzinssatz auf Geldbasis (Bargeld und Guthaben bei der EZB) damit ebenfalls 0% beträgt - und das wiederum hat unangenehme Auswirkungen.\nDie Geschichte zeigt, dass bei einer mäßigen Inflation von z.B. 5% die Wirtschaft gut läuft. In diesem Fall beträgt also der Realzins auf Geldbasis -5%. Jetzt, in Zeiten der Wirtschaftskrise, liegt der Zinssatz auf Geldbasis also um 5 sogenannte Prozentpunkte höher als bei florierender. Die Volkswirtschaftswissenschaft lehrt uns jedoch, dass dass sich Zinsniveau und Realwirtschftsrendite proportional entwickeln. Zumindest was den Zins auf Geldbasis betriffst, stimmt diese These also schon einmal nicht.\nDoch welche Bedeutung hat dies für \u0026ldquo;normale\u0026rdquo; Zinsen, also mit einer längeren Laufzeit und gegenüber anderen Gläubigern? Für einen Anleger ist Geldbasis grundsätzlich ein Substitutionsgut für andere Vermögensanlagen. Gibt es z.B. auf für eine fünfjährige Obligation 5% Zinsen, das Risko beträgt aber aufgrund der Wirtschaftskrise gar 6%, dann ist auch der Realzins von 0% auf Geldbasis besser als die -1% auf die Obligation. Mit anderen Worten: Der Realzins auf Geldbasis ist eine Untergrenze für Anlagezinsen.\nDieser Zins auf Geldbasis entsteht jedoch nicht auf dem freien Markt, sondern wird von der EZB zwischen 0% und 2% reguliert. Es ist also eine Preisregulierung. Zudem ist die Preisregulierung genau gegenläufig zum Marktpreis, wie zwei Absätze höher dargelegt wurde. Jeder Wirtschaftswissenschaftler weiß jedoch, wie Preisregulierung wirkt: Sie verzerrt den Markt und führt zu Fehlallokation und Ineffizienz. Nur beim Geld scheint dieses Gesetz nicht zu gelten?\n Ökonomie","date":"May 27, 2009","externalUrl":null,"permalink":"/de/blog/2009/2009-05-27-was-0-inflation-bedeutet/","section":"Blog","summary":"Wie Spiegel.de berichtet, sank die Inflationsrate im Mai 2009 auf 0%. D.h. zum einen, im Schnitt bleiben die Preise stabil. Doch was die wenigsten bedenken, bedeutet es auch, dass der Realzinssatz auf Geldbasis (Bargeld und Guthaben bei der EZB) damit ebenfalls 0% beträgt - und das wiederum hat unangenehme Auswirkungen.","title":"Was bedeuten 0% Inflation?","type":"blog"},{"content":"Die JavaBeans Spezifikation erlaubt public Member-Variable direkt als Properties zu verwenden, oder eben alternativ public getter/setter Methoden. Die JSF EL (JavaServer Faces Expression Language) aber akzeptiert ausschließlich getter/setter Methoden. Getter/setter Methoden werden jedoch zunehmend als Anti-Pattern gesehen, eine Diskussion hierzu findet sich im Blog von Adam Bien.\nEine einfache Konfigurations-Möglichkeit konnte ich hier nicht finden, aber natürlich kann der ELResolver insgesamt durch eine eigene Subklasse ausgetauscht werden, welche die gewünschte Funktionalität implementiert:\npublic class ELResolver extends ELResolver { private static final Object[] noArgs = new Object[0]; private static final Class[] noClassArgs = new Class[0]; @Override public Object getValue( ELContext context, Object base, Object property) throws NullPointerException, PropertyNotFoundException, ELException { String name = property.toString(); if (base != null) { // null objects cannot have anything return null; // either getter or variable try { // is a getter available? try { String getterName = \u0026#34;get\u0026#34; + name.substring(0, 1).toUpperCase() + name.substring(1); Method getter = base.getClass().getMethod(getterName, noClassArgs); context.setPropertyResolved(true); return getter.invoke(base, noArgs); } catch (NoSuchMethodException noSuchMethodExc) { // this is ok, there might be a public member bar } // is there a public member variable? try { Field field = getField(base, property); if (field != null) { context.setPropertyResolved(true); return field.get(base); } } catch (NoSuchFieldException noSuchFieldExc) { // no method nor a field is really worth an exception String message = \u0026#34;Error accessing property \u0026#39;\u0026#34; + name + \u0026#34;\u0026#39; in bean of type \u0026#34; + base.getClass().getName(); throw new PropertyNotFoundException(message, e); } } catch (Exception exc) { // other exceptions are re-thrown throw new RuntimeException(ex); } } } } Dieser muss dann noch in der face-config.xml aktiviert werden:\n\u0026lt;application\u0026gt; \u0026lt;el-resolver\u0026gt;de.hoennig.jsf.ELResolver\u0026lt;/el-resolver\u0026gt; \u0026lt;/application\u0026gt;  Software-Development Java","date":"August 7, 2008","externalUrl":null,"permalink":"/de/blog/2008/2008-08-07-jsf-controller-ohne-gettersetter/","section":"Blog","summary":"Die JavaBeans Spezifikation erlaubt public Member-Variable direkt als Properties zu verwenden, oder eben alternativ public getter/setter Methoden. Die JSF EL (JavaServer Faces Expression Language) aber akzeptiert ausschließlich getter/setter Methoden. Getter/setter Methoden werden jedoch zunehmend als Anti-Pattern gesehen, eine Diskussion hierzu findet sich im Blog von Adam Bien.","title":"JSF Controller ohne getter/setter","type":"blog"},{"content":"Mein laufendes Projekt ist ein Client für vorgegebene Webservices. Aus der WSDL-Datei werden durch eine Konfiguration in der pom.xml über das wsimport Kommando des Maven2-Plugins org.codehaus.mojo:jaxws-maven-plugin mit xjc Java-Klassen generiert. Nun ergab sich die Notwendigkeit, diese Klassen Serializable zu machen. Nach einer kurzen Recherche kam ich auf die folgende Lösung:\nZunächst wird in die xsd-Datei mit den Datentypen für den Webservice eine Annotation aufgenommen, mit der diese Klassen mit dem Interface Serializable markiert werden:\n\u0026lt;xs:schema xmlns:jaxb=\u0026#34;http://java.sun.com/xml/ns/jaxb\u0026#34; xmlns:xjc=\u0026#34;http://java.sun.com/xml/ns/jaxb/xjc\u0026#34; xmlns:xs=\u0026#34;http://www.w3.org/2001/XMLSchema\u0026#34; jaxb:version=\u0026#34;1.0\u0026#34; jaxb:extensionBindingPrefixes=\u0026#34;xjc\u0026#34;\u0026gt; \u0026lt;xs:annotation\u0026gt; \u0026lt;xs:appinfo\u0026gt; \u0026lt;jaxb:globalBindings\u0026gt; \u0026lt;xjc:serializable uid=\u0026#34;10000001\u0026#34;/\u0026gt; \u0026lt;/jaxb:globalBindings\u0026gt; \u0026lt;/xs:appinfo\u0026gt; \u0026lt;/xs:annotation\u0026gt; ... \u0026lt;/xs:schema\u0026gt; An derselben Stelle könnte man auch eine beliebige Basisklasse einführen, von der diese generierten Klassen dann abgeleitet werden. Auch andere interessante Konfigurationen sind dort möglich. Doch zurück zum konkreten Fall:\nDann muss noch in der pom.xml das Plugin für die Code-Generierung umkonfguriert werden, damit diese Erweiterung vom jxc überhaupt verwendet wird:\n\u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jaxws-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.9\u0026lt;/version\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;wsimport\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;keep\u0026gt;true\u0026lt;/keep\u0026gt; \u0026lt;extension\u0026gt;true\u0026lt;/extension\u0026gt; \u0026lt;xjcArgs\u0026gt;\u0026lt;/xjcArgs\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt;  Software-Development Java Maven","date":"July 16, 2008","externalUrl":null,"permalink":"/de/blog/2008/2008-07-16-generierte-webservice-klassen-im-client-serializable-machen/","section":"Blog","summary":"Mein laufendes Projekt ist ein Client für vorgegebene Webservices. Aus der WSDL-Datei werden durch eine Konfiguration in der pom.xml über das wsimport Kommando des Maven2-Plugins org.codehaus.mojo:jaxws-maven-plugin mit xjc Java-Klassen generiert. Nun ergab sich die Notwendigkeit, diese Klassen Serializable zu machen. Nach einer kurzen Recherche kam ich auf die folgende Lösung:","title":"generierte Webservice-Klassen im Client Serializable machen","type":"blog"},{"content":"Java Resource-Bundles (i.d.R. *.properties) müssen in ISO-8859-1 bzw. Quoted-ASCII erstellt sein. XML- und Java-Dateien dahin gegen hält man heute meist in UTF-8. In einem Projekt, in dem jeder Entwickler mit einer grafischen Entwicklungsumgebung arbeitet, die man so konfigurieren kann, dass jeweils der richtige Zeichensatz verwendet wird, ist das kein Problem.\nWas aber, wenn einige Entwickler mit einfachen Editoren arbeiten? Zu schnell passiert es, dass vergessen wird, den Zeichensatz anzupassen und schon hat man Zeichensatzsalat.\nSowohl mit ant als auch mit Maven gibt es eine Lösung: Man konvertiert die Ressource-Bundles einfach automatisch von UTF-8 nach Quoted-ASCII und schon kann man jede Quelldatei immer mit UTF-8 bearbeiten.\nFür Maven hilft hier das Plugin native2ascii-maven-plugin, welches in der pom.xml des jeweiligen Projektes konfiguriert werden muss:\n\u0026lt;project\u0026gt; ... \u0026lt;build\u0026gt; \u0026lt;finalName\u0026gt;...\u0026lt;/finalName\u0026gt; \u0026lt;plugins\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;native2ascii-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;native2ascii\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;encoding\u0026gt;UTF-8\u0026lt;/encoding\u0026gt; \u0026lt;dest\u0026gt;target/classes\u0026lt;/dest\u0026gt; \u0026lt;includes\u0026gt;**/*.properties\u0026lt;/includes\u0026gt; \u0026lt;src\u0026gt;src/main/resources\u0026lt;/src\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; \u0026lt;/build\u0026gt; ... \u0026lt;/project\u0026gt; Bei der Konfiguration werden dann alle *.properties-Dateien unterhalb von src/main/resources nach target/classes konvertiert, wobei der Quell-Zeichensatz UTF-8 ist.\nMit ant kann man ähnliches z.B. so erreichen:\n\u0026lt;native2ascii encoding=\u0026#34;UTF-8\u0026#34; dest=\u0026#34;${build.home}/war/WEB-INF/classes\u0026#34; src=\u0026#34;${src.home}/war/WEB-INF/classes\u0026#34; includes=\u0026#34;*.properties\u0026#34;/\u0026gt; Da es bei ant keine vorgegebenen Verzeichnisstruktur gibt, muss diese ggf. angepasst werden.\n Software-Development Java Maven","date":"July 2, 2008","externalUrl":null,"permalink":"/de/blog/2008/2008-07-02-resource-bundles-automatisch-von-utf-8-nach-quoted-ascii-konvertieren/","section":"Blog","summary":"Java Resource-Bundles (i.d.R. .properties) müssen in ISO-8859-1 bzw. Quoted-ASCII erstellt sein. XML- und Java-Dateien dahin gegen hält man heute meist in UTF-8. In einem Projekt, in dem jeder Entwickler mit einer grafischen Entwicklungsumgebung arbeitet, die man so konfigurieren kann, dass jeweils der richtige Zeichensatz verwendet wird, ist das kein Problem.","title":"Resource-Bundles automatisch von UTF-8 nach Quoted-ASCII konvertieren","type":"blog"},{"content":"Vor einigen Tagen bemerkte ich in einer neuen Applikation mit JSF und Spring Webflow, dass die Umlaute in den Formularen falsch übertragen wurden. Bei reinen JSF-Applikationen hatte ich dahingegen mit der Kodierung noch nie Probleme.\nDie Lösung ist, in der zum war-File gehörigen web.xml den Filter org.springframework.web.filter.CharacterEncodingFilter als ersten Filter einzutragen:\n\u0026lt;filter\u0026gt; \u0026lt;filter-name\u0026gt;SetCharacterEncodingFilter\u0026lt;/filter-name\u0026gt; \u0026lt;filter-class\u0026gt;org.springframework.web.filter.CharacterEncodingFilter\u0026lt;/filter-class\u0026gt; \u0026lt;init-param\u0026gt; \u0026lt;param-name\u0026gt;encoding\u0026lt;/param-name\u0026gt; \u0026lt;param-value\u0026gt;UTF8\u0026lt;/param-value\u0026gt; \u0026lt;/init-param\u0026gt; \u0026lt;init-param\u0026gt; \u0026lt;param-name\u0026gt;forceEncoding\u0026lt;/param-name\u0026gt; \u0026lt;param-value\u0026gt;true\u0026lt;/param-value\u0026gt; \u0026lt;/init-param\u0026gt; \u0026lt;/filter\u0026gt; \u0026lt;filter-mapping\u0026gt; \u0026lt;filter-name\u0026gt;SetCharacterEncodingFilter\u0026lt;/filter-name\u0026gt; \u0026lt;url-pattern\u0026gt;/*\u0026lt;/url-pattern\u0026gt; \u0026lt;/filter-mapping\u0026gt;  Software-published Java","date":"June 28, 2008","externalUrl":null,"permalink":"/de/blog/2008/2008-06-28-jsf-mit-spring-webflow-buttons-ohne-funktion/","section":"Blog","summary":"Vor einigen Tagen bemerkte ich in einer neuen Applikation mit JSF und Spring Webflow, dass die Umlaute in den Formularen falsch übertragen wurden. Bei reinen JSF-Applikationen hatte ich dahingegen mit der Kodierung noch nie Probleme.","title":"JSF mit Spring Webflow: Buttons ohne Funktion","type":"blog"},{"content":"","date":"June 28, 2008","externalUrl":null,"permalink":"/de/tags/Software-published/","section":"Tags","summary":"","title":"Software-Published","type":"tags"}]