\n\u003C/LinearLayout>\n",[50,11988,11989,11994,11999,12004,12009,12014,12019,12024,12029,12034,12039,12044],{"__ignoreMap":48},[53,11990,11991],{"class":55,"line":56},[53,11992,11993],{},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n",[53,11995,11996],{"class":55,"line":86},[53,11997,11998],{},"\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n",[53,12000,12001],{"class":55,"line":126},[53,12002,12003],{}," android:layout_width=\"match_parent\"\n",[53,12005,12006],{"class":55,"line":163},[53,12007,12008],{}," android:layout_height=\"match_parent\"\n",[53,12010,12011],{"class":55,"line":186},[53,12012,12013],{}," android:orientation=\"vertical\">\n",[53,12015,12016],{"class":55,"line":221},[53,12017,12018],{}," \u003CTextView\n",[53,12020,12021],{"class":55,"line":242},[53,12022,12023],{}," android:id=\"@+id/text\"\n",[53,12025,12026],{"class":55,"line":273},[53,12027,12028],{}," android:layout_width=\"match_parent\"\n",[53,12030,12031],{"class":55,"line":279},[53,12032,12033],{}," android:layout_height=\"30dp\"\n",[53,12035,12036],{"class":55,"line":496},[53,12037,12038],{}," android:gravity=\"center_vertical\"\n",[53,12040,12041],{"class":55,"line":503},[53,12042,12043],{}," android:padding=\"5dp\"/>\n",[53,12045,12046],{"class":55,"line":509},[53,12047,12048],{},"\u003C/LinearLayout>\n",[18,12050,12051],{},"Then we add the created adapter to a ListView (or a ListActivity like here):",[43,12053,12055],{"className":288,"code":12054,"language":290,"meta":48,"style":48},"public class ExpandableListActivity extends ListActivity {\n@Override\npublic void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n List\u003CString> entries = new ArrayList\u003CString>();\n for (int i = 1; i \u003C= 101; i++) {\n entries.add(\"Entry \" + i);\n }\n setListAdapter(new ExpandableListAdapter(entries, this));\n}\n private class ExpandableListAdapter extends BaseAdapter{....}\n}\n",[50,12056,12057,12062,12066,12071,12076,12081,12086,12091,12096,12101,12105,12110],{"__ignoreMap":48},[53,12058,12059],{"class":55,"line":56},[53,12060,12061],{},"public class ExpandableListActivity extends ListActivity {\n",[53,12063,12064],{"class":55,"line":86},[53,12065,11701],{},[53,12067,12068],{"class":55,"line":126},[53,12069,12070],{},"public void onCreate(Bundle savedInstanceState) {\n",[53,12072,12073],{"class":55,"line":163},[53,12074,12075],{}," super.onCreate(savedInstanceState);\n",[53,12077,12078],{"class":55,"line":186},[53,12079,12080],{}," List\u003CString> entries = new ArrayList\u003CString>();\n",[53,12082,12083],{"class":55,"line":221},[53,12084,12085],{}," for (int i = 1; i \u003C= 101; i++) {\n",[53,12087,12088],{"class":55,"line":242},[53,12089,12090],{}," entries.add(\"Entry \" + i);\n",[53,12092,12093],{"class":55,"line":273},[53,12094,12095],{}," }\n",[53,12097,12098],{"class":55,"line":279},[53,12099,12100],{}," setListAdapter(new ExpandableListAdapter(entries, this));\n",[53,12102,12103],{"class":55,"line":496},[53,12104,282],{},[53,12106,12107],{"class":55,"line":503},[53,12108,12109],{}," private class ExpandableListAdapter extends BaseAdapter{....}\n",[53,12111,12112],{"class":55,"line":509},[53,12113,282],{},[18,12115,12116],{},"The list as it is now shows all entries, so the next thing to do, is implementing the limiting function to the adapter.",[18,12118,12119],{},"Let’s start by giving our adapter a member variable for the limit:",[43,12121,12123],{"className":288,"code":12122,"language":290,"meta":48,"style":48},"private int limit = 20;\n",[50,12124,12125],{"__ignoreMap":48},[53,12126,12127],{"class":55,"line":56},[53,12128,12122],{},[18,12130,12131],{},"Next we need to modify some of the methods to get this working:",[18,12133,12134],{},"In getCount we need to either return the limit, or the size of the list, if the limit is greater than the list, because\nour list contains all entries and we only want to display a part of it.",[43,12136,12138],{"className":288,"code":12137,"language":290,"meta":48,"style":48},"@Override\npublic int getCount() {\n if (entries.size() \u003C= limit) {\n return entries.size();\n }\n return limit;\n}\n",[50,12139,12140,12144,12149,12154,12159,12163,12168],{"__ignoreMap":48},[53,12141,12142],{"class":55,"line":56},[53,12143,11701],{},[53,12145,12146],{"class":55,"line":86},[53,12147,12148],{},"public int getCount() {\n",[53,12150,12151],{"class":55,"line":126},[53,12152,12153],{}," if (entries.size() \u003C= limit) {\n",[53,12155,12156],{"class":55,"line":163},[53,12157,12158],{}," return entries.size();\n",[53,12160,12161],{"class":55,"line":186},[53,12162,12095],{},[53,12164,12165],{"class":55,"line":221},[53,12166,12167],{}," return limit;\n",[53,12169,12170],{"class":55,"line":242},[53,12171,282],{},[18,12173,12174],{},"Next, we need to adjust the getItem method with an further clause in the if",[43,12176,12178],{"className":288,"code":12177,"language":290,"meta":48,"style":48},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n return entries.get(position);\n",[50,12179,12180,12185],{"__ignoreMap":48},[53,12181,12182],{"class":55,"line":56},[53,12183,12184],{},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n",[53,12186,12187],{"class":55,"line":86},[53,12188,12189],{}," return entries.get(position);\n",[18,12191,12192],{},"Now for the biggest change for our new functionality: the implementation of the ‘more button’.",[43,12194,12196],{"className":288,"code":12195,"language":290,"meta":48,"style":48},"private LinearLayout getMoreView() {\n LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n moreView.setOnClickListener(new View.OnClickListener() {\n public void onClick(View v) {\n listAdapter.increaseLimit();\n }\n });\n return moreView;\n}\n",[50,12197,12198,12203,12208,12213,12218,12223,12227,12231,12236],{"__ignoreMap":48},[53,12199,12200],{"class":55,"line":56},[53,12201,12202],{},"private LinearLayout getMoreView() {\n",[53,12204,12205],{"class":55,"line":86},[53,12206,12207],{}," LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n",[53,12209,12210],{"class":55,"line":126},[53,12211,12212],{}," moreView.setOnClickListener(new View.OnClickListener() {\n",[53,12214,12215],{"class":55,"line":163},[53,12216,12217],{}," public void onClick(View v) {\n",[53,12219,12220],{"class":55,"line":186},[53,12221,12222],{}," listAdapter.increaseLimit();\n",[53,12224,12225],{"class":55,"line":221},[53,12226,4963],{},[53,12228,12229],{"class":55,"line":242},[53,12230,7598],{},[53,12232,12233],{"class":55,"line":273},[53,12234,12235],{}," return moreView;\n",[53,12237,12238],{"class":55,"line":279},[53,12239,282],{},[18,12241,12242],{},"Create another simple xml like this (disregarded i18n for this simple test case. Of course, Strings should normally be\ndeclared in the strings.xml):",[43,12244,12246],{"className":1938,"code":12245,"language":1940,"meta":48,"style":48},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"wrap_content\"\n android:orientation=\"horizontal\"\n android:paddingLeft=\"20dp\"\n android:paddingRight=\"20dp\">\n \u003CTextView\n android:id=\"@+id/MoreRowText\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"40dp\"\n android:text=\"Load more...\"\n android:gravity=\"center\"/>\n\u003C/LinearLayout>\n",[50,12247,12248,12252,12256,12261,12266,12271,12276,12281,12285,12290,12295,12300,12305,12310],{"__ignoreMap":48},[53,12249,12250],{"class":55,"line":56},[53,12251,11993],{},[53,12253,12254],{"class":55,"line":86},[53,12255,11998],{},[53,12257,12258],{"class":55,"line":126},[53,12259,12260],{}," android:layout_width=\"fill_parent\"\n",[53,12262,12263],{"class":55,"line":163},[53,12264,12265],{}," android:layout_height=\"wrap_content\"\n",[53,12267,12268],{"class":55,"line":186},[53,12269,12270],{}," android:orientation=\"horizontal\"\n",[53,12272,12273],{"class":55,"line":221},[53,12274,12275],{}," android:paddingLeft=\"20dp\"\n",[53,12277,12278],{"class":55,"line":242},[53,12279,12280],{}," android:paddingRight=\"20dp\">\n",[53,12282,12283],{"class":55,"line":273},[53,12284,12018],{},[53,12286,12287],{"class":55,"line":279},[53,12288,12289],{}," android:id=\"@+id/MoreRowText\"\n",[53,12291,12292],{"class":55,"line":496},[53,12293,12294],{}," android:layout_width=\"fill_parent\"\n",[53,12296,12297],{"class":55,"line":503},[53,12298,12299],{}," android:layout_height=\"40dp\"\n",[53,12301,12302],{"class":55,"line":509},[53,12303,12304],{}," android:text=\"Load more...\"\n",[53,12306,12307],{"class":55,"line":515},[53,12308,12309],{}," android:gravity=\"center\"/>\n",[53,12311,12312],{"class":55,"line":521},[53,12313,12048],{},[18,12315,12316],{},"And add a method to the adapter to increase the limit:",[43,12318,12320],{"className":288,"code":12319,"language":290,"meta":48,"style":48}," public void increaseLimit() {\n limit+=20;\n //remove the button if we can no longer expand the list\n if (limit >= entries.size()) {\n getListView().removeFooterView(moreView);\n }\n //notify to redraw the list\n notifyDataSetChanged();\n }\n",[50,12321,12322,12327,12332,12337,12342,12347,12351,12356,12361],{"__ignoreMap":48},[53,12323,12324],{"class":55,"line":56},[53,12325,12326],{}," public void increaseLimit() {\n",[53,12328,12329],{"class":55,"line":86},[53,12330,12331],{}," limit+=20;\n",[53,12333,12334],{"class":55,"line":126},[53,12335,12336],{}," //remove the button if we can no longer expand the list\n",[53,12338,12339],{"class":55,"line":163},[53,12340,12341],{}," if (limit >= entries.size()) {\n",[53,12343,12344],{"class":55,"line":186},[53,12345,12346],{}," getListView().removeFooterView(moreView);\n",[53,12348,12349],{"class":55,"line":221},[53,12350,12095],{},[53,12352,12353],{"class":55,"line":242},[53,12354,12355],{}," //notify to redraw the list\n",[53,12357,12358],{"class":55,"line":273},[53,12359,12360],{}," notifyDataSetChanged();\n",[53,12362,12363],{"class":55,"line":279},[53,12364,12095],{},[18,12366,12367],{},"At last, we simply add the button as a footer view to our list (After creation of the ListView, but before setting the\nListAdapter to the ListView):",[43,12369,12371],{"className":288,"code":12370,"language":290,"meta":48,"style":48},"moreView = getMoreView();\nlistAdapter = new ExpandableListAdapter(entries);\ngetListView().addFooterView(moreView);\nsetListAdapter(listAdapter);\n",[50,12372,12373,12378,12383,12388],{"__ignoreMap":48},[53,12374,12375],{"class":55,"line":56},[53,12376,12377],{},"moreView = getMoreView();\n",[53,12379,12380],{"class":55,"line":86},[53,12381,12382],{},"listAdapter = new ExpandableListAdapter(entries);\n",[53,12384,12385],{"class":55,"line":126},[53,12386,12387],{},"getListView().addFooterView(moreView);\n",[53,12389,12390],{"class":55,"line":163},[53,12391,12392],{},"setListAdapter(listAdapter);\n",[18,12394,12395],{},"And that’s it for the simple case of a static list 🙂",[18,12397,12398],{},"If you want to do this a bit more dynamic or you don’t want to load whole database table (or the whole internet) into\nyour list, you have to do some extra work.",[3260,12400,12401,12404,12407],{},[580,12402,12403],{},"Load the next X entries with an AsyncTask (display a ProgressDialog while it is getting the data!) and add a method\nto your adapter, that adds this entries to the list. Call this method from within the onPostExecute method of the\nAsyncTask",[580,12405,12406],{},"You may want to keep track of the total number of entries you have to know when the user can’t load any more entries\nand to remove the button. (If the dataset is not bound to grow every few seconds. Otherwise, maybe let the user try\nto hit the button and see for himself, if theres new data)",[580,12408,12409],{},"Keep in mind to notify your users if anything fails, like the next entries could not have been loaded, because they\nhave no internet connection.",[18,12411,12412,12413,12419],{},"A Little homework for you: Combine it with\nthe ",[585,12414,12418],{"href":12415,"rel":12416,"title":12417},"http://blog.synyx.de/2011/11/android-listview-with-rounded-corners/",[589],"android listview with rounded corners","rounded corners example","\nto make it look better 😛",[18,12421,12422,12423],{},"Here’s the source, btw:",[585,12424,12427],{"href":12425,"rel":12426},"https://media.synyx.de/uploads//2012/12/ExpandableListview.zip",[589],"ExpandableListview",[607,12429,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":12431},[],[2632,1899],"2012-12-07T09:39:26","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries.\\nWith a button at the end of the list, the user can load more entries.","https://synyx.de/blog/android-expandable-listview/",{},"/blog/android-expandable-listview",{"title":11782,"description":11791},"blog/android-expandable-listview",[11770,12441,12442,12443,12444,12445,12446,12447],"entries","entry","expandable","limit","limited","list","listview","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries. With a button at the end of the list,…","2cPxf8ntm24639cXpvDd1uvgjk37n-ulC-vUEA0BPZw",{"id":12451,"title":12452,"author":12453,"body":12455,"category":12823,"date":12824,"description":12825,"extension":617,"link":12826,"meta":12827,"navigation":499,"path":12828,"seo":12829,"slug":12459,"stem":12830,"tags":12831,"teaser":12834,"__hash__":12835},"blog/blog/studien-projektarbeit-mit-grails.md","Studien-Projektarbeit mit Grails",[12454],"matt",{"type":11,"value":12456,"toc":12821},[12457,12460,12463,12466,12469,12472,12481,12490,12495,12509,12518,12521,12530,12533,12552,12555,12558,12567,12570,12573,12582,12585,12595,12694,12697,12700,12747,12750,12756,12759,12768,12771,12781,12816,12819],[14,12458,12452],{"id":12459},"studien-projektarbeit-mit-grails",[18,12461,12462],{},"Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang\nOktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt für\ndas 5te Semester zu bekommen.",[18,12464,12465],{},"Das Gedränge kann durchaus groß sein, da hierbei die goldene Regel gilt: „wer zuerst kommt, malt zuerst“. Zudem ist der\nBegriff ‘passend’ auf den jeweiligen Studenten gemünzt. Manche Studenten suchen nach einem für Ihn möglichst\ninteressanten Thema, manche wollen von ihrem meist-geschätzten Professor betreut werden und wiederum andere wollen\nlieber eine ruhige Kugel schieben. Soweit so gut, nun gibt es allerdings noch eine Alternative zu den aufgeführten\nMöglichkeiten an eine Projektarbeit zu kommen. Der Student überlegt sich ein Thema und spricht einen Professor an, ob\ndieser das ausgewählte Thema betreuen möchte.",[18,12467,12468],{},"Da ich zu Beginn des Semesters noch nichts von der letzten Möglichkeit gewusst hatte und ich ansonsten wenig begeistert\nvon den angebotenen Themen war, musste ich mir etwas einfallen lassen. Nach einem aufschlussreichen Gespräch in der\nFachschaft, wusste ich nun auch von der Möglichkeit ein Projekt direkt mit einem Professor auszuhandeln.",[18,12470,12471],{},"Da ich die „neue“ Welt der dynamischen Sprachen im Java-Umfeld schon immer sehr reizvoll gefunden hatte legte ich mich\nauf das Schreiben einer einfachen Webapplikation mit dem Grails-Framework fest. Die Überlegung dahinter war recht\neinfach, es musste neu und spannend sein (ansonsten würde der betreuende Professor das Projekt abweisen) und ich hatte\nschon länger eine Projektidee, die durch das Studium nun vorangetrieben wurde. Nun ist es aber bekanntermaßen so, dass\nbei einer Studienarbeit der Anteil der Dokumentation und Planung recht hoch sein kann. Dementsprechend wählte ich auch\nkeine brandneue Technologie. Die Dokumentation und die erhältlichen Tutorials würden den Einsteig sicherlich stark\nvereinfachen.",[18,12473,12474,12475,12480],{},"Es stellt sich nun die Frage wie ich überhaupt zum Thema ",[585,12476,12479],{"href":12477,"rel":12478,"title":12479},"http://grails.org/",[589],"Grails"," bekommen bin. Die Antwort\nist ziemlich simpel – Mundpropaganda. Ich kann mich noch gut an einen Abend in der Kneipe erinnern. Ich saß mit\nbefreundeten ehemaligen Arbeitskollegen bei einem kühlen Bier und wir debattierten wie so des öfteren über die\nWebentwicklung mit Grails. “Wir können zu dritt in einem Monat das Projekt an dem wir nun schon jahrelang mit einem\nvollen Entwicklerteam schrauben nachbauen“. Mit diesem Satz im Hinterkopf ist mein Studienprojekt entstanden.",[18,12482,12483,12484,12489],{},"Das sollte genug zur Vorgeschichte des Studien-Projekts sein,. In einem kleinen Tutorial zum das\nGrails ",[585,12485,12488],{"href":12486,"rel":12487},"https://web.archive.org/web/20170106062111/http://www.grails.org:80/plugin/spring-security-core",[589],"Spring-Security -Plugin","\ns möchte das Plugin-System von Grails demonstrieren:",[18,12491,12492],{},[27,12493,12494],{},"Meine Umgebung:",[577,12496,12497,12500,12503,12506],{},[580,12498,12499],{},"Ubuntu 12.04",[580,12501,12502],{},"Eclipse IDE (Groovy/Grails Tool Suite von SpringSource)",[580,12504,12505],{},"Grails 2.1.0",[580,12507,12508],{},"Java 1.6.0_24",[18,12510,12511,12512,12517],{},"Um das Grails-Plugin nutzen zu können, ist es nötig das hierzu\nbenötigte ",[585,12513,12516],{"href":12486,"rel":12514,"title":12515},[589],"SpringSecurityPlugin","Plugin","\nzu installieren. Es gibt 2 Möglichkeiten dies zu bewerkstelligen:",[18,12519,12520],{},"Über die Komandozeile:",[43,12522,12524],{"className":8739,"code":12523,"language":8741,"meta":48,"style":48},"grails install-plugin spring-security-core\n",[50,12525,12526],{"__ignoreMap":48},[53,12527,12528],{"class":55,"line":56},[53,12529,12523],{},[18,12531,12532],{},"oder über einen Eintrag in der grails-app/conf/BuildConfig.groovy:",[43,12534,12536],{"className":8739,"code":12535,"language":8741,"meta":48,"style":48},"plugins {\n compile \":spring-security-core:1.2.7.1\"\n}\n",[50,12537,12538,12543,12548],{"__ignoreMap":48},[53,12539,12540],{"class":55,"line":56},[53,12541,12542],{},"plugins {\n",[53,12544,12545],{"class":55,"line":86},[53,12546,12547],{}," compile \":spring-security-core:1.2.7.1\"\n",[53,12549,12550],{"class":55,"line":126},[53,12551,282],{},[18,12553,12554],{},"Da ich die Übersicht der BuildConfig inzwischen zu schätzen weiß, wählte ich die zweite Variante.",[18,12556,12557],{},"Zur Überprüfung/Installation des Plugins muss nun der Befehl",[43,12559,12561],{"className":8739,"code":12560,"language":8741,"meta":48,"style":48},"grails compile\n",[50,12562,12563],{"__ignoreMap":48},[53,12564,12565],{"class":55,"line":56},[53,12566,12560],{},[18,12568,12569],{},"ausgeführt werden. Dieser Befehl löst die eingetragene Plugin-Dependency auf.",[18,12571,12572],{},"Der nächste Schritt nutzt nun gleich ein Skript des installierten Plugin für die Erstellung der nötigen Domain-Klassen.",[43,12574,12576],{"className":8739,"code":12575,"language":8741,"meta":48,"style":48},"grails s2-quickstart \u003CmeinePackageStruktur> User Role\n",[50,12577,12578],{"__ignoreMap":48},[53,12579,12580],{"class":55,"line":56},[53,12581,12575],{},[18,12583,12584],{},"Diese 3 kleinen Befehle reichen für die Grundfunktionalität von SpringSecurity aus.",[18,12586,12587,12588,12594],{},"Zur Überprüfung der Login-Funktionalität macht es Sinn in\nder ",[585,12589,12593],{"href":12590,"rel":12591,"title":12592},"https://web.archive.org/web/20140413052251/http://grails.org:80/Bootstrap+Classes",[589],"Bootstrap","grails-app/conf/Bootstrap.groovy","\nein paar Testdaten einzufügen:",[43,12596,12600],{"className":12597,"code":12598,"language":12599,"meta":48,"style":48},"language-groovy shiki shiki-themes github-light github-dark","class BootStrap{\n def init={servletContext->\n RoleuserRole=Role.findByAuthority('ROLE_USER')?:newRole(authority:'ROLE_USER').save(failOnError:true)\n Role adminRole=Role.findByAuthority('ROLE_ADMIN')?:newRole(authority:'ROLE_ADMIN').save(failOnError:true)\n UseradminUser=User.findByUsername('adminUser')?:newUser(username:'adminUser',enabled:true,password:'topSecret').save(failOnError:true)\n if(!adminUser .authorities.contains(userRole))\n UserRole.create(adminUser,userRole,true)\n if(!adminUser .authorities.contains(adminRole))\n UserRole.create(adminUser,adminRole,true)\n User dummyUser=User.findByUsername('dummyUser')?:newUser(username:'dummyUser',enabled:true,password:'notSoSecret').save(failOnError:true)\n if(!dummyUser.authorities.contains(userRole))\n UserRole.create(dummyUser,userRole,true)\n ...\n }\n assertUser.count()==2\n assertRole.count()==2\n assertUserRole.count()==3\n …\n}\n","groovy",[50,12601,12602,12607,12612,12617,12622,12627,12632,12637,12642,12647,12652,12657,12662,12666,12670,12675,12680,12685,12690],{"__ignoreMap":48},[53,12603,12604],{"class":55,"line":56},[53,12605,12606],{},"class BootStrap{\n",[53,12608,12609],{"class":55,"line":86},[53,12610,12611],{}," def init={servletContext->\n",[53,12613,12614],{"class":55,"line":126},[53,12615,12616],{}," RoleuserRole=Role.findByAuthority('ROLE_USER')?:newRole(authority:'ROLE_USER').save(failOnError:true)\n",[53,12618,12619],{"class":55,"line":163},[53,12620,12621],{}," Role adminRole=Role.findByAuthority('ROLE_ADMIN')?:newRole(authority:'ROLE_ADMIN').save(failOnError:true)\n",[53,12623,12624],{"class":55,"line":186},[53,12625,12626],{}," UseradminUser=User.findByUsername('adminUser')?:newUser(username:'adminUser',enabled:true,password:'topSecret').save(failOnError:true)\n",[53,12628,12629],{"class":55,"line":221},[53,12630,12631],{}," if(!adminUser .authorities.contains(userRole))\n",[53,12633,12634],{"class":55,"line":242},[53,12635,12636],{}," UserRole.create(adminUser,userRole,true)\n",[53,12638,12639],{"class":55,"line":273},[53,12640,12641],{}," if(!adminUser .authorities.contains(adminRole))\n",[53,12643,12644],{"class":55,"line":279},[53,12645,12646],{}," UserRole.create(adminUser,adminRole,true)\n",[53,12648,12649],{"class":55,"line":496},[53,12650,12651],{}," User dummyUser=User.findByUsername('dummyUser')?:newUser(username:'dummyUser',enabled:true,password:'notSoSecret').save(failOnError:true)\n",[53,12653,12654],{"class":55,"line":503},[53,12655,12656],{}," if(!dummyUser.authorities.contains(userRole))\n",[53,12658,12659],{"class":55,"line":509},[53,12660,12661],{}," UserRole.create(dummyUser,userRole,true)\n",[53,12663,12664],{"class":55,"line":515},[53,12665,322],{},[53,12667,12668],{"class":55,"line":521},[53,12669,9634],{},[53,12671,12672],{"class":55,"line":527},[53,12673,12674],{}," assertUser.count()==2\n",[53,12676,12677],{"class":55,"line":533},[53,12678,12679],{}," assertRole.count()==2\n",[53,12681,12682],{"class":55,"line":539},[53,12683,12684],{}," assertUserRole.count()==3\n",[53,12686,12687],{"class":55,"line":545},[53,12688,12689],{}," …\n",[53,12691,12692],{"class":55,"line":1700},[53,12693,282],{},[18,12695,12696],{},"Nun kann man beliebige Teile seiner Applikation von SpringSecurity überwachen lassen.",[18,12698,12699],{},"Ob auf Klassenebene/Methodenebene kann einfach per Annotation gesteuert werden.",[43,12701,12703],{"className":12597,"code":12702,"language":12599,"meta":48,"style":48},"import org.springframework.dao.DataIntegrityViolationException\nimport grails.plugins.springsecurity.Secured\n@Secured(['ROLE_USER'])\nclass MyFancyController {\n...\n@Secured(['ROLE_ADMIN'])\ndef mySuperfancyAdminOnlyMethod(){\n...\n}\n",[50,12704,12705,12710,12715,12720,12725,12729,12734,12739,12743],{"__ignoreMap":48},[53,12706,12707],{"class":55,"line":56},[53,12708,12709],{},"import org.springframework.dao.DataIntegrityViolationException\n",[53,12711,12712],{"class":55,"line":86},[53,12713,12714],{},"import grails.plugins.springsecurity.Secured\n",[53,12716,12717],{"class":55,"line":126},[53,12718,12719],{},"@Secured(['ROLE_USER'])\n",[53,12721,12722],{"class":55,"line":163},[53,12723,12724],{},"class MyFancyController {\n",[53,12726,12727],{"class":55,"line":186},[53,12728,3608],{},[53,12730,12731],{"class":55,"line":221},[53,12732,12733],{},"@Secured(['ROLE_ADMIN'])\n",[53,12735,12736],{"class":55,"line":242},[53,12737,12738],{},"def mySuperfancyAdminOnlyMethod(){\n",[53,12740,12741],{"class":55,"line":273},[53,12742,3608],{},[53,12744,12745],{"class":55,"line":279},[53,12746,282],{},[18,12748,12749],{},"Ein kurzer Funktionstest auf der Weboberfläche überzeugt von der Funktionalität.",[18,12751,12752],{},[736,12753],{"alt":12754,"src":12755},"\"Grails SpringSecurity login screen\"","https://media.synyx.de/uploads//2012/09/Bildschirmfoto-vom-2012-09-10-1801541.png",[18,12757,12758],{},"Über den Controller:",[43,12760,12762],{"className":8739,"code":12761,"language":8741,"meta":48,"style":48}," localhost:\u003CmyPort>/\u003CmyApp>/dbconsole\n",[50,12763,12764],{"__ignoreMap":48},[53,12765,12766],{"class":55,"line":56},[53,12767,12761],{},[18,12769,12770],{},"lässt sich die Struktur von der Datenhaltung in SpringSecurity leicht nachvollziehen.",[18,12772,12773,12774,12780],{},"Um noch mehr Funktionalität und auch generierte Admin-Oberflächen zu bekommen, ist es möglich\ndas ",[585,12775,12779],{"href":12776,"rel":12777,"title":12778},"https://web.archive.org/web/20170104185319/http://grails.org/plugin/spring-security-ui",[589],"Spring Security UI","spring-security-ui","\nplugin zu installieren. Dies stellt eine AJAX fähige Oberfläche zum Login bereit, sowie eine ganzen Haufen von\nAdmin-Funktionalität. Weiterhin gibt es zusätzlich noch einige weitere Plugins rund um das Thema Spring-Security:",[577,12782,12783,12786,12789,12792,12795,12798,12801,12804,12807,12810,12813],{},[580,12784,12785],{},"Spring Security ACL which adds support for object-level and method-level authorization using ACLs (access control\nlist)",[580,12787,12788],{},"Spring Security AppInfo which provides a basic UI to view the security configuration",[580,12790,12791],{},"Spring Security CAS which adds support for single sign-on using Jasig CAS",[580,12793,12794],{},"Spring Security OpenID which adds support for OpenID authentication",[580,12796,12797],{},"Spring Security Facebook which adds support for Facebook authentication",[580,12799,12800],{},"Spring Security Kerberos which adds support for single sign-on using Kerberos",[580,12802,12803],{},"Spring Security LDAP which adds support for LDAP and ActiveDirectory authentication",[580,12805,12806],{},"Spring Security Mock which adds support for fake/mock authentication during developement",[580,12808,12809],{},"Spring Security RADIUS which adds support for RADIUS authentication",[580,12811,12812],{},"Spring Security Shibboleth Native SP which adds support for container provided Shibboleth authentication.",[580,12814,12815],{},"Spring Security Twitter which adds support for Twitter authentication",[18,12817,12818],{},"Dies sollte einen kleine Überblick über den Aufbau und die Installation eines Grails-Plugins im Allgemeinen und von\nspringSecurity im speziellen geben können.",[607,12820,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":12822},[],[9269],"2012-09-10T18:35:43","Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang\\nOktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt für\\ndas 5te Semester zu bekommen.","https://synyx.de/blog/studien-projektarbeit-mit-grails/",{},"/blog/studien-projektarbeit-mit-grails",{"title":12452,"description":12462},"blog/studien-projektarbeit-mit-grails",[12832,12833,625],"grails","grailsplugin","Das Informatik-Studium beinhaltet in der Hochschule Karlsruhe auch praktische Übungen. In der Regel bemühen sich Anfang Oktober eine Vielzahl von junger (meist) hoch motivierter Informatik-Studenten um möglichst einen passendes Projekt…","tdjMkPlIVQL35Q9ecSB0rUESlMxdBITjcE_BHdDrQbI",{"id":12837,"title":12838,"author":12839,"body":12841,"category":12914,"date":12915,"description":12848,"extension":617,"link":12916,"meta":12917,"navigation":499,"path":12918,"seo":12919,"slug":12920,"stem":12921,"tags":12922,"teaser":12923,"__hash__":12924},"blog/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat.md","Clean Code Development – Prinzipien und Praktiken zur Steigerung der Software Qualität",[12840],"sommer",{"type":11,"value":12842,"toc":12912},[12843,12846,12849,12852,12855,12860,12863,12868,12871,12874,12879,12882,12885,12890,12893,12896,12899,12902,12905],[14,12844,12838],{"id":12845},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualität",[18,12847,12848],{},"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe",[18,12850,12851],{},"Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus?",[18,12853,12854],{},"Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien und Praktiken zusammen und sieht\neine stufenweise Einführung derer vor. Ohne auf die einzelnen Grade, welche den Weg zum Clean Code Developer\nbeschreiben, einzugehen möchte ich hier ein paar ausgewählte Prinzipien kurz vorstellen.",[18,12856,12857],{},[27,12858,12859],{},"Don’t Repeat Yourself (DRY)",[18,12861,12862],{},"Code Duplizierung sollte vermieden werden, da Anpassungen an mehreren stellen erfolgen müssen um Konsistenz zu\ngewährleisten. Eigentlich ein alter Hut aber Copy & Paste ist in der Praxis weit verbreitet.",[18,12864,12865],{},[27,12866,12867],{},"Keep it simple, stupid (KISS)",[18,12869,12870],{},"Mach es nicht komplizierter als notwendig.",[18,12872,12873],{},"Ja, auch wenn es dir uncool und langweilig erscheint!",[18,12875,12876],{},[27,12877,12878],{},"Favour Composition over Inheritance (FCoI)",[18,12880,12881],{},"Bei der Vererbung entstehen Abhängikeiten von den Subklassen zu den Elternklassen, was das Austauschen der\nFunktionalität zur Laufzeit erschwert. Wird die Vererbung inflationär verwendet, kann dies ein System schnell unnötig\nkomplex und schlecht testbar werden lassen.",[18,12883,12884],{},"Bei der Komposition verwendet eine Klasse eine andere, was die Abhängigkeiten reduziert und die Testbarkeit wesentlich\nvereinfacht.",[18,12886,12887],{},[27,12888,12889],{},"You Ain’t Gonna Need It (YAGNI)",[18,12891,12892],{},"Dieses Prinzip bedeutet soviel wie “implementiere nichts was du nicht brauchst”. Das klingt logisch aber dennoch hab ich\nselbst schon gegen dieses Prinzip verstoßen.",[18,12894,12895],{},"In der Praxis wird versucht Software auf künftige Anforderungen vorzubereiten aber oft stellt sich später heraus, dass\nsich die ursprünglich erwarteten Anforderungen tatsächlich nie ergeben haben.",[18,12897,12898],{},"Statt dessen ergeben sich natürlich ganz andere Anforderungen die nicht berücksichtigt wurden und deren Umsetzung jetzt\ndurch unnötigen Balast behindert werden.",[18,12900,12901],{},"Die Praktiken des Clean Code Developments sind Methoden und Techniken die ständig eingesetzt werden sollten und umfassen\nzum Beispiel das Issue Tracking, automatisierte Integrationstests, Continuous Integration, statische Codeanalysen aber\nauch das Weitergeben von Erfahrungen und die Teilnahme an Fachveranstaltungen.",[18,12903,12904],{},"Mehr zu Clean Code Development gibt es unter",[18,12906,12907],{},[585,12908,12909],{"href":12909,"rel":12910,"title":12911},"http://www.clean-code-developer.de",[589],"clean-code-developer",{"title":48,"searchDepth":86,"depth":86,"links":12913},[],[613],"2012-08-16T09:57:29","https://synyx.de/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat/",{},"/blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",{"title":12838,"description":12848},"clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat","blog/clean-code-development-prinzipien-und-praktiken-zur-steigerung-der-software-qualitat",[11651,6171],"Software Qualität ist ein Trendthema aber wie erreicht man eine hohe Qualität? Reichen gängige Instrumente wie UnitTests und Code-Reviews aus? Clean Code Development bietet keine fertigen Problemlösungen sondern fasst Prinzipien…","qanNmRX3aNPeWTNpe7MMGWO8r9OdqTQQpJRo9UX9hwo",{"id":12926,"title":12927,"author":12928,"body":12929,"category":12967,"date":12968,"description":12969,"extension":617,"link":12970,"meta":12971,"navigation":499,"path":12972,"seo":12973,"slug":12933,"stem":12974,"tags":12975,"teaser":12977,"__hash__":12978},"blog/blog/synyx-open-source-projekte-jetzt-auf-github.md","synyx Open Source Projekte jetzt auf GitHub",[6810],{"type":11,"value":12930,"toc":12965},[12931,12934,12937,12953,12956],[14,12932,12927],{"id":12933},"synyx-open-source-projekte-jetzt-auf-github",[18,12935,12936],{},"Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf\nsynyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer User, nahm viel Zeit in\nAnspruch, und ehrlich gesagt, programmieren wir lieber als User zu verwalten.",[18,12938,12939,12940,9093,12946,12952],{},"Seit Ende letzten Jahres haben wir einige Projekte auch auf GitHub veröffentlicht. Bis jetzt liefen beide Plattformen\nparallel. Wir haben uns entschieden, synyx.org abzuschalten, und unsere Open Source Projekte komplett auf GitHub\numzuziehen. Die Vorteile liegen eindeutig auf der Hand: GitHub bietet einfache Mechanismen,\nwie ",[585,12941,12945],{"href":12942,"rel":12943,"title":12944},"https://de.wikipedia.org/wiki/Abspaltung_%28Softwareentwicklung%29",[589],"Wikipedia: Forking / Abspaltung","Forks",[585,12947,12951],{"href":12948,"rel":12949,"title":12950},"http://www.heise.de/glossar/entry/Git-Pull-Request-397971.html",[589],"Heise: Pull Request","Pull Requests",", an um\nInteressierten die Möglichkeit zu bieten an unseren Projekten mitzuarbeiten.",[18,12954,12955],{},"Dank GitHub API, svn2git, Textile/Markdown in Gollum und Git war die Migration vom Redmine-basierten synyx.org zu\nGitHub relativ problemlos.",[18,12957,12958,12959,12964],{},"Wer an unseren Open Source Projekten Interesse hat, kann sich\nauf ",[585,12960,12961],{"href":12961,"rel":12962,"title":12963},"https://github.com/synyx/",[589],"GitHub synyx"," einfach mal umschauen.",{"title":48,"searchDepth":86,"depth":86,"links":12966},[],[614],"2012-06-26T11:00:57","Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf\\nsynyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer User, nahm viel Zeit in\\nAnspruch, und ehrlich gesagt, programmieren wir lieber als User zu verwalten.","https://synyx.de/blog/synyx-open-source-projekte-jetzt-auf-github/",{},"/blog/synyx-open-source-projekte-jetzt-auf-github",{"title":12927,"description":12936},"blog/synyx-open-source-projekte-jetzt-auf-github",[5970,11654,12976],"redmine","Wer sich öfters auf unserer Webpräsenz umschaut, hat sicherlich bemerkt, dass wir unsere Open Source Projekte bisher auf synyx.org veröffentlicht haben. Die Pflege von synyx.org, z.B. das manuelle Freischalten neuer…","-2nTQFX94lk0-P6SIZtLNuZgY8Yx4uf4o138Cj--5IA",{"id":12980,"title":12981,"author":12982,"body":12983,"category":13197,"date":13198,"description":13199,"extension":617,"link":13200,"meta":13201,"navigation":499,"path":13202,"seo":13203,"slug":13204,"stem":13205,"tags":13206,"teaser":13211,"__hash__":13212},"blog/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct.md","Android 2.1 SQLite: problem with QueryBuilder and Distinct",[11661],{"type":11,"value":12984,"toc":13195},[12985,12988,12991,12994,13171,13178,13181,13190,13193],[14,12986,12981],{"id":12987},"android-21-sqlite-problem-with-querybuilder-and-distinct",[18,12989,12990],{},"In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\nbut on 2.1 it crashed every time when trying to get a column from a cursor.",[18,12992,12993],{},"Here’s the simplified code:",[43,12995,12997],{"className":288,"code":12996,"language":290,"meta":48,"style":48},"//member, a SQLiteOpenHelper\nBackendOpenHelper helper;\n//...\npublic List \u003CExample> getExamples(String arg){\nSQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n builder.setTables(\"example e JOIN\n secondtable s ON e.id = s.example_id\");\n Map\u003CString, String> projectionMap =\n new HashMap\u003CString, String>();\n projectionMap.put(\"id\", \"e.id\");\n //... put in some more values ...\n builder.setProjectionMap(projectionMap);\n builder.setDistinct(true);\n builder.appendWhere(\" e.someRow = ? \");\n //... some more wheres ...\n SQLiteDatabase db = helper.getReadableDatabase();\n String[] selectionArgs = new String[] {\n arg\n };\n Cursor cursor = builder.query(db, null,\n null, selectionArgs, null, null, null);\n if (cursor.moveToFirst()) {\n while (cursor.isAfterLast() == false) {\n int index = cursor.getColumnIndex(\"id\");\n //on android 2.1, index is returned as -1\n //on newer versions as 1\n int id = cursor.getInt(index);\n //crashes if index is -1\n //...\n cursor.moveToNext();\n }\n }\n cursor.close();\n //...\n}\n",[50,12998,12999,13004,13009,13014,13019,13024,13029,13034,13039,13044,13049,13054,13059,13064,13069,13074,13079,13084,13089,13093,13098,13103,13108,13113,13118,13123,13128,13133,13138,13143,13148,13153,13157,13162,13167],{"__ignoreMap":48},[53,13000,13001],{"class":55,"line":56},[53,13002,13003],{},"//member, a SQLiteOpenHelper\n",[53,13005,13006],{"class":55,"line":86},[53,13007,13008],{},"BackendOpenHelper helper;\n",[53,13010,13011],{"class":55,"line":126},[53,13012,13013],{},"//...\n",[53,13015,13016],{"class":55,"line":163},[53,13017,13018],{},"public List \u003CExample> getExamples(String arg){\n",[53,13020,13021],{"class":55,"line":186},[53,13022,13023],{},"SQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n",[53,13025,13026],{"class":55,"line":221},[53,13027,13028],{}," builder.setTables(\"example e JOIN\n",[53,13030,13031],{"class":55,"line":242},[53,13032,13033],{}," secondtable s ON e.id = s.example_id\");\n",[53,13035,13036],{"class":55,"line":273},[53,13037,13038],{}," Map\u003CString, String> projectionMap =\n",[53,13040,13041],{"class":55,"line":279},[53,13042,13043],{}," new HashMap\u003CString, String>();\n",[53,13045,13046],{"class":55,"line":496},[53,13047,13048],{}," projectionMap.put(\"id\", \"e.id\");\n",[53,13050,13051],{"class":55,"line":503},[53,13052,13053],{}," //... put in some more values ...\n",[53,13055,13056],{"class":55,"line":509},[53,13057,13058],{}," builder.setProjectionMap(projectionMap);\n",[53,13060,13061],{"class":55,"line":515},[53,13062,13063],{}," builder.setDistinct(true);\n",[53,13065,13066],{"class":55,"line":521},[53,13067,13068],{}," builder.appendWhere(\" e.someRow = ? \");\n",[53,13070,13071],{"class":55,"line":527},[53,13072,13073],{}," //... some more wheres ...\n",[53,13075,13076],{"class":55,"line":533},[53,13077,13078],{}," SQLiteDatabase db = helper.getReadableDatabase();\n",[53,13080,13081],{"class":55,"line":539},[53,13082,13083],{}," String[] selectionArgs = new String[] {\n",[53,13085,13086],{"class":55,"line":545},[53,13087,13088],{}," arg\n",[53,13090,13091],{"class":55,"line":1700},[53,13092,7731],{},[53,13094,13095],{"class":55,"line":1709},[53,13096,13097],{}," Cursor cursor = builder.query(db, null,\n",[53,13099,13100],{"class":55,"line":1718},[53,13101,13102],{}," null, selectionArgs, null, null, null);\n",[53,13104,13105],{"class":55,"line":1724},[53,13106,13107],{}," if (cursor.moveToFirst()) {\n",[53,13109,13110],{"class":55,"line":1743},[53,13111,13112],{}," while (cursor.isAfterLast() == false) {\n",[53,13114,13115],{"class":55,"line":1749},[53,13116,13117],{}," int index = cursor.getColumnIndex(\"id\");\n",[53,13119,13120],{"class":55,"line":1758},[53,13121,13122],{}," //on android 2.1, index is returned as -1\n",[53,13124,13125],{"class":55,"line":1770},[53,13126,13127],{}," //on newer versions as 1\n",[53,13129,13130],{"class":55,"line":1777},[53,13131,13132],{}," int id = cursor.getInt(index);\n",[53,13134,13135],{"class":55,"line":1785},[53,13136,13137],{}," //crashes if index is -1\n",[53,13139,13140],{"class":55,"line":1791},[53,13141,13142],{}," //...\n",[53,13144,13145],{"class":55,"line":1797},[53,13146,13147],{}," cursor.moveToNext();\n",[53,13149,13150],{"class":55,"line":1809},[53,13151,13152],{}," }\n",[53,13154,13155],{"class":55,"line":1818},[53,13156,4963],{},[53,13158,13159],{"class":55,"line":1824},[53,13160,13161],{}," cursor.close();\n",[53,13163,13164],{"class":55,"line":1835},[53,13165,13166],{}," //...\n",[53,13168,13169],{"class":55,"line":5437},[53,13170,282],{},[18,13172,13173,13174,13177],{},"After some research I found out that this apparently happens, when using ",[573,13175,13176],{},"distinct"," with the QueryBuilder on android\n2.1.",[18,13179,13180],{},"So a quick fix for this problem is to simply don’t use the getColumnIndex() method from the cursor, but instead just\naccess it by its id (Though you have to remember to change this part of the code if you make changes to your table\nrows).",[43,13182,13184],{"className":288,"code":13183,"language":290,"meta":48,"style":48}," int id = cursor.getInt(1);\n",[50,13185,13186],{"__ignoreMap":48},[53,13187,13188],{"class":55,"line":56},[53,13189,13183],{},[18,13191,13192],{},"I hope this will help someone who encounters the same problem, so he doesn’t have to search for a solution as long as I\nhad to.",[607,13194,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":13196},[],[2632,1899],"2012-05-22T09:39:08","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\\nbut on 2.1 it crashed every time when trying to get a column from a cursor.","https://synyx.de/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct/",{},"/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",{"title":12981,"description":12990},"android-2-1-sqlite-problem-with-querybuilder-and-distinct","blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",[13207,11770,13208,13209,13210],"2-1","database","mobile","sqlite","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly, but on 2.1 it crashed every time when trying to…","x1Vxa8-B7BB8vb0rMrFKnHNmHw0JMSkiTZ3HMChf8mU",{"id":13214,"title":13215,"author":13216,"body":13217,"category":13318,"date":13319,"description":13320,"extension":617,"link":13321,"meta":13322,"navigation":499,"path":13323,"seo":13324,"slug":13221,"stem":13325,"tags":13326,"teaser":13328,"__hash__":13329},"blog/blog/my-take-on-things-java-community-events-vs-java-conferences.md","My take on things – Java Community events vs. Java Conferences",[8516],{"type":11,"value":13218,"toc":13316},[13219,13222,13225,13230,13233,13238,13243,13250,13253,13258,13263,13266,13269,13273,13278,13281,13284,13289,13292,13295,13300,13303,13306,13309,13313],[14,13220,13215],{"id":13221},"my-take-on-things-java-community-events-vs-java-conferences",[18,13223,13224],{},"Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\nich auf der JAX’2012 leider nur 2 Tage sein konnte.",[18,13226,13227],{},[27,13228,13229],{},"Organisation:",[18,13231,13232],{},"Hier können beide Konferenzen eindeutig punkten. Die JAX war an den beiden Tagen meines Besuchs hervorragend\norganisiert, es gab kaum Wartezeiten oder aber überfüllte Säle, selbst bei den recht beliebten Keynotes nicht. Beide\nVeranstaltungen profitieren natürlich von den hervorragenden Locations. Dies macht die Organisation deutlich einfacher,\nwas man wirklich an vielen, vielen Kleinigkeiten bemerkt. Die Devoxx liegt hier aber aufgrund der Tatsache das sie in\neinem der größten Kinos in Europa stattfindet noch einen Punkt besser im Rennen. Die Bequemlichkeit der Säle ist einfach\nungeschlagen (Kinosessel der modernsten Art!) und natürlich auch die Sicht. Während man im einen oder anderen Talk auf\nder JAX auf einmal eine Säule oder den Beamer im Weg hatte (ist eben nicht besser machbar) gibt es aufgrund der\nperfekten Location auf der Devoxx solche Probleme nicht.",[18,13234,13235],{},[573,13236,13237],{},"+1 für die Devoxx",[18,13239,13240],{},[27,13241,13242],{},"Verpflegung:",[18,13244,13245,13246,13249],{},"Die Verpflegung ist ja immer so eine Sache auf Konferenzen, die einen brauchen nur Kaffee, die anderen dauernd\nNervenfutter und Süßes für ihre ",[573,13247,13248],{},"Konzentration."," Die Devoxx bietet mit dauernden Kaffeetankstellen als auch dauernd\ngefüllten Kühlschränken recht ordentliche Ausstattung. Auch für Frühstück, Mittagessen und einen Nachmittagssnack ist\ngesorgt. An für sich gibt es hier keine Klagen. Ach doch, bitte, bitte liebe Devoxxianer … sorgt für besseren Kaffee!!!!\nIch bezahle gerne ein wenig mehr 🙂",[18,13251,13252],{},"Das Angebot der Devoxx ist allerdings in keinster Weise mit dem Angebot der JAX zu vergleichen. Auf der JAX gab es\nwährend der 2 Tage alles, was das Herz begehrt. Guten Kaffee, hervorragende Zwischensnacks und ein Buffet zu quasi jeder\nTageszeit, natürlich unterschiedlichst befüllt. Hierfür muss ich großen Respekt zollen. Das war einfach unfassbar gut\nund lecker! Einzig eine Kleinigkeit der Verbesserung fällt mir noch ein. Man könnte Beschildern, was wirklich\nvegetarisch oder aber vegan ist. Das Personal am Buffet konnte aber nahezu jede Frage beantworten und so hatte man auch\nals Vegetarier das gute Gefühl ordentlich zu essen 🙂",[18,13254,13255],{},[573,13256,13257],{},"+1 für die JAX",[18,13259,13260],{},[27,13261,13262],{},"Technische Ausstattung:",[18,13264,13265],{},"Ja die technische Ausstattung ist natürlich so eine Sache. Die Devoxx ist hier klar im Vorteil durch die wirklich\ngeniale Location. Gegen solch einen Sound und eine Sicht und eine Lichtstärke der Beamer und Projektoren ist natürlich\nkein Gras gewachsen.",[18,13267,13268],{},"Vergleichbar sind also letztlich nur die kleineren Dinge wie Mikrofone oder aber die Gadgets. Auf beiden Seiten\narbeiteten aufopferungssvoll die Techniker an einer perfekten Situation. Ein kleiner Pluspunkt für die Devoxx vielleicht\naufgrund der sehr gut angenommenen Twitterwall und den einfach unschlagbar guten Apps für die Mobile Devices. Auf der\nDevoxx ist das WLan auch eindeutig besser, da man in einigen Sälen der JAX schlicht keines hatte (zumindest ging es mir\nund meinen Kollegen so für die Räume im Keller).",[18,13270,13271],{},[573,13272,13237],{},[18,13274,13275],{},[27,13276,13277],{},"Anspruch Sessions, Workshops:",[18,13279,13280],{},"Hier erspare ich mir einen Vergleich, da es einfach kaum zu vergleichen ist. Die Devoxx wird seit 2 Jahren von den\ngroßen Firmen hinter Java als Podest genutzt und ist einfach auch besser international erreichbar, daher sind die\nSpeaker rein namentlich schon kaum zu schlagen (für den der Wert darauf legt). Oracle als auch Google oder Springsource\nhaben regelmäßig ihre TopSpeaker an Bord. Auf der JAX wirken die Talks auch deutlich lösungsorientierter, während es auf\nder Devoxx sehr sehr viel um die reine Entwicklung geht. Hier spürt man meiner Meinung nach einfach auch die Geschichte\nbeider Konferenzen noch sehr. Die Devoxx welche aus der Community entstand, während die JAX ein kommerzielles Event ist.",[18,13282,13283],{},"Daher erspare ich mir auch den Vergleich der Kosten, das macht reichlich wenig Sinn und jeder sollte für sich\nentscheiden, was hier ein gutes Maß ist und was nicht. Eine Kleinigkeit aber, die die Devoxxianer bereits seit einigen\nJahren machen, sollte sich hier die JAX doch noch abschauen: Den Talks ein Level mitgeben, für wen sie geeignet sind (\nbsp. Beginner, Senior, Expert oder vgl..) Ich saß dann doch ein zwei Mal in einem Talk, wo einfach das eigene Wissen\ngrößer war als das des Speakers.",[18,13285,13286],{},[27,13287,13288],{},"Speaker:",[18,13290,13291],{},"Die Speaker sind natürlich eine sehr schwer vergleichbare Sache, alleine schon durch die oben bereits erwähnte Tatsache,\ndass die Devoxx von den großen drei als Bühne genutzt wird, während die JAX hier nicht so populär ist. Allerdings kann\nauch die JAX mit sehr namhaften und erfahrenen Speakern punkten und holt hier aus meiner Sicht Jahr für Jahr auf.",[18,13293,13294],{},"Ich mag hier keinen Punkt für die eine oder andere Seite geben.",[18,13296,13297],{},[27,13298,13299],{},"Umgebung:",[18,13301,13302],{},"Die JAX findet in einer echten Konferenzlocation statt, während die Devoxx, wie bereits erwähnt in einem der größten\nKinokomplexe Europas stattfindet. Das macht sich natürlich auch in der Umgebung bemerkbar, könnte man meinen. Aber: Das\nKino ist wirklich derart am Ende von Antwerpen, dass dies viel eher ein Nachteil ist, denn ein Vorteil. Die\nRheingoldhalle in der die JAX nun seit 2011 stattfindet ist deutlich direkter in der Stadt selbst, was für das Programm\ndanach sehr von Vorteil ist.",[18,13304,13305],{},"Für mich als Besucher aus Süddeutschland ist die JAX natürlich eine Top-Location, da ich sogar pendeln kann, wenn ich\nmag. Die Devoxx schlägt hier direkt noch mit einer 5 Stunden Anfahrt zu, ist allerdings auch per Flieger direkt zu\nerreichen, während man für Mainz wohl am besten über FFM fliegt und dann noch ein wenig die DB genießt. Allerdings kann\nder Vorteil des direkten Flughafens in der Stadt wohl doch bei internationalen Speakern punkten.",[18,13307,13308],{},"Dennoch gebe ich aufgrund meiner persönlichen Situation hier den Punkt an die JAX.",[18,13310,13311],{},[573,13312,13257],{},[18,13314,13315],{},"Zusammenfassend kann man wohl sagen, dass beide Konferenzen ihre Fans haben und sich auch nicht so ähnlich sind, dass\nman nicht beide besuchen könnte. Ich persönlich werde wohl auch dieses Jahr wieder die Devoxx besuchen und dann im\nFrühjahr 2013 wieder die JAX… 😉",{"title":48,"searchDepth":86,"depth":86,"links":13317},[],[614],"2012-04-25T09:44:39","Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb\\nEuropas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte, während\\nich auf der JAX’2012 leider nur 2 Tage sein konnte.","https://synyx.de/blog/my-take-on-things-java-community-events-vs-java-conferences/",{},"/blog/my-take-on-things-java-community-events-vs-java-conferences",{"title":13215,"description":13224},"blog/my-take-on-things-java-community-events-vs-java-conferences",[1909,2344,13327,290,2506,4144],"devoxx-conference","Heute will ich einmal die Gelegenheit nutzen und die beiden wohl populärsten Konferenzen für Java-Entwicklung innerhalb Europas gegenüberstellen. Dazu muss ich aber fairerweise anmerken, dass ich die Devoxx’2011 komplett besuchte,…","LlCT-W2p4EzOxwXO4tBpBQx5VG80g60BW-CYXAspPV0",{"id":13331,"title":13332,"author":13333,"body":13335,"category":13486,"date":13487,"description":13488,"extension":617,"link":13489,"meta":13490,"navigation":499,"path":13491,"seo":13492,"slug":13339,"stem":13493,"tags":13494,"teaser":13501,"__hash__":13502},"blog/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git.md","New Homepage with nanoc, Twitter Bootstrap, LESS and Git",[13334],"buch",{"type":11,"value":13336,"toc":13477},[13337,13340,13343,13351,13354,13358,13361,13370,13373,13377,13396,13400,13403,13407,13416,13420,13429,13433,13436,13445,13448,13451,13460,13463,13472,13475],[14,13338,13332],{"id":13339},"new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[18,13341,13342],{},"With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic\ndecisions filling our needs.",[18,13344,13345,13346,13350],{},"Our previous websites were always implemented in OpenCms since we are ",[585,13347,13349],{"href":3355,"rel":13348},[589],"OpenCms","\nSolution Provider and contributor. Using a CMS like OpenCms seemed the natural decision and it worked well for several\nversions of our homepage. But given the amount of work needed to set it up, develop templates and administrate it, was\nit really what we needed?",[18,13352,13353],{},"A CMS is useful if you have many editors, granular access management with user roles, editor/publisher workflows etc.\nBut we are a team of technically versed people, even our marketing prefers to edit HTML directly instead of rely on a\nWYSIWYG editor where you don’t always know the resulting markup. So OpenCms was a little heavy-weight for our homepage\nwhere we didn’t need most of it’s features.",[1181,13355,13357],{"id":13356},"finding-the-right-tool-for-our-needs","Finding the right tool for our needs",[18,13359,13360],{},"Result of analyzing the old homepage was that 99% of it was static content. The only dynamic parts were blog aggregation\nand a contact form. Only for these two it wasn’t worth using a CMS or application framework. Yet we still wanted some\ntemplating and generate static HTML.",[18,13362,13363,13364,13369],{},"There are many static HTML generation tools. I’m not going into detail on which we looked into, just the solution we\ncame up with. Some evaluation lead to ",[585,13365,13368],{"href":13366,"rel":13367},"http://nanoc.stoneship.org/",[589],"nanoc"," which is simple to set-up, supports various\ntemplating formats, is very flexible, has good documentation and is easy to use.",[18,13371,13372],{},"What about the contact form and blog aggregation? Contact forms that only send an email are no added value to an email\naddress. Analysis of it’s usage on the old homepage resulted in that it attracted mainly spam and very few real\nmessages. Blog aggregation is sufficient every few minutes and doesn’t need to be live. So instead of dynamically\nfetching and rendering, it can be done by a cronjob and regenerating server-side.",[1181,13374,13376],{"id":13375},"templating","Templating",[18,13378,13379,13380,99,13385,1884,13390,13395],{},"For templating we\nuse ",[585,13381,13384],{"href":13382,"rel":13383},"https://ruby-doc.org/stdlib/libdoc/erb/rdoc/ERB.html",[589],"“ERB”",[585,13386,13389],{"href":13387,"rel":13388},"https://getbootstrap.com/",[589],"“Twitter Bootstrap”",[585,13391,13394],{"href":13392,"rel":13393},"https://lesscss.org",[589],"“LESS”"," for CSS. Nanoc supports precompiling LESS and compressing CSS.",[655,13397,13399],{"id":13398},"erb","ERB",[18,13401,13402],{},"ERB is part of Ruby’s standard library and quite similar to JSP templating in the Java world. All we need is HTML, some\nmeta data replacement from pages (e. g. a title) and snippet inclusion for which nanoc provides a rendering helper.",[655,13404,13406],{"id":13405},"less","LESS",[18,13408,13409,13410,13415],{},"LESS enables a more maintainable way of writing CSS since it extends CSS by dynamic behavior such as variables, mixins,\noperations and functions. We chose it over ",[585,13411,13414],{"href":13412,"rel":13413},"http://sass-lang.com/",[589],"SCSS"," because we also use Twitter Bootstrap. I think\nin modern CSS styling you should use either of them to make your CSS maintainable and readable. For example defining our\nnew CI colors as variables or mixins for common button stylings is a good use of these features.",[655,13417,13419],{"id":13418},"twitter-bootstrap","Twitter Bootstrap",[18,13421,13422,13423,13428],{},"The latest popular layouting/css toolkit for common things like a grid layout. In my point of view the main advantage to\nolder CSS frameworks like ",[585,13424,13427],{"href":13425,"rel":13426},"http://960.gs/",[589],"960 Grid System"," is the use of LESS in Twitter Bootstrap, so for example the\ngrid columns can be altered by changing variables.",[1181,13430,13432],{"id":13431},"deployment-and-publishing","Deployment and Publishing",[18,13434,13435],{},"We set-up deployment via Git. Basically a Git post-receive hook calls a shell script that loads the correct Ruby\nversion via rvm and executes a rake task:",[43,13437,13439],{"className":8739,"code":13438,"language":8741,"meta":48,"style":48},"rake deploy:post_receive\n",[50,13440,13441],{"__ignoreMap":48},[53,13442,13443],{"class":55,"line":56},[53,13444,13438],{},[18,13446,13447],{},"The rake task then updates dependencies via ‘bundle install’, fetches blog posts via RSS and Tweets via Twitter API,\ncleans up old files and compiles the site via nanoc.",[18,13449,13450],{},"So adding new content or changing some styling is as easy as doing that locally, commiting and publishing by",[43,13452,13454],{"className":8739,"code":13453,"language":8741,"meta":48,"style":48},"git push stage\n",[50,13455,13456],{"__ignoreMap":48},[53,13457,13458],{"class":55,"line":56},[53,13459,13453],{},[18,13461,13462],{},"or",[43,13464,13466],{"className":8739,"code":13465,"language":8741,"meta":48,"style":48},"git push live\n",[50,13467,13468],{"__ignoreMap":48},[53,13469,13470],{"class":55,"line":56},[53,13471,13465],{},[18,13473,13474],{},"The advantage of using Git in this way is not just the easy publishing, but also it’s main feature: versioning. Content\nchanges can be followed, reproduced and rolled back. For example if you have a marketing campaign and styled your page\nin X-mas colors, you can do so in a branch and after New Years you can switch back to the usual styling by switching\nthe branch again.",[607,13476,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":13478},[13479,13480,13485],{"id":13356,"depth":86,"text":13357},{"id":13375,"depth":86,"text":13376,"children":13481},[13482,13483,13484],{"id":13398,"depth":126,"text":13399},{"id":13405,"depth":126,"text":13406},{"id":13418,"depth":126,"text":13419},{"id":13431,"depth":86,"text":13432},[613],"2012-03-06T10:59:57","With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic\\ndecisions filling our needs.","https://synyx.de/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git/",{},"/blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git",{"title":13332,"description":13342},"blog/new-homepage-with-nanoc-twitter-bootstrap-less-and-git",[13495,13496,13497,13498,13405,13368,11654,13499,8247,13500,756],"cms","css","git","homepage","opencms","styling","With the redesign of our current homepage there was the chance to re-evaluate our requirements and make pragmatic decisions filling our needs. Our previous websites were always implemented in OpenCms…","QKXG5zey8Vf0GGkf5yVxdjTPxZR1fNOEpeJFFbIENL4",{"id":13504,"title":13505,"author":13506,"body":13507,"category":13537,"date":13538,"description":13539,"extension":617,"link":13540,"meta":13541,"navigation":499,"path":13542,"seo":13543,"slug":13544,"stem":13545,"tags":13546,"teaser":13549,"__hash__":13550},"blog/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter.md","Eine Kochmuddi für hungrige Synyx-Mitarbeiter",[6810],{"type":11,"value":13508,"toc":13535},[13509,13512,13519,13522,13525,13528],[14,13510,13505],{"id":13511},"eine-kochmuddi-für-hungrige-synyx-mitarbeiter",[18,13513,13514,13518],{},[736,13515],{"alt":13516,"src":13517},"\"Kochmellie beim ersten Synyx-Einsatz\"","https://media.synyx.de/uploads//2011/06/mellie.jpg","\nWer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer\ngrößer. Der Magen grummelt. Die Konzentration lässt nach. Und dann stellt sich wie immer die Frage: „Was sollen wir denn\nheute Mittag essen?“",[18,13520,13521],{},"Ständig belegte Brötchen – das ist auf Dauer langweilig. Essen gehen – schön und gut, aber recht kostenintensiv und\nzeitaufwendig. Daher kochen bei Synyx ab und an Mitarbeiter für Mitarbeiter zum Selbstkostenpreis. Das macht zwar Spaß,\naber auch das kostet viel Zeit.",[18,13523,13524],{},"Aus diesem Grund haben die Synyx Chefs uns Mitarbeitern mal wieder was echt Gutes gegönnt: eine Kochmuddi! Zwei bis drei\nMal die Woche kommt Mellie, die Kochmuddi oder besser gesagt die Kochmellie ;-), bei uns vorbei und verwöhnt uns mit\nihren Kochkünsten.",[18,13526,13527],{},"Es gibt eine vegetarische und eine fleischhaltige Variante zum Selbstkostenpreis. Heute stand auf dem „Menüplan“ Nudeln\nmit Bolognese oder Nudeln mit Tomate-Zucchini-Soße. Dazu gab es Salat. Das Essen war ausgesprochen lecker. Weiter so!",[18,13529,13530],{},[585,13531,13534],{"href":13532,"rel":13533},"http://www.hypersmash.com/dreamhost/",[589],"Dreamhost promotion code",{"title":48,"searchDepth":86,"depth":86,"links":13536},[],[614],"2011-06-14T13:35:06","\\nWer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer\\ngrößer. Der Magen grummelt. Die Konzentration lässt nach. Und dann stellt sich wie immer die Frage: „Was sollen wir denn\\nheute Mittag essen?“","https://synyx.de/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter/",{},"/blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter",{"title":13505,"description":13518},"eine-kochmuddi-fur-hungrige-synyx-mitarbeiter","blog/eine-kochmuddi-fur-hungrige-synyx-mitarbeiter",[13547,13548,756],"kochen","mitarbeiter","Wer kennt das nicht: Die Uhrzeit geht langsam aber sicher auf die Mittagszeit zu und das Loch im Bauch wird immer größer. Der Magen grummelt. Die Konzentration lässt nach. Und…","K-fyhzn_D2LovH6cZp_mKMDWsSCoE8fQIZqnRuFxyTs",{"id":13552,"title":13553,"author":13554,"body":13555,"category":13574,"date":13575,"description":13576,"extension":617,"link":13577,"meta":13578,"navigation":499,"path":13579,"seo":13580,"slug":13581,"stem":13582,"tags":13583,"teaser":13588,"__hash__":13589},"blog/blog/opensource-is-not-just-about-the-license.md","Being Open Source instead of just Open-Sourcing or Open Source is not just about the license",[13334],{"type":11,"value":13556,"toc":13572},[13557,13560,13563,13566],[14,13558,13553],{"id":13559},"being-open-source-instead-of-just-open-sourcing-or-open-source-is-not-just-about-the-license",[18,13561,13562],{},"Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community\nthat matters, not just users, strong leaders that listen and still communicate their vision and goal, growing base of\ncontributors who don’t want to be ignored, even if there’s a benevolent dictator as project lead.",[18,13564,13565],{},"There are some popular Open Source projects which recently failed in some of these areas and got forked. Synyx decided\nto go with the forks even though we are mainly users and in these cases not main contributors. What’s the reason behind\nthese decisions? Our company vision tells us to live Open Source which includes much more than what it looks on its\nsurface. It’s not a goal in itself. We believe openness, communication and trancparency are key to quality software,\nespecially long term. That leads to more deliverable value and improved competitiveness as well as more transparency to\nthe community behind.",[18,13567,13568],{},[736,13569],{"alt":48,"src":13570,"title":13571},"https://media.synyx.de/uploads//2011/05/chili-300x199.jpg","chili",{"title":48,"searchDepth":86,"depth":86,"links":13573},[],[613,963],"2011-05-20T17:01:44","Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community\\nthat matters, not just users, strong leaders that listen and still communicate their vision and goal, growing base of\\ncontributors who don’t want to be ignored, even if there’s a benevolent dictator as project lead.","https://synyx.de/blog/opensource-is-not-just-about-the-license/",{},"/blog/opensource-is-not-just-about-the-license",{"title":13553,"description":13562},"opensource-is-not-just-about-the-license","blog/opensource-is-not-just-about-the-license",[13584,1909,13585,7263,13586,11654,12976,13587],"chiliproject","hudson","libreoffice","transparency","Open Source is not just about available sources or certain licenses. Successful Open Source projects have a community that matters, not just users, strong leaders that listen and still communicate…","CCudIs0-B2Q3HB1MJjr6jXo1c__MAyAy4luOsfGU9SQ",{"id":13591,"title":13592,"author":13593,"body":13594,"category":14037,"date":14038,"description":13601,"extension":617,"link":14039,"meta":14040,"navigation":499,"path":14041,"seo":14042,"slug":14043,"stem":14044,"tags":14045,"teaser":14054,"__hash__":14055},"blog/blog/utilizing-git-to-dive-into-huge-code-bases.md","Utilizing Git to dive into huge code bases – Git SVN Tips",[13334],{"type":11,"value":13595,"toc":14035},[13596,13599,13602,13605,13608,13622,13625,13628,13631,13634,13637,13640,13643,13646,13649,13652,13655,13658,13661,13664,13667,13670,13673,13687,13717,13720,13723,13726,13740,13742,13756,13759,13762,13788,13791,13834,13837,13840,13843,13862,13890,13893,13896,13899,13902,13926,13929,13943,13970,13973,13976,13979,13982,14005,14013,14027,14030,14033],[14,13597,13592],{"id":13598},"utilizing-git-to-dive-into-huge-code-bases-git-svn-tips",[18,13600,13601],{},"Unfortunately there are still projects not on dvsc like git. That’s especially true",[18,13603,13604],{},"for enterprise customers which are at least stuck on Subversion if not worse.",[18,13606,13607],{},"So the first thing I do on new projects I join:",[43,13609,13611],{"className":8739,"code":13610,"language":8741,"meta":48,"style":48},"\n git svn clone -s svn-url\n\n",[50,13612,13613,13617],{"__ignoreMap":48},[53,13614,13615],{"class":55,"line":56},[53,13616,500],{"emptyLinePlaceholder":499},[53,13618,13619],{"class":55,"line":86},[53,13620,13621],{}," git svn clone -s svn-url\n",[18,13623,13624],{},"Or installing Git if I have to work on customer provided machines. That’s even more",[18,13626,13627],{},"important than the rest of a development environment like an IDE.",[18,13629,13630],{},"From experience I find it especially useful to experiment with new code basis",[18,13632,13633],{},"utilizing Git. Grown and big projects aren’t easy to understand architecturally and",[18,13635,13636],{},"implementation wise without digging deep. With the help of Git you can jump right",[18,13638,13639],{},"in, without fear and without messing everything up or having too much unrevertable",[18,13641,13642],{},"local changes. Just commit early and often! By doing it locally, in experimental",[18,13644,13645],{},"branches you can try and learn. Before you publish something to a wider audience",[18,13647,13648],{},"(svn) you can reorder, cherrypick and change everything or parts of it. Git is my",[18,13650,13651],{},"tool of choice to get my hands dirty with legacy code (new one too of course).",[18,13653,13654],{},"Some useful tips on how I use Git-SVN:",[18,13656,13657],{},"SVN history is linear, so you can’t use branches and merge the usual git-way without",[18,13659,13660],{},"thinking.",[18,13662,13663],{},"What often happens to me is that I implement a new feature, do some refactorings on",[18,13665,13666],{},"my way etc and an urgant bug report comes along. But I commited on master, don’t",[18,13668,13669],{},"want to push it to SVN yet since it’s not finished yet and might not be stable. What",[18,13671,13672],{},"to do? git svn dcommit would push all my local master commits to svn. The solution:",[43,13674,13676],{"className":8739,"code":13675,"language":8741,"meta":48,"style":48},"\n git branch featureA\n\n",[50,13677,13678,13682],{"__ignoreMap":48},[53,13679,13680],{"class":55,"line":56},[53,13681,500],{"emptyLinePlaceholder":499},[53,13683,13684],{"class":55,"line":86},[53,13685,13686],{}," git branch featureA\n",[43,13688,13690],{"className":8739,"code":13689,"language":8741,"meta":48,"style":48},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[50,13691,13692,13697,13702,13707,13712],{"__ignoreMap":48},[53,13693,13694],{"class":55,"line":56},[53,13695,13696],{},"\u003Ctt>\n",[53,13698,13699],{"class":55,"line":86},[53,13700,13701],{}," |svn | master\n",[53,13703,13704],{"class":55,"line":126},[53,13705,13706],{}," ---o---o---o---o---o---o---o---o---o---o\n",[53,13708,13709],{"class":55,"line":163},[53,13710,13711],{}," | featureA\n",[53,13713,13714],{"class":55,"line":186},[53,13715,13716],{},"\u003C/tt>\n",[18,13718,13719],{},"Now both branches featureA and master point to the latest commit. But we want master",[18,13721,13722],{},"to point to an earlier commit. Let’s say the last 10 commits aren’t in SVN yet and",[18,13724,13725],{},"the last 8 are experimental, so 2 could be pushed.",[43,13727,13729],{"className":8739,"code":13728,"language":8741,"meta":48,"style":48},"\n git reset --hard HEAD~8\n\n",[50,13730,13731,13735],{"__ignoreMap":48},[53,13732,13733],{"class":55,"line":56},[53,13734,500],{"emptyLinePlaceholder":499},[53,13736,13737],{"class":55,"line":86},[53,13738,13739],{}," git reset --hard HEAD~8\n",[18,13741,13462],{},[43,13743,13745],{"className":8739,"code":13744,"language":8741,"meta":48,"style":48},"\n git reset --hard sha-hash-of-commit-to-point-to\n\n",[50,13746,13747,13751],{"__ignoreMap":48},[53,13748,13749],{"class":55,"line":56},[53,13750,500],{"emptyLinePlaceholder":499},[53,13752,13753],{"class":55,"line":86},[53,13754,13755],{}," git reset --hard sha-hash-of-commit-to-point-to\n",[18,13757,13758],{},"Now my master is in the state it was in 8 commits ago and my experimental changes",[18,13760,13761],{},"are still in featureA branch.",[43,13763,13765],{"className":8739,"code":13764,"language":8741,"meta":48,"style":48},"\u003Ctt>\n |svn | master\n ---o---o---o---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[50,13766,13767,13771,13776,13780,13784],{"__ignoreMap":48},[53,13768,13769],{"class":55,"line":56},[53,13770,13696],{},[53,13772,13773],{"class":55,"line":86},[53,13774,13775],{}," |svn | master\n",[53,13777,13778],{"class":55,"line":126},[53,13779,13706],{},[53,13781,13782],{"class":55,"line":163},[53,13783,13711],{},[53,13785,13786],{"class":55,"line":186},[53,13787,13716],{},[18,13789,13790],{},"I can continue with fixing that critical bug, commit and svn dcommit. Have a look on how your history look with gitk\n–all.",[43,13792,13794],{"className":8739,"code":13793,"language":8741,"meta":48,"style":48},"\u003Ctt>\n | svn\n ---o---o---o master\n /\n ---o---o---o\n \\---o---o---o---o---o---o---o\n | featureA\n\u003C/tt>\n",[50,13795,13796,13800,13805,13810,13815,13820,13825,13830],{"__ignoreMap":48},[53,13797,13798],{"class":55,"line":56},[53,13799,13696],{},[53,13801,13802],{"class":55,"line":86},[53,13803,13804],{}," | svn\n",[53,13806,13807],{"class":55,"line":126},[53,13808,13809],{}," ---o---o---o master\n",[53,13811,13812],{"class":55,"line":163},[53,13813,13814],{}," /\n",[53,13816,13817],{"class":55,"line":186},[53,13818,13819],{}," ---o---o---o\n",[53,13821,13822],{"class":55,"line":221},[53,13823,13824],{}," \\---o---o---o---o---o---o---o\n",[53,13826,13827],{"class":55,"line":242},[53,13828,13829],{}," | featureA\n",[53,13831,13832],{"class":55,"line":273},[53,13833,13716],{},[18,13835,13836],{},"Dcommit rebased 3 commits and especially if",[18,13838,13839],{},"there were some more upstream svn commits, I want to base my experimental stuff",[18,13841,13842],{},"ontop of this. So I do a",[43,13844,13846],{"className":8739,"code":13845,"language":8741,"meta":48,"style":48},"\n git checkout featureA\n git rebase master\n\n",[50,13847,13848,13852,13857],{"__ignoreMap":48},[53,13849,13850],{"class":55,"line":56},[53,13851,500],{"emptyLinePlaceholder":499},[53,13853,13854],{"class":55,"line":86},[53,13855,13856],{}," git checkout featureA\n",[53,13858,13859],{"class":55,"line":126},[53,13860,13861],{}," git rebase master\n",[43,13863,13865],{"className":8739,"code":13864,"language":8741,"meta":48,"style":48},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn | featureA\n\u003C/tt>\n",[50,13866,13867,13871,13876,13881,13886],{"__ignoreMap":48},[53,13868,13869],{"class":55,"line":56},[53,13870,13696],{},[53,13872,13873],{"class":55,"line":86},[53,13874,13875],{}," | master\n",[53,13877,13878],{"class":55,"line":126},[53,13879,13880],{}," ---o---o---o---o---o---o---o---o---o---o---o\n",[53,13882,13883],{"class":55,"line":163},[53,13884,13885],{}," | svn | featureA\n",[53,13887,13888],{"class":55,"line":186},[53,13889,13716],{},[18,13891,13892],{},"Even if I could live without the upstream changes on my featureA branch for now, I’d",[18,13894,13895],{},"need a rebase later anyway, so I can do it in advance. That’s because the history",[18,13897,13898],{},"wouldn’t be linear anymore by doing a three-way merge of my featureA into master without rebasing.",[18,13900,13901],{},"When I’m satisfied and with featureA and nothing changed in master I can",[43,13903,13905],{"className":8739,"code":13904,"language":8741,"meta":48,"style":48},"\n git checkout master\n git merge featureA\n git branch -d featureA\n\n",[50,13906,13907,13911,13916,13921],{"__ignoreMap":48},[53,13908,13909],{"class":55,"line":56},[53,13910,500],{"emptyLinePlaceholder":499},[53,13912,13913],{"class":55,"line":86},[53,13914,13915],{}," git checkout master\n",[53,13917,13918],{"class":55,"line":126},[53,13919,13920],{}," git merge featureA\n",[53,13922,13923],{"class":55,"line":163},[53,13924,13925],{}," git branch -d featureA\n",[18,13927,13928],{},"And since it’s a fast-forward merge can continue to push it to SVN",[43,13930,13932],{"className":8739,"code":13931,"language":8741,"meta":48,"style":48},"\n git svn dcommit\n\n",[50,13933,13934,13938],{"__ignoreMap":48},[53,13935,13936],{"class":55,"line":56},[53,13937,500],{"emptyLinePlaceholder":499},[53,13939,13940],{"class":55,"line":86},[53,13941,13942],{}," git svn dcommit\n",[43,13944,13946],{"className":8739,"code":13945,"language":8741,"meta":48,"style":48},"\u003Ctt>\n | master\n ---o---o---o---o---o---o---o---o---o---o---o\n | svn\n\u003C/tt>\n",[50,13947,13948,13952,13957,13961,13966],{"__ignoreMap":48},[53,13949,13950],{"class":55,"line":56},[53,13951,13696],{},[53,13953,13954],{"class":55,"line":86},[53,13955,13956],{}," | master\n",[53,13958,13959],{"class":55,"line":126},[53,13960,13880],{},[53,13962,13963],{"class":55,"line":163},[53,13964,13965],{}," | svn\n",[53,13967,13968],{"class":55,"line":186},[53,13969,13716],{},[18,13971,13972],{},"If something did change in master I just do another rebase before the merge.",[18,13974,13975],{},"If I come to the conclusion that my experimental branch was just for learning",[18,13977,13978],{},"purpose and only one or two useful refactoring or unit-test improving commits I take",[18,13980,13981],{},"only these to master and abandon the branch.",[43,13983,13985],{"className":8739,"code":13984,"language":8741,"meta":48,"style":48},"\n git checkout master\n git cherry-pick sha-of-one-commit\n git cherry-pick sha-of-another\n\n",[50,13986,13987,13991,13995,14000],{"__ignoreMap":48},[53,13988,13989],{"class":55,"line":56},[53,13990,500],{"emptyLinePlaceholder":499},[53,13992,13993],{"class":55,"line":86},[53,13994,13915],{},[53,13996,13997],{"class":55,"line":126},[53,13998,13999],{}," git cherry-pick sha-of-one-commit\n",[53,14001,14002],{"class":55,"line":163},[53,14003,14004],{}," git cherry-pick sha-of-another\n",[18,14006,14007,14008],{},"If I’m overall satisfied with the results of my experimental branch, but not with commit messages, how the commits are\nordered and maybe their scope, I use git\nrebases ",[585,14009,14012],{"href":14010,"rel":14011},"http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode",[589],"interactive mode",[43,14014,14016],{"className":8739,"code":14015,"language":8741,"meta":48,"style":48},"\n git rebase -i sha-after-this-commit\n\n",[50,14017,14018,14022],{"__ignoreMap":48},[53,14019,14020],{"class":55,"line":56},[53,14021,500],{"emptyLinePlaceholder":499},[53,14023,14024],{"class":55,"line":86},[53,14025,14026],{}," git rebase -i sha-after-this-commit\n",[18,14028,14029],{},"reordering commits, splitting commits, editing commit messages, squashing multiple commits together.",[18,14031,14032],{},"There are endless more possibilities to get a better grip on your code-base.",[607,14034,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":14036},[],[613],"2011-03-21T06:22:53","https://synyx.de/blog/utilizing-git-to-dive-into-huge-code-bases/",{},"/blog/utilizing-git-to-dive-into-huge-code-bases",{"title":13592,"description":13601},"utilizing-git-to-dive-into-huge-code-bases","blog/utilizing-git-to-dive-into-huge-code-bases",[14046,13497,14047,4144,14048,14049,14050,14051,14052,14053],"dvcs","productivity","subversion","svn","tips","vcs","versioncontrol","withoutfear","Unfortunately there are still projects not on dvsc like git. That’s especially true for enterprise customers which are at least stuck on Subversion if not worse. So the first thing…","jY0wlPCOmzUfKbhiV6O6parFyGSU5NhgtbhpJnpQyIQ",{"id":14057,"title":14058,"author":14059,"body":14060,"category":14111,"date":14112,"description":14113,"extension":617,"link":14114,"meta":14115,"navigation":499,"path":14116,"seo":14117,"slug":14064,"stem":14118,"tags":14119,"teaser":14121,"__hash__":14122},"blog/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles.md","Synyx Team bekommt drei neue Gesichter und das ist noch nicht alles …",[6810],{"type":11,"value":14061,"toc":14109},[14062,14065,14072,14075,14078,14081,14084,14087,14090,14093,14096,14099,14102],[14,14063,14058],{"id":14064},"synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",[18,14066,14067,14071],{},[736,14068],{"alt":14069,"src":14070},"\"Neue Gesichter bei Synyx\"","https://media.synyx.de/uploads//2011/02/neue_mitarbeiter.jpg","\nBereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber\nschnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als erwartet. Es gab zwar\neine große Anzahl von Bewerbern, doch häufig waren für die umfangreichen und verantwortungsvollen Aufgabengebiete nicht\ndie entsprechenden Bewerber dabei.",[18,14073,14074],{},"Nach einer letztendlich getroffenen Vorauswahl und einigen Vorstellungsgesprächen wurde uns dann schnell klar, wer die\npassenden Neulinge im Synyx-Team sind.",[18,14076,14077],{},"Aus diesem Grund wollen wir die neuen Synyxler einmal vorstellen.",[18,14079,14080],{},"Für den Bereich Office-Management haben wir Sabine David ins Boot geholt.",[18,14082,14083],{},"Die gelernte Bürokauffrau war zuvor als kaufmännische Angestellte tätig und sucht die Herausforderung in einem sehr\numfangreichen und verantwortungsvollen Tätigkeitsfeld.",[18,14085,14086],{},"Als neues „altes“ Gesicht begrüßen wir Jochen Schalanda in unserem Team.",[18,14088,14089],{},"Der Diplominformatiker (FH) war bereits während seines Praxissemesters und anschließender Diplomarbeit von 2008-2009\nein wertvolles Mitglied von Synyx. Das Thema seiner Diplomarbeit umfasste den Entwurf und die Umsetzung einer auf REST\naufsetzenden Interoperabilitäts-Schnittstelle und deren Integration in eine JEE-basierte Geschäftsanwendungen. Nach\nseiner Diplomarbeit und einer 13-monatigen Zeit als Software Engineer in Riad (Saudi Arabien) kehrt Jochen Schalanda\njetzt als Senior Developer zu Synyx zurück.",[18,14091,14092],{},"Und dann war da noch…",[18,14094,14095],{},"Andreas Weigel, der während seines Informatikstudiums (9.Semester(KIT)) bei uns arbeitet. Derzeit erweitert er sein\nWissen in OpenCms. Der Java – Erfahrene Student wird zukünftig die einzelnen Projektteams tatkräftig unterstützen.",[18,14097,14098],{},"Alle drei Neuankömmlinge heißen wir nochmals herzlich willkommen!",[18,14100,14101],{},"Zur weiteren Verstärkung unseres Teams suchen wir kreative Java-Entwickler, sowie eine/n Auszubildende/n zum\nFachinformatiker/in Systemintegration und eine/n Auszubildende/n zum Fachinformatiker/in Anwendungsentwicklung.",[18,14103,14104,14105,1890],{},"Nähere Informationen dazu gibt es auf der ",[585,14106,14108],{"href":730,"rel":14107},[589],"Homepage",{"title":48,"searchDepth":86,"depth":86,"links":14110},[],[614],"2011-02-22T16:20:28","\\nBereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber\\nschnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als erwartet. Es gab zwar\\neine große Anzahl von Bewerbern, doch häufig waren für die umfangreichen und verantwortungsvollen Aufgabengebiete nicht\\ndie entsprechenden Bewerber dabei.","https://synyx.de/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles/",{},"/blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",{"title":14058,"description":14071},"blog/synyx-team-bekommt-drei-neue-gesichter-und-das-ist-noch-nicht-alles",[13548,756,14120],"team","Bereits seit Ende 2010 war Synyx auf der Suche nach personeller Verstärkung in verschiedenen Bereichen. Wie sich aber schnell herausstellte, erwies sich die Suche nach den passenden Persönlichkeiten schwieriger als…","3UZuyLwS4FxS_Fc9443qyXu5SWrRhh7fCVG2X2cth8c",{"id":14124,"title":14125,"author":14126,"body":14127,"category":14198,"date":14199,"description":14200,"extension":617,"link":14201,"meta":14202,"navigation":499,"path":14203,"seo":14204,"slug":14206,"stem":14207,"tags":14208,"teaser":14214,"__hash__":14215},"blog/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann.md","'Fragile Agile' von Pavlo Baron und Michael Hüttermann",[8516],{"type":11,"value":14128,"toc":14196},[14129,14133,14152,14157,14160,14163,14168,14171,14176,14179,14182,14185,14188,14191],[14,14130,14132],{"id":14131},"fragile-agile-von-pavlo-baron-und-michael-hüttermann","\"Fragile Agile\" von Pavlo Baron und Michael Hüttermann",[18,14134,14135,14139,14140,14145,14146,14151],{},[736,14136],{"alt":14137,"src":14138},"\"fragile_agile\"","https://media.synyx.de/uploads//2010/09/fragile_agile.png","\nGespannt habe ich auf das neue Buch der Kollegen ",[585,14141,14144],{"href":14142,"rel":14143},"http://www.pbit.org",[589],"Baron"," und ",[585,14147,14150],{"href":14148,"rel":14149},"http://huettermann.net/",[589],"Hüttermann","\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.",[18,14153,14154],{},[573,14155,14156],{},"“In der freien Wirtschaft ist nur selten Platz für rein technische Spielereien. Gut, bei Google oder Microsoft\nvielleicht, deswegen zieht es da ja auch so viele Geeks hin, als wäre ihr Logo mit Honig beschmiert”",[18,14158,14159],{},"Heutzutage wird Agilität fast immer direkt gleichgesetzt mit Scrum und/oder Kanban.",[18,14161,14162],{},"Damit beginnt das Buch und räumt hier auch direkt mit dieser Fehlinterpretation auf, um in den folgenden Kapiteln\nMissverständnis für Missverständnis aufzuräumen. Hierbei gelingt es den Kollegen, man merkt ihnen die vielen Jahre\nErfahrung an, durch geschickte Metaphern, trotz des eigentlich sehr anstrengenden Stoffs, den Leser bei Laune und beim\nLesen und Verstehen zu halten. Zumindest erging es mir so. Es gibt wenige Fachbücher, die ich in 2 ICE-Sessions\ndurchgelesen hatte und direkt auch rezensieren wollte.",[18,14164,14165],{},[573,14166,14167],{},"“Truckfaktor: Die Anzahl von Menschen im Projektteam, die von einem Truck erfasst werden müssen, bevor das Projekt in\nernsthafte Schwierigkeiten gerät”",[18,14169,14170],{},"Das Buch erläutert sachlich, manchmal durchaus auch ein wenig (bewusst) polemisch, direkte Implikationen innerhalb von\nTeams. Besonderen Wert legen die Autoren hier auf die sozialen Komponenten des Menschen im Teams: Motivation,\nKommunikation und Nachhaltigkeit, wobei Letzteres aus meiner Sicht ein klein wenig zu kurz kommt.",[18,14172,14173],{},[573,14174,14175],{},"“Sie arbeiten schnell und somit agil? Falsch! Agil heißt, den Kunden zufriedenzustellen, nicht schnell etwas\nherunterzuklopfen”",[18,14177,14178],{},"Was man aber nicht erwarten darf, sind die 10 Tipps, die es dem Mitarbeiter im Projekt einfacher machen, erfolgreicher,\neffizienter zu arbeiten. Vermutlich ist das auch Absicht, damit der Leser mehr nachdenkt und nicht “stumpf” einem\nKatalog von Pattern folgt 😉",[18,14180,14181],{},"Die Beispiele und Berichte aus der realen Welt sind durchaus stimmig gewählt und bringen viele der Argumente noch klarer\nzum Vorschein. Besonders in den Kapiteln 9 und 10 (Funktionierende Software, Nachhaltige Geschwindigkeit) wird sich\nsicherlich fast jeder Entwickler wieder finden und die Argumente bestätigen können.",[18,14183,14184],{},"Als Ergänzung liegt dem Buch übrigens ein Gutscheincode für einen eBook Download bei! Hierfür muss man dem Hanser Verlag\ndicken Respekt zollen!",[18,14186,14187],{},"Ich kann dieses Buch jedem Projektleiter, jedem Entwickler aber auch jedem “Chef” nur wärmstens empfehlen, um zu\nbegreifen, wie die sozialen Aspekte direkt auf den Projekterfolg einwirken. Was ich besonders gelungen finde, ist das\nsich das Buch nicht an Knaben oder Serum entlang hangelt, sondern auch deutlich macht, dass Agilität zuerst einmal\nnichts mit einem Prozess- / Vorgehensmodell zu tun hat, sondern eigentlich schlicht das Zusammenwirken sozialer\nKomponenten in einem Team von “Machern” ist.",[18,14189,14190],{},"Das ist eines der Bücher, obwohl deutschsprachig, welches wirklich jeder einmal lesen sollte, der es mit Agilität ernst\nnimmt! Kurz, knackig, ohne Buzzwordanhäufung und wilde Tipps, sondern viel mehr zum selbst nachdenken anregend! (und das\nist aus meiner Sicht wiederum agil 🙂 )",[18,14192,14193],{},[573,14194,14195],{},"“Design is not what it looks like and feels like. Design is how it works!” (Steve Jobs)",{"title":48,"searchDepth":86,"depth":86,"links":14197},[],[614],"2010-09-28T09:17:01","\\nGespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann\\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.","https://synyx.de/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann/",{},"/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann",{"title":14125,"description":14205},"\nGespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann\nüber die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich\nAgilität aufgenommen und interpretiert werden kann. Zu oft habe ich selbst gesehen, wie man Agilität missbrauchen kann,\nsei es als Schutzschild für eigene Versäumnisse, sei es um ein Buzzword mehr in seinem Lebenslauf zu haben.","fragile-agile-von-pavlo-baron-und-michael-huttermann","blog/fragile-agile-von-pavlo-baron-und-michael-huttermann",[14209,14210,14211,14212,14047,14213,2507],"agil","baron","huttermann","processes","project-management","Gespannt habe ich auf das neue Buch der Kollegen Baron und Hüttermann über die Zerbrechlichkeit der Agilität gewartet. Zu oft habe ich selbst Erfahrung damit gemacht, wie missverständlich Agilität aufgenommen…","i8BRfnraw8pkykCcNeMsM3fcbvrjInhdaciQUi0tPj8",{"id":14217,"title":14218,"author":14219,"body":14221,"category":14234,"date":14235,"description":48,"extension":617,"link":14236,"meta":14237,"navigation":499,"path":14238,"seo":14239,"slug":14240,"stem":14241,"tags":14242,"teaser":14246,"__hash__":14247},"blog/blog/i-think-i-spider-1-0-released.md","I think I spider 1.0 released",[14220],"linsin",{"type":11,"value":14222,"toc":14232},[14223,14226],[14,14224,14218],{"id":14225},"i-think-i-spider-10-released",[18,14227,14228],{},[736,14229],{"alt":14230,"src":14231},"I Think I Spider","https://media.synyx.de/uploads//2010/09/app-icon-512px.png",{"title":48,"searchDepth":86,"depth":86,"links":14233},[],[2632,964],"2010-09-11T17:54:14","https://synyx.de/blog/i-think-i-spider-1-0-released/",{},"/blog/i-think-i-spider-1-0-released",{"title":14218,"description":48},"i-think-i-spider-1-0-released","blog/i-think-i-spider-1-0-released",[11770,14243,14244,14245,5720],"apple","iphone","ithinkispider","Today we are proud to present our own very first App (it’s actually our second Android App) that made it to the App Store and Android Market – I think…","ihp4cPfv4tMEA3ebuCKmOKtft0Yrr9slF_Lsd0LB1fw",{"id":14249,"title":14250,"author":14251,"body":14252,"category":14346,"date":14347,"description":48,"extension":617,"link":14348,"meta":14349,"navigation":499,"path":14350,"seo":14351,"slug":14352,"stem":14353,"tags":14354,"teaser":14357,"__hash__":14358},"blog/blog/resolutions-on-android.md","Android resolution and layout problems",[11661],{"type":11,"value":14253,"toc":14340},[14254,14257,14262,14266,14269,14272,14275,14278,14281,14285,14288,14291,14297,14300,14304,14307,14310,14313,14316,14319,14322,14325,14328,14331,14334,14337],[14,14255,14250],{"id":14256},"android-resolution-and-layout-problems",[18,14258,14259],{},[736,14260],{"alt":14230,"src":14261},"https://media.synyx.de/uploads//2010/07/512px.png",[1181,14263,14265],{"id":14264},"dp-or-not-dp","dp or not dp?",[18,14267,14268],{},"It was some work, but after a little time, the layout fitted for each density – well, at least so it seemed…",[18,14270,14271],{},"It fitted only for the three default dpi values (120, 160 and 240).",[18,14273,14274],{},"If you used a larger / smaller screen with the same density, the positions didn’t match exactly any more.",[18,14276,14277],{},"The problem was, that we used (as we are also said to always use) dp to set the views. It’s true that its nice to do\nthis, if you use the standard widgets and if you don’t have such a highly customized layout, but in our case it seemed\nlike the wrong decision to use dp.",[18,14279,14280],{},"The solution to this was to use pixel values instead and to create the layouts for the different resolutions and not the\ndensities. The new values were easy to calculate, because you only needed to take the values of the 480x320px\nresolution (exactly the same values as in dp) and multiply them by 0.75 for the 320x240px resolution and by 1.5 for\nthe 854×480 one (and adjust the height a little here…).",[1181,14282,14284],{"id":14283},"changing-the-layout-in-your-code","Changing the layout in your code",[18,14286,14287],{},"The next problem was (even before we converted the values in px) to display the layout on the 800×480 and 854×480\nresolutions, because you can’t declare layouts for both of them – they always take the same one.",[18,14289,14290],{},"We also didn’t want to have a black border for the bigger resolution, scaling the background to a bigger length was also\nno problem, so we decided to adjust the layout for this particular case in our code:",[43,14292,14295],{"className":14293,"code":14294,"language":3923},[3922],"\n//called in onCreate()\nif (getWindowManager().getDefaultDisplay().getHeight() == 800) {\n AbsoluteLayout.LayoutParams params;\n params = (LayoutParams) findViewById(R.id.someView).getLayoutParams();\n params.y = params.y - 7;\n// more adjusting here...\n}\n\n",[50,14296,14294],{"__ignoreMap":48},[18,14298,14299],{},"Well, maybe it isn’t a good solution, but we didn’t find any other possibility here.",[1181,14301,14303],{"id":14302},"using-resource-qualifiers-for-the-different-resolutions","Using resource qualifiers for the different resolutions",[18,14305,14306],{},"A downside of this approach is that you have to add adjustments to every resolution that is available now and that comes\nout in the future, like the bigger ones for tablets.",[18,14308,14309],{},"Right now, you can support most of the resolutions by simply declaring different layouts by using different folders (\nexcept for 800×480 and 854×480), but I’m not sure how it is going to be with new resolutions.",[18,14311,14312],{},"The layouts we declare are seperated in the following folders:",[18,14314,14315],{},"layout-normal-mdpi -> 320×480",[18,14317,14318],{},"layout-normal-hdpi -> 800×480 and 854×480 (adjusted in the code)",[18,14320,14321],{},"layout-normal-ldpi -> 400×240",[18,14323,14324],{},"layout-large-mdpi -> 800×480 tablet (mostly the same as the layout-normal-hdpi layout, but needs to be in this\nfolder)",[18,14326,14327],{},"layout-small-ldpi -> 320×240",[18,14329,14330],{},"Also one -landscape folder for each of them for the landscape layout for the widget (the rest of the app is portrait\nonly)",[1181,14332,7231],{"id":14333},"conclusion",[18,14335,14336],{},"If someone downloads the App with an unsupported resolution, it will not be displayed correctly. For such cases it could\nbe helpful if you could declare the resolutions that your app supports, or at least the aspect ratios and to have\nresource directory qualifiers for this, so that you don’t have to adjust your layout in the code.",[18,14338,14339],{},"My conclusion of this is that the wide variety of different resolutions, densities and especially aspect ratios makes it\nreally hard to create a good looking app that supports all of them. Google should really have put more restrictions to\nthese terms to make it easier for the developers so that they can provide better quality apps for the users.",{"title":48,"searchDepth":86,"depth":86,"links":14341},[14342,14343,14344,14345],{"id":14264,"depth":86,"text":14265},{"id":14283,"depth":86,"text":14284},{"id":14302,"depth":86,"text":14303},{"id":14333,"depth":86,"text":7231},[2632,964,1899],"2010-09-08T13:28:24","https://synyx.de/blog/resolutions-on-android/",{},"/blog/resolutions-on-android",{"title":14250,"description":48},"resolutions-on-android","blog/resolutions-on-android",[11770,14355,14245,11772,14356],"i-think-i-spider","resolution","During the process of developing I think I spider we discovered various problems regarding Android’s different resolutions. Here you can see which problems we encountered and how we solved them:…","zE1O2G5yrUPOJAy03zZlQOFstFCOfkCJc5_scwvunN8",{"id":14360,"title":14361,"author":14362,"body":14363,"category":14567,"date":14568,"description":14569,"extension":617,"link":14570,"meta":14571,"navigation":499,"path":14572,"seo":14573,"slug":14367,"stem":14575,"tags":14576,"teaser":14580,"__hash__":14581},"blog/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app.md","Dependency Hell or Including JSR303 into a hibernated JavaEE App",[8516],{"type":11,"value":14364,"toc":14565},[14365,14368,14375,14382,14385,14528,14535,14538,14549,14560,14563],[14,14366,14361],{"id":14367},"dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[18,14369,14370,14371,4312],{},"Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\nan OR-Mapper. (",[585,14372,14373],{"href":14373,"rel":14374},"http://redmine.synyx.org/projects/show/hades",[589],[18,14376,14377,14378,4312],{},"First I just followed the Spring tutorial which is quite simple and straight\nforward. (",[585,14379,14380],{"href":14380,"rel":14381},"http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#validation-beanvalidation-spring",[589],[18,14383,14384],{},"After a redeploy and writing an example bean and a test for it, it was just disappointing because nothing worked and the\nstacktrace was not really helping at a first look.",[43,14386,14388],{"className":8739,"code":14387,"language":8741,"meta":48,"style":48},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\njava.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\nat org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\nat org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\nat org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\nat org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\nat org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\nat org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\nat org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\nat org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\nat org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\nat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\nat java.lang.reflect.Method.invoke(Method.java:597)\nat org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\nat org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\nat org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\nat org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\nat org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\nat org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\nat org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\nat org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\nat org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\nat org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\nat org.apache.maven.surefire.Surefire.run(Surefire.java:177)\nat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\nat java.lang.reflect.Method.invoke(Method.java:597)\nat org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\nat org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n\n",[50,14389,14390,14395,14400,14405,14410,14415,14420,14425,14430,14435,14440,14445,14450,14455,14460,14465,14470,14475,14480,14485,14490,14495,14500,14505,14510,14514,14518,14523],{"__ignoreMap":48},[53,14391,14392],{"class":55,"line":56},[53,14393,14394],{},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\n",[53,14396,14397],{"class":55,"line":86},[53,14398,14399],{},"java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\n",[53,14401,14402],{"class":55,"line":126},[53,14403,14404],{},"at org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\n",[53,14406,14407],{"class":55,"line":163},[53,14408,14409],{},"at org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\n",[53,14411,14412],{"class":55,"line":186},[53,14413,14414],{},"at org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\n",[53,14416,14417],{"class":55,"line":221},[53,14418,14419],{},"at org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\n",[53,14421,14422],{"class":55,"line":242},[53,14423,14424],{},"at org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\n",[53,14426,14427],{"class":55,"line":273},[53,14428,14429],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\n",[53,14431,14432],{"class":55,"line":279},[53,14433,14434],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\n",[53,14436,14437],{"class":55,"line":496},[53,14438,14439],{},"at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\n",[53,14441,14442],{"class":55,"line":503},[53,14443,14444],{},"at org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\n",[53,14446,14447],{"class":55,"line":509},[53,14448,14449],{},"at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n",[53,14451,14452],{"class":55,"line":515},[53,14453,14454],{},"at java.lang.reflect.Method.invoke(Method.java:597)\n",[53,14456,14457],{"class":55,"line":521},[53,14458,14459],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\n",[53,14461,14462],{"class":55,"line":527},[53,14463,14464],{},"at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\n",[53,14466,14467],{"class":55,"line":533},[53,14468,14469],{},"at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\n",[53,14471,14472],{"class":55,"line":539},[53,14473,14474],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\n",[53,14476,14477],{"class":55,"line":545},[53,14478,14479],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\n",[53,14481,14482],{"class":55,"line":1700},[53,14483,14484],{},"at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\n",[53,14486,14487],{"class":55,"line":1709},[53,14488,14489],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\n",[53,14491,14492],{"class":55,"line":1718},[53,14493,14494],{},"at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\n",[53,14496,14497],{"class":55,"line":1724},[53,14498,14499],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\n",[53,14501,14502],{"class":55,"line":1743},[53,14503,14504],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\n",[53,14506,14507],{"class":55,"line":1749},[53,14508,14509],{},"at org.apache.maven.surefire.Surefire.run(Surefire.java:177)\n",[53,14511,14512],{"class":55,"line":1758},[53,14513,14449],{},[53,14515,14516],{"class":55,"line":1770},[53,14517,14454],{},[53,14519,14520],{"class":55,"line":1777},[53,14521,14522],{},"at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\n",[53,14524,14525],{"class":55,"line":1785},[53,14526,14527],{},"at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n",[18,14529,14530,14531,14534],{},"Why did I get an exception from a class called PersistenceUtil while just doing some validations via JSR303? Well, after\nreading the specification for JSR303 and some related blog entries, the solution is quite simple. JSR303 specification\nmanifests, that if a class PersistenceUtil is in the classpath, the JPATraversalResolver has to be integrated into the\nvalidation as well. If afterwards the method ",[573,14532,14533],{},"getPersistenceUtil()"," is getting called, its obvious that this method is\nnot available due to the fact that it is just a simple name matching.",[18,14536,14537],{},"Now I had THREE questions instead of one 🙂:",[577,14539,14540,14543,14546],{},[580,14541,14542],{},"Why is there a class called PersistenceUtil in my classpath while I DON’T use any JPA2 library (And this class is\nonly relevant for JPA2) – well this is surely just because the Hibernate guys had chosen the same name. Anyway, why the\nJPA2 guys used names like “PersistenceUtil” ????",[580,14544,14545],{},"Why the specification manifests that if a class called PersistenceUtil is in classpath, there must be also a JPA\nvalidation?",[580,14547,14548],{},"Why they do not additionally check against the needed method getPersistenceUtil() as well?",[18,14550,14551,14552,14555,14556,14559],{},"Anyway, as I needed a workaround, I checked the projects classpath… and I found my class ",[573,14553,14554],{},"PersistenceUtil"," but it was in\na jar called “ejb3-persistence-1.0.1GA.jar” – don’t ask me, why there also is a class called PersistenceUtil (btw. I\nhate ANY classes with ",[573,14557,14558],{},"UTIL"," in its name). I googled around a bit and found more people, who had the same problem and\nthe fix is really really easy. The guys who maintained the jar, fixed the problem due a little refactoring in version\n1.0.2GA.",[18,14561,14562],{},"So the only thing to do was to update this dependency in pom.xml to 1.0.2GA and everything went fine… Took me three\nhours to find out 🙁",[607,14564,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":14566},[],[614],"2010-07-20T16:10:45","Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\\nan OR-Mapper. (http://redmine.synyx.org/projects/show/hades)","https://synyx.de/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app/",{},"/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",{"title":14361,"description":14574},"Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE\napplication. The application is built on Spring 3.0 and uses our Synyx Hades project, which is also based on Spring, as\nan OR-Mapper. (http://redmine.synyx.org/projects/show/hades)","blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[14577,14578,14579,290,5971,756],"architecture","dependencyhell","dependencymanagement","Today i integrated the JSR303 reference implementation, which is Hibernate-Validator 4.x, into an existing JavaEE application. The application is built on Spring 3.0 and uses our Synyx Hades project, which…","jaMqXSr9rHWkAW8ml4UhHnhi_9ybaohJ2bmPFJ0BOnA",{"id":14583,"title":14584,"author":14585,"body":14586,"category":14617,"date":14618,"description":14619,"extension":617,"link":14620,"meta":14621,"navigation":499,"path":14622,"seo":14623,"slug":14625,"stem":14626,"tags":14627,"teaser":14629,"__hash__":14630},"blog/blog/uberladen-vs-trivialisieren-java-magazin-artikel.md","Überladen vs. Trivialisieren – Zwischen Platin und Blech",[6810],{"type":11,"value":14587,"toc":14615},[14588,14591,14612],[14,14589,14584],{"id":14590},"überladen-vs-trivialisieren-zwischen-platin-und-blech",[18,14592,14593,14594,14599,14600,14605,14606,14611],{},"Das Interview mit Joachim Arrasz, Softwarearchitekt bei ",[585,14595,14598],{"href":14596,"rel":14597},"http://www.synyx.de",[589],"Synyx GmbH & Co. KG",",\nund ",[585,14601,14604],{"href":14602,"rel":14603},"http://www.pbit.org/index.html",[589],"Pavlo Baron",", Enterprise Architekt in München, ist nun\nim ",[585,14607,14610],{"href":14608,"rel":14609},"http://it-republik.de/jaxenter/java-magazin-ausgaben/Lucene-000399.html",[589],"Java Magazin"," erschienen. In der Rubrik\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.",[18,14613,14614],{},"In der Diskussion geht es um die Balance zwischen minimalistischen und total überzogenen Lösungswegen in der\nSoftwarearchitektur. Anhand eines konkreten Beispiels aus der Praxis diskutieren Joachim Arrasz und Pavlo Baron, wie man\neine Lösung überladen oder allzu sehr vereinfachen kann. Und vor allem: Wie findet man die goldene Mitte?",{"title":48,"searchDepth":86,"depth":86,"links":14616},[],[613],"2010-06-08T13:28:00","Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG,\\nund Pavlo Baron, Enterprise Architekt in München, ist nun\\nim Java Magazin erschienen. In der Rubrik\\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.","https://synyx.de/blog/uberladen-vs-trivialisieren-java-magazin-artikel/",{},"/blog/uberladen-vs-trivialisieren-java-magazin-artikel",{"title":14584,"description":14624},"Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG,\nund Pavlo Baron, Enterprise Architekt in München, ist nun\nim Java Magazin erschienen. In der Rubrik\n“Architektur” erschien der Artikel mit dem Titel “Überladen vs. Trivialisieren – Zwischen Platin und Blech”.","uberladen-vs-trivialisieren-java-magazin-artikel","blog/uberladen-vs-trivialisieren-java-magazin-artikel",[14628,2347],"losungsweg","Das Interview mit Joachim Arrasz, Softwarearchitekt bei Synyx GmbH & Co. KG, und Pavlo Baron, Enterprise Architekt in München, ist nun im Java Magazin erschienen. In der Rubrik “Architektur” erschien…","nhsOQi94sEgrfo5KsCn9ZYMiAsYPQWlLxJhTZ_KBJOA",{"id":14632,"title":14633,"author":14634,"body":14635,"category":14729,"date":14730,"description":14642,"extension":617,"link":14731,"meta":14732,"navigation":499,"path":14733,"seo":14734,"slug":14735,"stem":14736,"tags":14737,"teaser":14739,"__hash__":14740},"blog/blog/5-reasons-for-teams.md","Five reasons why you should not work alone on IT-Projects",[5980],{"type":11,"value":14636,"toc":14722},[14637,14640,14643,14646,14649,14653,14662,14665,14669,14678,14682,14685,14688,14691,14694,14697,14701,14704,14713],[14,14638,14633],{"id":14639},"five-reasons-why-you-should-not-work-alone-on-it-projects",[18,14641,14642],{},"In my opinion its much better to have a team working on a project than a single person.",[18,14644,14645],{},"Even if this means that your customer might have to wait a bit longer for his project to start (because other projects\nalso occupy more people) everybody benefits because of increased productivity, better code and happy team members.",[18,14647,14648],{},"Here are my top five reasons why you should not leave one guy alone with a IT project…",[655,14650,14652],{"id":14651},"avoid-single-points-of-failure","Avoid Single Points of Failure",[18,14654,14655,14656,14661],{},"People get sick, are on vacation or might even resign from their job. You have to be able to compensate this by having\nother members that don’t need weeks or months to understand the projects requirements or codebase.\nAvoiding ",[585,14657,14660],{"href":14658,"rel":14659},"http://en.wikipedia.org/wiki/Single_Point_of_Failure",[589],"single points of failures"," saves you from having to get\nnew (other) people up-to-date which will cost you time, money and probably even upsets your customer.",[18,14663,14664],{},"Additionally your customer might ask for enhancements, bugfixes or even new (related) applications any time after the\noriginal project is finished. People that were involved on that project might be working on all kind of other projects\nthen. If you have more than one guy that knows the domain and the code then you gain alot of flexibility in resource\nmanagement.",[655,14666,14668],{"id":14667},"think-twice-triply","Think Twice / Triply / …",[18,14670,14671,14672,14677],{},"Another big benefit is, that team members have somebody to discuss any tasks with. These discussions might be about how\nto design a special feature or how the customers domain is modeled best. The members can save each other from producing\nbugs by reviewing each others code. They can also\nuse ",[585,14673,14676],{"href":14674,"rel":14675},"http://www.extremeprogramming.org/rules/pair.html",[589],"Pairprogramming"," for tricky parts of the application.",[655,14679,14681],{"id":14680},"individual-skills","Individual Skills",[18,14683,14684],{},"Each member of your team also brings his special and individual skills and expirience. One guy might be better when it\ncomes down to software architecture, another one might be the best choice to communicate with the customer and a third\nmight be an expert at designing user interfaces.",[18,14686,14687],{},"Since IT-Projects require alot of different skills each of them will benefit from an increased bandwidth of skills.",[655,14689,14690],{"id":2406},"Motivation",[18,14692,14693],{},"IT-Projects can be frustrating sometimes. One person that works alone gets demotivated easily because he feels left\nalone with whatever is frustrating him.",[18,14695,14696],{},"Being able to talk about problems and motivating each other helps to stay in a good temper and thus be more productive.\nHaving a good team and fun at work helps to endure frustrating parts of a project.",[655,14698,14700],{"id":14699},"distraction","Distraction",[18,14702,14703],{},"If someone has to accomplish everything by himself he might also get easy distracted. He might start browsing the web or\nhe pays more attention his colleagues projects than to his own. But he will probably stay focused if you have a team\nthat works with him, because he has someone to justify himself to.",[18,14705,14706,14707,14712],{},"A small daily standup-meeting (e.g. a ",[585,14708,14711],{"href":14709,"rel":14710},"http://www.scrumbasics.com/conducting-daily-scrum-meeting/",[589],"daily SCRUM",") where\neveryone explains what he has done the last day can help the team to stay focused.",[18,14714,14715,14716,14721],{},"Imagine how guilty you’d feel if everyone worked hard and you only watched videos on ",[585,14717,14720],{"href":14718,"rel":14719},"http://www.youtube.com/",[589],"youtube","\ninstead of writing a unit test for the feature you implemented the day before.",{"title":48,"searchDepth":86,"depth":86,"links":14723},[14724,14725,14726,14727,14728],{"id":14651,"depth":126,"text":14652},{"id":14667,"depth":126,"text":14668},{"id":14680,"depth":126,"text":14681},{"id":2406,"depth":126,"text":14690},{"id":14699,"depth":126,"text":14700},[613],"2010-05-25T11:03:19","https://synyx.de/blog/5-reasons-for-teams/",{},"/blog/5-reasons-for-teams",{"title":14633,"description":14642},"5-reasons-for-teams","blog/5-reasons-for-teams",[14047,14738,14213],"project","In my opinion its much better to have a team working on a project than a single person. Even if this means that your customer might have to wait a…","IVwgKz9GQYkBCYmZch_5lMbuzLkIv6gZqFpTi0fFih8",{"id":14742,"title":14743,"author":14744,"body":14745,"category":14946,"date":14947,"description":14948,"extension":617,"link":14755,"meta":14949,"navigation":499,"path":14950,"seo":14951,"slug":14749,"stem":14953,"tags":14954,"teaser":14959,"__hash__":14960},"blog/blog/modular-web-applications-based-on-spring.md","Modular Web-Applications based on Spring",[5980],{"type":11,"value":14746,"toc":14944},[14747,14750,14759,14762,14771,14785,14788,14812,14818,14826,14836,14871,14888,14894,14932,14942],[14,14748,14743],{"id":14749},"modular-web-applications-based-on-spring",[18,14751,14752,14753,14758],{},"Many of the Web-Applications we develop for our customers are based upon our small Framework on top\nof ",[585,14754,14757],{"href":14755,"rel":14756},"https://synyx.de/blog/modular-web-applications-based-on-spring/",[589],"Spring / Spring MVC",". This framework basically\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\nSpring already does.",[18,14760,14761],{},"Modular design of applications brings a lot of advantages but – as always – also some disadvantages. A modular structure\ncan help to increase cohesion and let developers focus on the function their concrete module has. Another big thing is\nreusability. So the core framework already brings functionality that is used in all projects that depend on the\nframework so far: user management for example. On the other hand modular design also brings complexity. I think its\nbusiness of a framework to hide this complexity from the user (developer in this case). Nevertheless its good when the\ndeveloper knows (and understands) what goes on under the hood and (even more important) can easily extend the framework\nwhere he needs to.",[18,14763,14764,14765,14770],{},"As I mentioned we use Spring / Spring MVC as a base for many projects. Spring provides a lot of points where you can\nextend the framework by implementing interfaces and defining or injecting these implementations to the classes that do\nthe real work (like interceptors\nin ",[585,14766,14769],{"href":14767,"rel":14768},"http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/mvc/annotation/DefaultAnnotationHandlerMapping.html",[589],"AnnotationHandlerMapping",").\nIn most cases this is enough. If you start to develop a modular application it is not. At least it was not for our case.",[18,14772,14773,14774,14777,14778,14781,14782],{},"Our Web-Applications based on the mentioned Framework always use at least two “modules”: The core-module (that brings\nuser management and some nice features to make coders happy) and the application itself. Each of these modules brings\nits own bean-configurations which are loaded all together using a wildcard resource like the following that reads all\n",[50,14775,14776],{},"beans.xml"," files within any subfolder of",[50,14779,14780],{},"META-INF"," in the applications classpath: ",[50,14783,14784],{},"classpath*:META-INF/**/beans.xml",[18,14786,14787],{},"This is a really simple way how the modules can interact since one module can provide a Service another depends on.",[18,14789,14790,14791,14796,14797,14800,14801,14804,14805,14808,14809,14811],{},"Now, the big problem is, that each module often brings its own components that have to be registered to Springs\n“services”. Let me explain the problem to you by talking\nabout",[585,14792,14795],{"href":14793,"rel":14794},"http://blog.synyx.de/2010/04/21/know-your-apis-lessons-learned-from-resourcebundle/",[589],"internationalization again",".\nEach module brings its own resource bundle containing the internationalization for its web-interface components. Spring\nprovides a simple way to register a",[50,14798,14799],{},"MessageSource"," by defining one bean with id",[50,14802,14803],{},"messageSource"," in your context. And that\nis exactly the problem. It is",[27,14806,14807],{},"one","bean. So you need a way where each module can register its own",[50,14810,14799],{},", even\nif Spring only supports one. So our framework has to handle this, because it also introduces the modular structure.",[18,14813,14814,14815,14817],{},"The “out of the box” way that would work is that the application, that assembles all these modules together defines the\n",[50,14816,14799],{}," with all basenames (of the properties-files) the application uses. But this would be part of the\nmentioned complexity that should be kept away from the daily business and brings some other problems in (what, if one\nmodule wants to store its internationalization within the database?…).",[18,14819,14820,14821],{},"So what did we do? We use a Simple plugin-mechanism a colleague developed and Synyx publishes\nOpenSource:",[585,14822,14825],{"href":14823,"rel":14824},"http://hera.synyx.org",[589],"Hera",[18,14827,14828,14829,14831,14832,14835],{},"We use a bean that gets registered as",[50,14830,14799],{}," to Spring that takes care of dispatching the message-resolving\nrequests to the real ",[50,14833,14834],{},"MessageSources"," implementations spread all over the modules.",[43,14837,14839],{"className":8739,"code":14838,"language":8741,"meta":48,"style":48},"\u003Cbean id=\"messageSource\">\n \u003Cproperty name=\"sources\">\n \u003Cplugin:list class=\"org.synyx.minos.message.ModuleMessageSource\"/>\n \u003C/property>\n \u003Cproperty name=\"useCodeAsDefaultMessage\" value=\"true\" />\n\u003C/bean>\n",[50,14840,14841,14846,14851,14856,14861,14866],{"__ignoreMap":48},[53,14842,14843],{"class":55,"line":56},[53,14844,14845],{},"\u003Cbean id=\"messageSource\">\n",[53,14847,14848],{"class":55,"line":86},[53,14849,14850],{}," \u003Cproperty name=\"sources\">\n",[53,14852,14853],{"class":55,"line":126},[53,14854,14855],{}," \u003Cplugin:list class=\"org.synyx.minos.message.ModuleMessageSource\"/>\n",[53,14857,14858],{"class":55,"line":163},[53,14859,14860],{}," \u003C/property>\n",[53,14862,14863],{"class":55,"line":186},[53,14864,14865],{}," \u003Cproperty name=\"useCodeAsDefaultMessage\" value=\"true\" />\n",[53,14867,14868],{"class":55,"line":221},[53,14869,14870],{},"\u003C/bean>\n",[18,14872,14873,14874,14877,14878,14881,14882,14884,14885,14887],{},"So this registers our",[50,14875,14876],{},"DispatchingMessageSource"," that gets injected into all beans within the context, implementing\n",[50,14879,14880],{},"ModuleMessageSource"," by Hera. This pretty much does the trick. The reason that we use",[50,14883,14880],{}," instead of\nSprings built-in",[50,14886,14799],{},"-interface is on the one hand so that we can do some performance-tweaks and on the\nother hand so that we dont get any “unwanted” implementations, which get to the context somehow.",[18,14889,14890,14891,14893],{},"With some simple dispatching logic within",[50,14892,14876],{}," we found a powerful way to conquer the insufficiency\nof Spring, in conjunction with our modular system.",[43,14895,14897],{"className":8739,"code":14896,"language":8741,"meta":48,"style":48},"List candidates = sources.getPluginsFor(getPrefixFromCode(code));\nfor (MessageSourcePlugin source : candidates) {\n MessageFormat format = resolveMessageWithSource(source, code, locale);\n if (null != format) {\n return format;\n }\n}\n",[50,14898,14899,14904,14909,14914,14919,14924,14928],{"__ignoreMap":48},[53,14900,14901],{"class":55,"line":56},[53,14902,14903],{},"List candidates = sources.getPluginsFor(getPrefixFromCode(code));\n",[53,14905,14906],{"class":55,"line":86},[53,14907,14908],{},"for (MessageSourcePlugin source : candidates) {\n",[53,14910,14911],{"class":55,"line":126},[53,14912,14913],{}," MessageFormat format = resolveMessageWithSource(source, code, locale);\n",[53,14915,14916],{"class":55,"line":163},[53,14917,14918],{}," if (null != format) {\n",[53,14920,14921],{"class":55,"line":186},[53,14922,14923],{}," return format;\n",[53,14925,14926],{"class":55,"line":221},[53,14927,4968],{},[53,14929,14930],{"class":55,"line":242},[53,14931,282],{},[18,14933,14934,14935,2937,14938,14941],{},"By the way, we use this mechanism a lot when it comes to easily extending functionality of the framework-core including\n",[50,14936,14937],{},"HandlerInterceptor",[50,14939,14940],{},"PropertyEditorRegistrar"," and our Modules itself.",[607,14943,5113],{},{"title":48,"searchDepth":86,"depth":86,"links":14945},[],[613],"2010-04-23T11:45:49","Many of the Web-Applications we develop for our customers are based upon our small Framework on top\\nof Spring / Spring MVC. This framework basically\\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\\nSpring already does.",{},"/blog/modular-web-applications-based-on-spring",{"title":14743,"description":14952},"Many of the Web-Applications we develop for our customers are based upon our small Framework on top\nof Spring / Spring MVC. This framework basically\nbrings often used components ready-to-use (or ready to customize) and – of course – makes things even simpler than\nSpring already does.","blog/modular-web-applications-based-on-spring",[14577,14955,14956,14957,14958,5141],"framework","internationalization","modular","plugin","Many of the Web-Applications we develop for our customers are based upon our small Framework on top of Spring / Spring MVC. This framework basically brings often used components ready-to-use…","9CCujFfUkmvCk9K2-lTvameRh4QQAhnYiCvpeGTJeT8",[14962,14964,14967,14970,14972,14975,14978,14981,14983,14985,14987,14989,14992,14994,14997,15000,15003,15006,15009,15012,15015,15018,15020,15023,15026,15029,15032,15034,15037,15040,15042,15044,15046,15049,15052,15055,15058,15061,15064,15067,15070,15072,15074,15077,15080,15083,15086,15089,15092,15095,15098,15100,15102,15105,15108,15110,15113,15115,15118,15121,15124,15126,15129,15131,15134,15137,15140,15143,15146,15149,15151,15153,15156,15159,15161,15163,15165,15168,15170,15173,15176,15179,15182,15185,15188,15191,15194,15197,15200,15203,15206,15209,15212,15215,15218,15221,15224,15227,15230,15232,15235,15237,15240,15243,15244,15246,15249,15251,15254,15257,15260,15262,15264,15266,15269,15272,15275,15278,15281,15284,15287,15290,15293,15296,15299,15301,15304,15307,15310,15313,15314,15317,15320,15323,15326,15329,15332,15335,15338,15341,15343,15346],{"slug":1079,"name":14963},"Jennifer Abel",{"slug":14965,"name":14966},"allmendinger","Otto Allmendinger",{"slug":14968,"name":14969},"antony","Ben Antony",{"slug":8516,"name":14971},"Joachim Arrasz",{"slug":14973,"name":14974},"bauer","David Bauer",{"slug":14976,"name":14977},"bechtold","Janine Bechtold",{"slug":14979,"name":14980},"boersig","Jasmin Börsig",{"slug":13334,"name":14982},"Fabian Buch",{"slug":4275,"name":14984},"Aljona Buchloh",{"slug":9,"name":14986},"Julia Burgard",{"slug":3227,"name":14988},"Caspar Schwedes",{"slug":14990,"name":14991},"christina-schmitt","Christina Schmitt",{"slug":7361,"name":14993},"Michael Clausen",{"slug":14995,"name":14996},"contargo_poetzsch","Thomas Pötzsch",{"slug":14998,"name":14999},"damrath","Sebastian Damrath",{"slug":15001,"name":15002},"daniel","Markus Daniel",{"slug":15004,"name":15005},"dasch","Julia Dasch",{"slug":15007,"name":15008},"denman","Joffrey Denman",{"slug":15010,"name":15011},"dfuchs","Daniel Fuchs",{"slug":15013,"name":15014},"dobler","Max Dobler",{"slug":15016,"name":15017},"dobriakov","Vladimir Dobriakov",{"slug":15019,"name":15019},"dreiqbik",{"slug":15021,"name":15022},"dschaefer","Denise Schäfer",{"slug":15024,"name":15025},"dschneider","Dominik Schneider",{"slug":15027,"name":15028},"duerlich","Isabell Duerlich",{"slug":15030,"name":15031},"dutkowski","Bernd Dutkowski",{"slug":15033,"name":15033},"eifler",{"slug":15035,"name":15036},"essig","Tim Essig",{"slug":15038,"name":15039},"ferstl","Maximilian Ferstl",{"slug":3032,"name":15041},"Prisca Fey",{"slug":2796,"name":15043},"Leonard Frank",{"slug":9286,"name":15045},"Arnold Franke",{"slug":15047,"name":15048},"frischer","Nicolette Rudmann",{"slug":15050,"name":15051},"fuchs","Petra Fuchs",{"slug":15053,"name":15054},"gari","Sarah Gari",{"slug":15056,"name":15057},"gast","Gast",{"slug":15059,"name":15060},"graf","Johannes Graf",{"slug":15062,"name":15063},"grammlich","Daniela Grammlich",{"slug":15065,"name":15066},"guthardt","Sabrina Guthardt",{"slug":15068,"name":15069},"haeussler","Johannes Häussler",{"slug":6178,"name":15071},"Daniel Hammann",{"slug":2515,"name":15073},"Julian Heetel",{"slug":15075,"name":15076},"heft","Florian Heft",{"slug":15078,"name":15079},"heib","Sebastian Heib",{"slug":15081,"name":15082},"heisler","Ida Heisler",{"slug":15084,"name":15085},"helm","Patrick Helm",{"slug":15087,"name":15088},"herbold","Michael Herbold",{"slug":15090,"name":15091},"hofmann","Peter Hofmann",{"slug":15093,"name":15094},"hopf","Florian Hopf",{"slug":15096,"name":15097},"jaud","Alina Jaud",{"slug":4756,"name":15099},"Robin De Silva Jayasinghe",{"slug":9468,"name":15101},"Jonathan Buch",{"slug":15103,"name":15104},"junghanss","Gitta Junghanß",{"slug":15106,"name":15107},"kadyietska","Khrystyna Kadyietska",{"slug":5980,"name":15109},"Marc Kannegiesser",{"slug":15111,"name":15112},"karoly","Robert Károly",{"slug":6810,"name":15114},"Katja Arrasz-Schepanski",{"slug":15116,"name":15117},"kaufmann","Florian Kaufmann",{"slug":15119,"name":15120},"kesler","Mike Kesler",{"slug":15122,"name":15123},"kirchgaessner","Bettina Kirchgäßner",{"slug":6446,"name":15125},"Yannic Klem",{"slug":15127,"name":15128},"klenk","Timo Klenk",{"slug":11661,"name":15130},"Tobias Knell",{"slug":15132,"name":15133},"knoll","Anna-Lena Knoll",{"slug":15135,"name":15136},"knorre","Matthias Knorre",{"slug":15138,"name":15139},"koenig","Melanie König",{"slug":15141,"name":15142},"kraft","Thomas Kraft",{"slug":15144,"name":15145},"krupicka","Florian Krupicka",{"slug":15147,"name":15148},"kuehn","Christian Kühn",{"slug":4430,"name":15150},"Christian Lange",{"slug":5149,"name":15152},"Luca Arrasz",{"slug":15154,"name":15155},"leist","Sascha Leist",{"slug":15157,"name":15158},"lihs","Michael Lihs",{"slug":14220,"name":15160},"David Linsin",{"slug":2413,"name":15162},"Christian Maniyar",{"slug":633,"name":15164},"Björnie",{"slug":15166,"name":15167},"martin-koch","Martin Koch",{"slug":12454,"name":15169},"Tobias Matt",{"slug":15171,"name":15172},"mennerich","Christian Mennerich",{"slug":15174,"name":15175},"menz","Alexander Menz",{"slug":15177,"name":15178},"meseck","Frederick Meseck",{"slug":15180,"name":15181},"messner","Oliver Messner",{"slug":15183,"name":15184},"michael-ploed","Michael Plöd",{"slug":15186,"name":15187},"mies","Marius Mies",{"slug":15189,"name":15190},"mihai","Alina Mihai",{"slug":15192,"name":15193},"moeller","Jörg Möller",{"slug":15195,"name":15196},"mohr","Rebecca Mohr",{"slug":15198,"name":15199},"moretti","David Moretti",{"slug":15201,"name":15202},"mueller","Sven Müller",{"slug":15204,"name":15205},"muessig","Alexander Müssig",{"slug":15207,"name":15208},"neupokoev","Grigory Neupokoev",{"slug":15210,"name":15211},"nussbaecher","Carmen Nussbächer",{"slug":15213,"name":15214},"ochs","Pascal Ochs",{"slug":15216,"name":15217},"oelhoff","Jan Oelhoff",{"slug":15219,"name":15220},"oengel","Yasin Öngel",{"slug":15222,"name":15223},"oezsoy","Enis Özsoy",{"slug":15225,"name":15226},"posch","Maya Posch",{"slug":15228,"name":15229},"ralfmueller","Ralf Müller",{"slug":15231,"name":15231},"redakteur",{"slug":15233,"name":15234},"reich","Michael Reich",{"slug":3392,"name":15236},"Karl-Ludwig Reinhard",{"slug":15238,"name":15239},"rmueller","Rebecca Müller",{"slug":15241,"name":15242},"rosum","Jan Rosum",{"slug":6358,"name":6358},{"slug":11280,"name":15245},"Sascha Rüssel",{"slug":15247,"name":15248},"sauter","Moritz Sauter",{"slug":2250,"name":15250},"Julian Schäfer",{"slug":15252,"name":15253},"scherer","Petra Scherer",{"slug":15255,"name":15256},"schlicht","Anne Schlicht",{"slug":15258,"name":15259},"schmidt","Jürgen Schmidt",{"slug":6179,"name":15261},"Tobias Schneider",{"slug":3031,"name":15263},"Benjamin Seber",{"slug":12840,"name":15265},"Marc Sommer",{"slug":15267,"name":15268},"speaker-fels","Jakob Fels",{"slug":15270,"name":15271},"speaker-gierke","Oliver Gierke",{"slug":15273,"name":15274},"speaker-krupa","Malte Krupa",{"slug":15276,"name":15277},"speaker-mader","Jochen Mader",{"slug":15279,"name":15280},"speaker-meusel","Tim Meusel",{"slug":15282,"name":15283},"speaker-milke","Oliver Milke",{"slug":15285,"name":15286},"speaker-paluch","Mark Paluch",{"slug":15288,"name":15289},"speaker-schad","Jörg Schad",{"slug":15291,"name":15292},"speaker-schalanda","Jochen Schalanda",{"slug":15294,"name":15295},"speaker-schauder","Jens Schauder",{"slug":15297,"name":15298},"speaker-unterstein","Johannes Unterstein",{"slug":15300,"name":2263},"speaker-wolff",{"slug":15302,"name":15303},"speaker-zoerner","Stefan Zörner",{"slug":15305,"name":15306},"stefan-belger","Stefan Belger",{"slug":15308,"name":15309},"steinegger","Roland Steinegger",{"slug":15311,"name":15312},"stern","sternchen synyx",{"slug":756,"name":756},{"slug":15315,"name":15316},"szulc","Mateusz Szulc",{"slug":15318,"name":15319},"tamara","Tamara Tunczinger",{"slug":15321,"name":15322},"theuer","Tobias Theuer",{"slug":15324,"name":15325},"thieme","Sandra Thieme",{"slug":15327,"name":15328},"thies-clasen","Marudor",{"slug":15330,"name":15331},"toernstroem","Olle Törnström",{"slug":15333,"name":15334},"ullinger","Max Ullinger",{"slug":15336,"name":15337},"ulrich","Stephan Ulrich",{"slug":15339,"name":15340},"wagner","Stefan Wagner",{"slug":7135,"name":15342},"Andreas Weigel",{"slug":15344,"name":15345},"werner","Fabian Werner",{"slug":15347,"name":15348},"wolke","Sören Wolke",["Reactive",15350],{"$scookieConsent":15351,"$ssite-config":15353},{"functional":15352,"analytics":15352},false,{"_priority":15354,"env":15358,"name":15359,"url":15360},{"name":15355,"env":15356,"url":15357},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",15363],{"category-it":-1,"authors":-1},"/blog/tags/it"]