\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/procedures.xml\"/>\n ...\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/all/table_add_column_xyz.xml\"/>\n \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/${projectname.branch}/adjust_procedure_asd.xml\"/>\n ...\n\u003C/databaseChangeLog>\n",[32,3174,3175,3180,3185,3190,3195,3200,3205,3210,3215,3220,3224],{"__ignoreMap":30},[35,3176,3177],{"class":37,"line":38},[35,3178,3179],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",[35,3181,3182],{"class":37,"line":71},[35,3183,3184],{},"\u003CdatabaseChangeLog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n",[35,3186,3187],{"class":37,"line":95},[35,3188,3189],{}," xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",[35,3191,3192],{"class":37,"line":119},[35,3193,3194],{}," xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd\">\n",[35,3196,3197],{"class":37,"line":143},[35,3198,3199],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/tables.xml\"/>\n",[35,3201,3202],{"class":37,"line":167},[35,3203,3204],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"install/all/procedures.xml\"/>\n",[35,3206,3207],{"class":37,"line":191},[35,3208,3209],{}," ...\n",[35,3211,3212],{"class":37,"line":215},[35,3213,3214],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/all/table_add_column_xyz.xml\"/>\n",[35,3216,3217],{"class":37,"line":470},[35,3218,3219],{}," \u003Cinclude relativeToChangelogFile=\"true\" file=\"changes/${projectname.branch}/adjust_procedure_asd.xml\"/>\n",[35,3221,3222],{"class":37,"line":604},[35,3223,3209],{},[35,3225,3226],{"class":37,"line":613},[35,3227,3228],{},"\u003C/databaseChangeLog>\n",[18,3230,3231,3232,3237],{},"As you can see, we have used the ",[1635,3233,3234],{},[2083,3235,3236],{},"${projectname.branch}"," placeholder in the path of a changelog. The file that is\nreferenced there, has to be added for each of the branches, because this changelog is also used for every branch. This\ncan be somewhat inconvenient in some times, when you only have to add a change to one of the branches, but that should\nnot happen that often. It’s more likely (at least for our case) that you have to adjust the same thing for all branches,\nbut a little differnt, or fill some table with different data.",[18,3239,3240],{},"Also, the right execution order of the scripts is secured this way. Furthermore, we don’t have to create and update one\nchangelog for every branch, where it can easily happen, that one file is left out and it goes through unnoticed. In our\nsetup, if you forget to add a file that’s declared in the changelog, that’s another case, because you will know it as\nsoon as you execute the script for the specific branch. So we considered this to be the best method to address multiple\nbranches.",[18,3242,3243,3244,3247,3248,3251],{},"You can also use the placeholder in other places, like the ",[2083,3245,3246],{},"loadUpdateData"," tag, where you can specify a .csv file from\nwhich liquibase will load data. There, You’ll only need to add the changelog to the ‘",[2083,3249,3250],{},"all","‘ folder and the .csv files in\neach branch folder. Furthermore, we are",[2851,3253,3255],{"id":3254},"maven-profiles","maven profiles",[18,3257,3258],{},"To configure and execute liquibase, we use different maven profiles. We need to specify the url, username and password\nfor each server, so we have one profile for each of them. The properties that are the same based on the environment (\ntest, stage, prod), are defined in a config file included from the pom (as already seen above), so we also need to add a\nproperty for the environment in each profile. Like this we can create a liquibase profile for each application of an\nenvironment of a branch (yup, there are quite some profiles because of this, but it is simply needed – you don’t have to\nkeep them in your settings.xml all the time, though, so it isn’t that much of a pain, once they are created 😛 ). By\nsetting the username and password locally in the maven settings.xml, we also keep sure that no passwords are commited in\nour version control.",[18,3260,3261],{},"example profile:",[25,3263,3265],{"className":2863,"code":3264,"language":2865,"meta":30,"style":30},"\n \u003Cprofile>\n \u003Cid>xyz-test\u003C/id>\n \u003Cproperties>\n \u003Cprojectname.branch>xyz\u003C/projectname.branch>\n \u003Cprojectname.environment>test\u003C/projectname.environment>\n \u003Cprojectname.dbName>dbname\u003C/projectname.dbName>\n \u003Cprojectname.liquibase.url>jdbc:oracle:thin:@192.168.224.234:1521:DBID\u003C/projectname.liquibase.url>\n \u003Cprojectname.liquibase.username>username\u003C/projectname.liquibase.username>\n \u003Cprojectname.liquibase.password>password\u003C/projectname.liquibase.password>\n \u003Cprojectname.liquibase.schemaName>schema\u003C/projectname.liquibase.schemaName>\n \u003Cprojectname.liquibase.changeLogFile>target/classes/path/to/changelog/db.changelog.xml\u003C/projectname.liquibase.changeLogFile>\n \u003C/properties>\n \u003C/profile>\n",[32,3266,3267,3272,3277,3282,3287,3292,3297,3302,3307,3312,3317,3322,3327,3332],{"__ignoreMap":30},[35,3268,3269],{"class":37,"line":38},[35,3270,3271],{"emptyLinePlaceholder":1589},"\n",[35,3273,3274],{"class":37,"line":71},[35,3275,3276],{}," \u003Cprofile>\n",[35,3278,3279],{"class":37,"line":95},[35,3280,3281],{}," \u003Cid>xyz-test\u003C/id>\n",[35,3283,3284],{"class":37,"line":119},[35,3285,3286],{}," \u003Cproperties>\n",[35,3288,3289],{"class":37,"line":143},[35,3290,3291],{}," \u003Cprojectname.branch>xyz\u003C/projectname.branch>\n",[35,3293,3294],{"class":37,"line":167},[35,3295,3296],{}," \u003Cprojectname.environment>test\u003C/projectname.environment>\n",[35,3298,3299],{"class":37,"line":191},[35,3300,3301],{}," \u003Cprojectname.dbName>dbname\u003C/projectname.dbName>\n",[35,3303,3304],{"class":37,"line":215},[35,3305,3306],{}," \u003Cprojectname.liquibase.url>jdbc:oracle:thin:@192.168.224.234:1521:DBID\u003C/projectname.liquibase.url>\n",[35,3308,3309],{"class":37,"line":470},[35,3310,3311],{}," \u003Cprojectname.liquibase.username>username\u003C/projectname.liquibase.username>\n",[35,3313,3314],{"class":37,"line":604},[35,3315,3316],{}," \u003Cprojectname.liquibase.password>password\u003C/projectname.liquibase.password>\n",[35,3318,3319],{"class":37,"line":613},[35,3320,3321],{}," \u003Cprojectname.liquibase.schemaName>schema\u003C/projectname.liquibase.schemaName>\n",[35,3323,3324],{"class":37,"line":619},[35,3325,3326],{}," \u003Cprojectname.liquibase.changeLogFile>target/classes/path/to/changelog/db.changelog.xml\u003C/projectname.liquibase.changeLogFile>\n",[35,3328,3329],{"class":37,"line":632},[35,3330,3331],{}," \u003C/properties>\n",[35,3333,3334],{"class":37,"line":640},[35,3335,3336],{}," \u003C/profile>\n",[18,3338,3339],{},"With this config, it uses the property file target/classes/liquibase-test.properties (keep in mind, the file initially\nlies in the folder src/main/resources, but because we build the project before we execute liquibase, it is then located\nunder target/classes/ , with its parameters replaced by our properties).",[18,3341,3342],{},"liquibase-test.properties:",[25,3344,3346],{"className":3063,"code":3345,"language":3065,"meta":30,"style":30},"changeLogFile=${projectname.liquibase.changeLogFile}\ndriver=oracle.jdbc.OracleDriver\nurl=${projectname.liquibase.url}\nusername=${projectname.liquibase.username}\npassword=${projectname.liquibase.password}\ndefaultSchemaName=${projectname.liquibase.schemaName}\nverbose=true\ndropFirst=false\n",[32,3347,3348,3353,3358,3363,3368,3373,3378,3383],{"__ignoreMap":30},[35,3349,3350],{"class":37,"line":38},[35,3351,3352],{},"changeLogFile=${projectname.liquibase.changeLogFile}\n",[35,3354,3355],{"class":37,"line":71},[35,3356,3357],{},"driver=oracle.jdbc.OracleDriver\n",[35,3359,3360],{"class":37,"line":95},[35,3361,3362],{},"url=${projectname.liquibase.url}\n",[35,3364,3365],{"class":37,"line":119},[35,3366,3367],{},"username=${projectname.liquibase.username}\n",[35,3369,3370],{"class":37,"line":143},[35,3371,3372],{},"password=${projectname.liquibase.password}\n",[35,3374,3375],{"class":37,"line":167},[35,3376,3377],{},"defaultSchemaName=${projectname.liquibase.schemaName}\n",[35,3379,3380],{"class":37,"line":191},[35,3381,3382],{},"verbose=true\n",[35,3384,3385],{"class":37,"line":215},[35,3386,3387],{},"dropFirst=false\n",[18,3389,3390],{},"Here we map our properties from the profiles to the actual liquibase property names and also set a few other liquibase\nconfigs.",[18,3392,3393],{},"For scripts you need to execute in another schema as the one the db user has set as the default schema, we also set the\ndefaultSchemaName property of liquibase (mainly the case, if we execute scripts as the SYSDBA user).",[483,3395,3397],{"id":3396},"execution-conclusion","Execution & Conclusion",[18,3399,3400],{},"Because of the use of maven, we can execute all of the changes from our local machines very easy:",[25,3402,3404],{"className":3063,"code":3403,"language":3065,"meta":30,"style":30},"mvn clean install -Pxyz-test\n",[32,3405,3406],{"__ignoreMap":30},[35,3407,3408],{"class":37,"line":38},[35,3409,3403],{},[18,3411,3412],{},"If you connect against a remote server, you are even warned with a dialogue that contains the database name, url and\nusername, it wants to execute the scripts on, before the scripts are actually executed. So you can check them again and\nabort the migration if you used the wrong profile.",[18,3414,3415],{},"With this setup we can now add scripts for only one branch, multiple branches, or all branches, without having to worry\nto forget to add one change to a branch and leaving the error unnoticed. Even if we forget to put some file in the\nfolder of one branch, our changelog file is global for all branches! So if we try to execute it the next time, liquibase\nnotices the missing file and informs us about this (and aborts the execution). And because we don’t have different\nfolders for the environments, but only the branches, this gets noticed on the test machines.",[18,3417,3418],{},"Please let us know what you think of our approach and if you know an even better one!",[1574,3420,3421],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":30,"searchDepth":71,"depth":71,"links":3423},[3424,3425,3431],{"id":2803,"depth":71,"text":2804},{"id":2848,"depth":71,"text":2849,"children":3426},[3427,3428,3429,3430],{"id":2853,"depth":95,"text":2854},{"id":3056,"depth":95,"text":3057},{"id":3165,"depth":95,"text":3166},{"id":3254,"depth":95,"text":3255},{"id":3396,"depth":71,"text":3397},[1582],"2013-04-12T11:19:55","In this post, we want to show you our Liquibase setup in a larger scale project that we’ve\\nbeen developing for some time now.","https://synyx.de/blog/liquibase-our-setup-in-a-larger-scale-project/",{},"/blog/liquibase-our-setup-in-a-larger-scale-project",{"title":2784,"description":3439},"In this post, we want to show you our Liquibase setup in a larger scale project that we’ve\nbeen developing for some time now.","blog/liquibase-our-setup-in-a-larger-scale-project",[3442,3443,3444,3445],"database","database-change-management","database-migration","liquibase","In this post, we want to show you our Liquibase setup in a larger scale project that we’ve been developing for some time now. Gather Requirements First off, a bit…","Edhd0rsE8wWORJZowAN7nLiXbUivWvpaqbjev0vh3MQ",{"id":3449,"title":3450,"author":3451,"body":3453,"category":3787,"date":3790,"description":3791,"extension":1586,"link":3792,"meta":3793,"navigation":1589,"path":3794,"seo":3795,"slug":3457,"stem":3796,"tags":3797,"teaser":3803,"__hash__":3804},"blog/blog/make-software-projects-fit-for-git.md","Make software-projects fit for git",[3452],"ruessel",{"type":11,"value":3454,"toc":3785},[3455,3458,3461,3467,3472,3496,3499,3505,3510,3520,3526,3532,3535,3570,3575,3578,3607,3610,3641,3644,3647,3723,3726,3729,3734,3737,3740,3743,3773,3776,3779,3782],[14,3456,3450],{"id":3457},"make-software-projects-fit-for-git",[18,3459,3460],{},"More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…",[18,3462,3463],{},[499,3464],{"alt":3465,"src":3466},"\"git-logo\"","https://media.synyx.de/uploads//2011/10/git-logo.png",[18,3468,3469,2040],{},[2083,3470,3471],{},"Source code has to be accessible",[18,3473,3474,3475,3481,3482,3488,3489,3495],{},"We are used to use subversion, a central repository with the leading stage of developement, when using git – all\nrepositories are equal. To take the best of both worlds we host a git-repository, which is defined to be leading (only\nby convention). We like to have things under control, so we\nuse ",[2035,3476,3480],{"href":3477,"rel":3478,"title":3479},"http://eagain.net/gitweb/?p=gitosis.git",[2038],"Gitosis download","gitosis"," to serve these repositories, but we think\nabout using ",[2035,3483,3487],{"href":3484,"rel":3485,"title":3486},"https://github.com/sitaramc/gitolite/wiki",[2038],"gitolite on GitHub","gitolite"," because of better/easier\naccess-management. You can also host at ",[2035,3490,3494],{"href":3491,"rel":3492,"title":3493},"https://github.com/",[2038],"Github","GitHub",", they do great work and it´s their daily\nbusiness.",[18,3497,3498],{},"What else do we need to develop an amazing piece of software in addition to good code and a working methodology? Which\ntools assist the development process and need capability to handle git-repositories?",[18,3500,3501],{},[499,3502],{"alt":3503,"src":3504},"\"chiliproject-logo\"","https://media.synyx.de/uploads//2011/10/chiliproject-logo.png",[18,3506,3507,2040],{},[2083,3508,3509],{},"Software should do what it is intended for",[18,3511,3512,3513,3519],{},"To reach this goal we collect production requirements and fragment the subsequent work into processable packets with\na *\n*project-management tool** called redmine, more precisely ",[2035,3514,3518],{"href":3515,"rel":3516,"title":3517},"https://www.chiliproject.org/",[2038],"ChiliProject","**chiliproject\n**",", an rapidly evolving fork of redmine.",[18,3521,3522],{},[499,3523],{"alt":3524,"src":3525},"\"jenkins_logo\"","https://media.synyx.de/uploads//2011/10/jenkins_logo.png",[18,3527,3528,3531],{},[2083,3529,3530],{},"Software is something executable",",",[18,3533,3534],{},"plain source-code is for documentation purposes 😉",[18,3536,3537,3538,3544,3545,3548,3549,3555,3556,3562,3563,3569],{},"We have to build it. Most of our projects are written in Java and built\nwith ",[2035,3539,3543],{"href":3540,"rel":3541,"title":3542},"http://maven.apache.org/",[2038],"Maven","Apache Maven",". To build the written code automatically and pursue the process of\n",[1635,3546,3547],{},"continuous integration"," we utilize a tool named hudson, more precisely ",[2035,3550,3554],{"href":3551,"rel":3552,"title":3553},"http://jenkins-ci.org/",[2038],"Jenkins","**Jenkins\n**",", an Oracle-independent fork (yeah we like using forks, especially when main\ndevelopers from the origin project are switching to the new fork, if you are interested in all of our reasons read\nthe ",[2035,3557,3561],{"href":3558,"rel":3559,"title":3560},"http://blog.synyx.de/2011/05/opensource-is-not-just-about-the-license/",[2038],"opensource is not just about the license","blogpost","\nwritten by ",[2035,3564,3568],{"href":3565,"rel":3566,"title":3567},"http://blog.synyx.de/autoren/?uid=14",[2038],"Fabian Buch","Fabian",")",[18,3571,3572],{},[1635,3573,3574],{},"So first mission is to make ChiliProject fit for git.",[18,3576,3577],{},"Luckily ChiliProject can handle git-repositories out of the box, but the repositories have to be cloned to localhost(\nthe machine running chiliproject). This can be achieved by giving read-rights to the user running chiliproject, in our\ncase this is generating a passwordless ssh-keypair, deploy the public part of it to gitosis and explicitly give rights\nto this public-key. To use the generated private-key every time we use git (in conjunction with ssh) we have to modify\nthe file ~/.ssh/config:",[25,3579,3581],{"className":27,"code":3580,"language":29,"meta":30,"style":30},"Host git.domain.tld\nUser git\nIdentityFile ~/.ssh/git_key.priv\n",[32,3582,3583,3591,3599],{"__ignoreMap":30},[35,3584,3585,3588],{"class":37,"line":38},[35,3586,3587],{"class":41},"Host",[35,3589,3590],{"class":45}," git.domain.tld\n",[35,3592,3593,3596],{"class":37,"line":71},[35,3594,3595],{"class":41},"User",[35,3597,3598],{"class":45}," git\n",[35,3600,3601,3604],{"class":37,"line":95},[35,3602,3603],{"class":41},"IdentityFile",[35,3605,3606],{"class":45}," ~/.ssh/git_key.priv\n",[18,3608,3609],{},"Now we need to clone the repository manually by login to the machine running Chili and do:",[25,3611,3613],{"className":27,"code":3612,"language":29,"meta":30,"style":30},"mkdir /path/to/git/repositories/\ncd /path/to/git/repositories/\ngit clone git@gitosis.domain.tld:gitrepo.git\n",[32,3614,3615,3623,3630],{"__ignoreMap":30},[35,3616,3617,3620],{"class":37,"line":38},[35,3618,3619],{"class":41},"mkdir",[35,3621,3622],{"class":45}," /path/to/git/repositories/\n",[35,3624,3625,3628],{"class":37,"line":71},[35,3626,3627],{"class":522},"cd",[35,3629,3622],{"class":45},[35,3631,3632,3635,3638],{"class":37,"line":95},[35,3633,3634],{"class":41},"git",[35,3636,3637],{"class":45}," clone",[35,3639,3640],{"class":45}," git@gitosis.domain.tld:gitrepo.git\n",[18,3642,3643],{},"for sure git has to be installed on the server running Chili. And the repository already exists…",[18,3645,3646],{},"but how do we keep this cloned repository up to date? We solved this by installing a cronjob running as the user,\nrunning Chili, every 5 minutes:",[25,3648,3650],{"className":27,"code":3649,"language":29,"meta":30,"style":30},"*/5 * * * * for i in /path/to/git/repositories/*/; do cd $i && git fetch; git reset refs/remotes/origin/master; done >>/dev/null 2>&1\n",[32,3651,3652],{"__ignoreMap":30},[35,3653,3654,3658,3661,3663,3666,3668,3670,3673,3676,3679,3682,3685,3688,3691,3694,3696,3699,3701,3703,3706,3709,3711,3714,3717,3720],{"class":37,"line":38},[35,3655,3657],{"class":3656},"szBVR","*",[35,3659,3660],{"class":52},"/5 ",[35,3662,3657],{"class":3656},[35,3664,3665],{"class":3656}," *",[35,3667,3665],{"class":3656},[35,3669,3665],{"class":3656},[35,3671,3672],{"class":3656}," for",[35,3674,3675],{"class":52}," i ",[35,3677,3678],{"class":3656},"in",[35,3680,3681],{"class":45}," /path/to/git/repositories/*/",[35,3683,3684],{"class":52},"; ",[35,3686,3687],{"class":3656},"do",[35,3689,3690],{"class":522}," cd",[35,3692,3693],{"class":52}," $i && ",[35,3695,3634],{"class":41},[35,3697,3698],{"class":45}," fetch",[35,3700,3684],{"class":52},[35,3702,3634],{"class":41},[35,3704,3705],{"class":45}," reset",[35,3707,3708],{"class":45}," refs/remotes/origin/master",[35,3710,3684],{"class":52},[35,3712,3713],{"class":3656},"done",[35,3715,3716],{"class":3656}," >>",[35,3718,3719],{"class":52},"/dev/null ",[35,3721,3722],{"class":3656},"2>&1\n",[18,3724,3725],{},"jap, you can log into a file instead of /dev/null, but we trust… 🙂",[18,3727,3728],{},"that´s it you can now add the local repository to your project in ChiliProject, but give full path including the\n“.git”-folder, it is little fussy in this point.",[18,3730,3731],{},[1635,3732,3733],{},"Get Jenkins to work with git",[18,3735,3736],{},"First, we have to do the same things done for ChiliProject, install the git-binary on the system running Jenkins,\ngenerate ssh-keypair, give read-rights to the user(possible stumbling block: we are running jenkins in a\njava-servlet-container so it´s the user running this container!)",[18,3738,3739],{},"modify ~/.ssh/config. Now we should be able to manually clone the targeted repositories, but that´s not what we want (\nremember automatically and continous integration?)",[18,3741,3742],{},"In order to be able to tag the built release version, the user running jenkins needs to give an author to the\ngit-repository, so modify/create ~/.gitconfig of this user:",[25,3744,3746],{"className":27,"code":3745,"language":29,"meta":30,"style":30},"[user]\n name = \"Jenkins\"\n email = \"jenkins@jenkins.domain.tld\"\n",[32,3747,3748,3753,3763],{"__ignoreMap":30},[35,3749,3750],{"class":37,"line":38},[35,3751,3752],{"class":52},"[user]\n",[35,3754,3755,3758,3760],{"class":37,"line":71},[35,3756,3757],{"class":41}," name",[35,3759,49],{"class":45},[35,3761,3762],{"class":45}," \"Jenkins\"\n",[35,3764,3765,3768,3770],{"class":37,"line":95},[35,3766,3767],{"class":41}," email",[35,3769,49],{"class":45},[35,3771,3772],{"class":45}," \"jenkins@jenkins.domain.tld\"\n",[18,3774,3775],{},"Jenkins is not able to handle git by default, we have to install a plugin: login -> Jenkins -> manage Jenkins ->\nmanage plugins -> available -> Git plugin (that´s easy to remember)",[18,3777,3778],{},"After restarting Jenkins you´ll find, under “projectX”/configure -> Source Code Management, the new section git where\nyou can insert the url of your repository -> save",[18,3780,3781],{},"Finally you can build the project(small prayer could help 😉 ) and enjoy the built software and Jenkins´ expandable\nworkflow…",[1574,3783,3784],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":30,"searchDepth":71,"depth":71,"links":3786},[],[3788,1582,3789],"administrator-blog","open-source-blog","2011-10-25T12:34:45","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to\\nmake a complete software-project fit for git, by not only using git-svn with subversion and git on top, some more\\nsteps are required than just handling files with git, learning its syntax and understanding the way it works…","https://synyx.de/blog/make-software-projects-fit-for-git/",{},"/blog/make-software-projects-fit-for-git",{"title":3450,"description":3460},"blog/make-software-projects-fit-for-git",[3798,3799,3800,3801,2779,3802],"apache","java","maven","open-source","software-development","More and more Projects at our company are taking advantage of distributed and local revision control by using git. So to make a complete software-project fit for git, by not…","vCNC4CoaSUI5ng_rt7Nu-7xypFBFlUaDbA6FV5gZLNA",{"id":3806,"title":3807,"author":3808,"body":3810,"category":3880,"date":3881,"description":3882,"extension":1586,"link":3883,"meta":3884,"navigation":1589,"path":3885,"seo":3886,"slug":3888,"stem":3889,"tags":3890,"teaser":3896,"__hash__":3897},"blog/blog/fragile-agile-von-pavlo-baron-und-michael-huttermann.md","'Fragile Agile' von Pavlo Baron und Michael Hüttermann",[3809],"arrasz",{"type":11,"value":3811,"toc":3878},[3812,3816,3834,3839,3842,3845,3850,3853,3858,3861,3864,3867,3870,3873],[14,3813,3815],{"id":3814},"fragile-agile-von-pavlo-baron-und-michael-hüttermann","\"Fragile Agile\" von Pavlo Baron und Michael Hüttermann",[18,3817,3818,3822,3823,2253,3828,3833],{},[499,3819],{"alt":3820,"src":3821},"\"fragile_agile\"","https://media.synyx.de/uploads//2010/09/fragile_agile.png","\nGespannt habe ich auf das neue Buch der Kollegen ",[2035,3824,3827],{"href":3825,"rel":3826},"http://www.pbit.org",[2038],"Baron",[2035,3829,3832],{"href":3830,"rel":3831},"http://huettermann.net/",[2038],"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,3835,3836],{},[2083,3837,3838],{},"“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,3840,3841],{},"Heutzutage wird Agilität fast immer direkt gleichgesetzt mit Scrum und/oder Kanban.",[18,3843,3844],{},"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,3846,3847],{},[2083,3848,3849],{},"“Truckfaktor: Die Anzahl von Menschen im Projektteam, die von einem Truck erfasst werden müssen, bevor das Projekt in\nernsthafte Schwierigkeiten gerät”",[18,3851,3852],{},"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,3854,3855],{},[2083,3856,3857],{},"“Sie arbeiten schnell und somit agil? Falsch! Agil heißt, den Kunden zufriedenzustellen, nicht schnell etwas\nherunterzuklopfen”",[18,3859,3860],{},"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,3862,3863],{},"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,3865,3866],{},"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,3868,3869],{},"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,3871,3872],{},"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,3874,3875],{},[2083,3876,3877],{},"“Design is not what it looks like and feels like. Design is how it works!” (Steve Jobs)",{"title":30,"searchDepth":71,"depth":71,"links":3879},[],[2134],"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":3807,"description":3887},"\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",[3891,3892,3893,2778,3894,2779,3895],"agil","baron","huttermann","productivity","scrum","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":3899,"title":3900,"author":3901,"body":3902,"category":4106,"date":4107,"description":4108,"extension":1586,"link":4109,"meta":4110,"navigation":1589,"path":4111,"seo":4112,"slug":3906,"stem":4114,"tags":4115,"teaser":4120,"__hash__":4121},"blog/blog/dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app.md","Dependency Hell or Including JSR303 into a hibernated JavaEE App",[3809],{"type":11,"value":3903,"toc":4104},[3904,3907,3914,3921,3924,4067,4074,4077,4088,4099,4102],[14,3905,3900],{"id":3906},"dependency-hell-or-including-jsr303-into-a-hibernated-javaee-app",[18,3908,3909,3910,3569],{},"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. (",[2035,3911,3912],{"href":3912,"rel":3913},"http://redmine.synyx.org/projects/show/hades",[2038],[18,3915,3916,3917,3569],{},"First I just followed the Spring tutorial which is quite simple and straight\nforward. (",[2035,3918,3919],{"href":3919,"rel":3920},"http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#validation-beanvalidation-spring",[2038],[18,3922,3923],{},"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.",[25,3925,3927],{"className":3063,"code":3926,"language":3065,"meta":30,"style":30},"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",[32,3928,3929,3934,3939,3944,3949,3954,3959,3964,3969,3974,3979,3984,3989,3994,3999,4004,4009,4014,4019,4024,4029,4034,4039,4044,4049,4053,4057,4062],{"__ignoreMap":30},[35,3930,3931],{"class":37,"line":38},[35,3932,3933],{},"testProperty(org.synyx.jsr303.validation.ValidatorTest) Time elapsed: 0.002 sec \u003C\u003C\u003C ERROR!\n",[35,3935,3936],{"class":37,"line":71},[35,3937,3938],{},"java.lang.NoSuchMethodError: javax.persistence.Persistence.getPersistenceUtil()Ljavax/persistence/PersistenceUtil;\n",[35,3940,3941],{"class":37,"line":95},[35,3942,3943],{},"at org.hibernate.validator.engine.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:33)\n",[35,3945,3946],{"class":37,"line":119},[35,3947,3948],{},"at org.hibernate.validator.engine.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:112)\n",[35,3950,3951],{"class":37,"line":143},[35,3952,3953],{},"at org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:47)\n",[35,3955,3956],{"class":37,"line":167},[35,3957,3958],{},"at org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:761)\n",[35,3960,3961],{"class":37,"line":191},[35,3962,3963],{},"at org.hibernate.validator.engine.ValidatorImpl.validatePropertyForGroup(ValidatorImpl.java:562)\n",[35,3965,3966],{"class":37,"line":215},[35,3967,3968],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:496)\n",[35,3970,3971],{"class":37,"line":470},[35,3972,3973],{},"at org.hibernate.validator.engine.ValidatorImpl.validateProperty(ValidatorImpl.java:131)\n",[35,3975,3976],{"class":37,"line":604},[35,3977,3978],{},"at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validateProperty(SpringValidatorAdapter.java:118)\n",[35,3980,3981],{"class":37,"line":613},[35,3982,3983],{},"at org.synyx.jsr303.validation.ValidatorTest.testProperty(ValidatorTest.java:64)\n",[35,3985,3986],{"class":37,"line":619},[35,3987,3988],{},"at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n",[35,3990,3991],{"class":37,"line":632},[35,3992,3993],{},"at java.lang.reflect.Method.invoke(Method.java:597)\n",[35,3995,3996],{"class":37,"line":640},[35,3997,3998],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)\n",[35,4000,4001],{"class":37,"line":646},[35,4002,4003],{},"at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)\n",[35,4005,4006],{"class":37,"line":659},[35,4007,4008],{},"at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)\n",[35,4010,4011],{"class":37,"line":678},[35,4012,4013],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)\n",[35,4015,4016],{"class":37,"line":691},[35,4017,4018],{},"at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)\n",[35,4020,4021],{"class":37,"line":704},[35,4022,4023],{},"at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)\n",[35,4025,4026],{"class":37,"line":716},[35,4027,4028],{},"at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)\n",[35,4030,4031],{"class":37,"line":728},[35,4032,4033],{},"at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)\n",[35,4035,4036],{"class":37,"line":741},[35,4037,4038],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)\n",[35,4040,4041],{"class":37,"line":749},[35,4042,4043],{},"at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)\n",[35,4045,4046],{"class":37,"line":760},[35,4047,4048],{},"at org.apache.maven.surefire.Surefire.run(Surefire.java:177)\n",[35,4050,4051],{"class":37,"line":766},[35,4052,3988],{},[35,4054,4055],{"class":37,"line":774},[35,4056,3993],{},[35,4058,4059],{"class":37,"line":780},[35,4060,4061],{},"at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)\n",[35,4063,4064],{"class":37,"line":785},[35,4065,4066],{},"at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)\n",[18,4068,4069,4070,4073],{},"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 ",[2083,4071,4072],{},"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,4075,4076],{},"Now I had THREE questions instead of one 🙂:",[2809,4078,4079,4082,4085],{},[2402,4080,4081],{},"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” ????",[2402,4083,4084],{},"Why the specification manifests that if a class called PersistenceUtil is in classpath, there must be also a JPA\nvalidation?",[2402,4086,4087],{},"Why they do not additionally check against the needed method getPersistenceUtil() as well?",[18,4089,4090,4091,4094,4095,4098],{},"Anyway, as I needed a workaround, I checked the projects classpath… and I found my class ",[2083,4092,4093],{},"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 ",[2083,4096,4097],{},"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,4100,4101],{},"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 🙁",[1574,4103,3421],{},{"title":30,"searchDepth":71,"depth":71,"links":4105},[],[2134],"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":3900,"description":4113},"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",[4116,4117,4118,3799,3800,4119],"architecture","dependencyhell","dependencymanagement","synyx","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":4123,"title":4124,"author":4125,"body":4127,"category":4277,"date":4278,"description":4279,"extension":1586,"link":4280,"meta":4281,"navigation":1589,"path":4282,"seo":4283,"slug":4131,"stem":4284,"tags":4285,"teaser":4288,"__hash__":4289},"blog/blog/template-based-document-generation-using-odfdom.md","Template based document generation using ODFDOM",[4126],"hopf",{"type":11,"value":4128,"toc":4271},[4129,4132,4135,4150,4154,4163,4174,4177,4181,4189,4192,4195,4198,4201,4204,4208,4211,4218,4223,4226,4229,4232,4235,4238,4241,4244,4247,4250,4253,4256,4258,4261,4264,4268],[14,4130,4124],{"id":4131},"template-based-document-generation-using-odfdom",[18,4133,4134],{},"Generating documents from data that is managed by a web application is a quite common need. Think about letters that are\ngenerated for a customer relationship management system or bills that are to be send for membership fees. For corporate\nidentity reasons you don’t want these documents to look like generated from a plain text file but you want to have\nlogos, tables, address labels and so on.",[18,4136,4137,4138,4143,4144,4149],{},"As the people that are designing the look of these documents often are not programmers it is a good idea to provide a\nway to use well know tools for creating and editing templates for these documents. What we have been doing for some time\nis to let the customer create template documents using ",[2035,4139,4142],{"href":4140,"rel":4141},"http://www.openoffice.org/",[2038],"OpenOffice.org",", the open source\nword processor, and transform these documents programmatically. OpenOffice.org uses the\nstandardized ",[2035,4145,4148],{"href":4146,"rel":4147},"http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office",[2038],"Open Document"," format to save its\ndocuments. Open Document files are zip archives that contain some XML documents as well as additional content (images,\nmacros, …).",[2851,4151,4153],{"id":4152},"the-uno-approach","The UNO approach",[18,4155,4156,4157,4162],{},"One of the older approaches we have been using for document processing is to access an OpenOffice.org instance running\non the server using the ",[2035,4158,4161],{"href":4159,"rel":4160},"http://wiki.services.openoffice.org/wiki/Uno",[2038],"UNO API",". UNO is a language agnostic API that\nprovides access to a lot of functionality of OpenOffice.org using an IDL. Though really powerful this approach also\nyields some drawbacks:",[2809,4164,4165,4168,4171],{},[2402,4166,4167],{},"Understanding and learning the UNO API is hard and takes a lot of time",[2402,4169,4170],{},"Some features of a document can’t be accessed using the API (e.g. the id of form control elements is saved in the\ndocument but is not accessible using UNO)",[2402,4172,4173],{},"An instance of OpenOffice.org can only be used by one thread at a time so you need some kind of instance pooling.",[18,4175,4176],{},"These drawbacks make it really hard to design and implement a robust system that can handle the load of a typical web\napplication and can still be maintained by a lot of developers.",[2851,4178,4180],{"id":4179},"odfdom","ODFDOM",[18,4182,4183,4184,4188],{},"Some time after the standardization of the Open Document format a new project was\nborn: ",[2035,4185,4180],{"href":4186,"rel":4187},"http://odftoolkit.org/projects/odfdom/pages/Home",[2038],", a sub project of the odftoolkt project. ODFDOM is a\npure Java API that provides both low level DOM access to the Open Document XML format as well as convenience\nfunctionality to manipulate document data.",[18,4190,4191],{},"As with ODFDOM the application and the document generation all run on the Java Virtual Machine it is easier to maintain\nfrom an adminitrators perspective. Also in contrast to the UNO API ODFDOM is really easy to use.",[18,4193,4194],{},"The following snippet creates a new text document, inserts some text and saves the document to a temp file.",[18,4196,4197],{},"`OdfTextDocument doc = OdfTextDocument.newTextDocument();",[18,4199,4200],{},"doc.addText(\"Hello World!\");",[18,4202,4203],{},"doc.save(File.createTempFile(\"odfdom\", \".odt\"));`",[2851,4205,4207],{"id":4206},"templating","Templating",[18,4209,4210],{},"To use Odfdom for templating you can choose one of the many placeholder approaches in OpenOffice.org. A very simple one\nis the use of user fields. To insert a user field in OpenOffice.org create a new document and go to Insert => Field\ncommand => Others. There you choose the tab variables and user field. You can add a name and a value. The value in our\ncase is only there to have a visual feedback when designing the document. The user field will be replaced automatically.",[18,4212,4213,4214,4217],{},"Let’s see how we can replace our placeholder value. The values for user fields as inserted above are stored in a node\n",[2035,4215,4216],{"href":4216},"text:user-field-decl",". This is an excerpt from the Open Document content.xml for a simple example document:",[18,4219,4220],{},[32,4221,4222],{},"\u003Ctext:user-field-decl office:value-type=\"string\" office:string-value=\"hello\" text:name=\"test\"/>",[18,4224,4225],{},"The user field is named test, it’s initial value for visual feedback is set to “hello”.",[18,4227,4228],{},"Imagine that the data that we want to replace with the values in the template is stored in a simple Map of Strings. To\nreplace all dummy values with values from you application you can access the nodes using the method\ngetElementsByTagName(“element”):",[18,4230,4231],{},"`Map\u003CString, String> values = new HashMap\u003CString, String>();",[18,4233,4234],{},"values.put(\"test\", \"inserted automatically\");",[18,4236,4237],{},"OdfDocument doc = OdfDocument.loadDocument(\"/path/to/template.odt\");",[18,4239,4240],{},"NodeList nodes = doc.getOfficeBody().getElementsByTagName(OdfTextUserFieldDecl.ELEMENT_NAME.getQName());",[18,4242,4243],{},"for (int i = 0; i \u003C nodes.getLength(); i++) {",[18,4245,4246],{},"OdfTextUserFieldDecl element = (OdfTextUserFieldDecl) nodes.item(i);",[18,4248,4249],{},"if (values.containsKey(element.getTextNameAttribute())) {",[18,4251,4252],{},"element.setOfficeStringValueAttribute(values.get(element.getTextNameAttribute()));",[18,4254,4255],{},"}",[18,4257,4255],{},[18,4259,4260],{},"doc.save(\"/path/to/result.odt\");`",[18,4262,4263],{},"When running the code above, the value in the document is replaced with the value set programmatically.",[2851,4265,4267],{"id":4266},"conclusion","Conclusion",[18,4269,4270],{},"So far we are running code using ODFDOM for document generation successfully in two larger projects that have been\ndeveloped recently. We believe that ODFDOM will help us delivering additional value for our customers with less\ndevelopment effort.",{"title":30,"searchDepth":71,"depth":71,"links":4272},[4273,4274,4275,4276],{"id":4152,"depth":95,"text":4153},{"id":4179,"depth":95,"text":4180},{"id":4206,"depth":95,"text":4207},{"id":4266,"depth":95,"text":4267},[1582,3789],"2010-06-13T17:57:40","Generating documents from data that is managed by a web application is a quite common need. Think about letters that are\\ngenerated for a customer relationship management system or bills that are to be send for membership fees. For corporate\\nidentity reasons you don’t want these documents to look like generated from a plain text file but you want to have\\nlogos, tables, address labels and so on.","https://synyx.de/blog/template-based-document-generation-using-odfdom/",{},"/blog/template-based-document-generation-using-odfdom",{"title":4124,"description":4134},"blog/template-based-document-generation-using-odfdom",[4286,3799,4179,4287],"document-management","openoffice-org","Generating documents from data that is managed by a web application is a quite common need. Think about letters that are generated for a customer relationship management system or bills…","s3AA3WrDYmRY0S6MePU2SvOKGaEOPjNDiiAX_h4Pn5U",{"id":4291,"title":4292,"author":4293,"body":4295,"category":4390,"date":4391,"description":4302,"extension":1586,"link":4392,"meta":4393,"navigation":1589,"path":4394,"seo":4395,"slug":4396,"stem":4397,"tags":4398,"teaser":4400,"__hash__":4401},"blog/blog/5-reasons-for-teams.md","Five reasons why you should not work alone on IT-Projects",[4294],"kannegiesser",{"type":11,"value":4296,"toc":4383},[4297,4300,4303,4306,4309,4313,4322,4325,4329,4338,4342,4345,4348,4352,4355,4358,4362,4365,4374],[14,4298,4292],{"id":4299},"five-reasons-why-you-should-not-work-alone-on-it-projects",[18,4301,4302],{},"In my opinion its much better to have a team working on a project than a single person.",[18,4304,4305],{},"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,4307,4308],{},"Here are my top five reasons why you should not leave one guy alone with a IT project…",[2851,4310,4312],{"id":4311},"avoid-single-points-of-failure","Avoid Single Points of Failure",[18,4314,4315,4316,4321],{},"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 ",[2035,4317,4320],{"href":4318,"rel":4319},"http://en.wikipedia.org/wiki/Single_Point_of_Failure",[2038],"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,4323,4324],{},"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.",[2851,4326,4328],{"id":4327},"think-twice-triply","Think Twice / Triply / …",[18,4330,4331,4332,4337],{},"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 ",[2035,4333,4336],{"href":4334,"rel":4335},"http://www.extremeprogramming.org/rules/pair.html",[2038],"Pairprogramming"," for tricky parts of the application.",[2851,4339,4341],{"id":4340},"individual-skills","Individual Skills",[18,4343,4344],{},"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,4346,4347],{},"Since IT-Projects require alot of different skills each of them will benefit from an increased bandwidth of skills.",[2851,4349,4351],{"id":4350},"motivation","Motivation",[18,4353,4354],{},"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,4356,4357],{},"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.",[2851,4359,4361],{"id":4360},"distraction","Distraction",[18,4363,4364],{},"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,4366,4367,4368,4373],{},"A small daily standup-meeting (e.g. a ",[2035,4369,4372],{"href":4370,"rel":4371},"http://www.scrumbasics.com/conducting-daily-scrum-meeting/",[2038],"daily SCRUM",") where\neveryone explains what he has done the last day can help the team to stay focused.",[18,4375,4376,4377,4382],{},"Imagine how guilty you’d feel if everyone worked hard and you only watched videos on ",[2035,4378,4381],{"href":4379,"rel":4380},"http://www.youtube.com/",[2038],"youtube","\ninstead of writing a unit test for the feature you implemented the day before.",{"title":30,"searchDepth":71,"depth":71,"links":4384},[4385,4386,4387,4388,4389],{"id":4311,"depth":95,"text":4312},{"id":4327,"depth":95,"text":4328},{"id":4340,"depth":95,"text":4341},{"id":4350,"depth":95,"text":4351},{"id":4360,"depth":95,"text":4361},[1582],"2010-05-25T11:03:19","https://synyx.de/blog/5-reasons-for-teams/",{},"/blog/5-reasons-for-teams",{"title":4292,"description":4302},"5-reasons-for-teams","blog/5-reasons-for-teams",[3894,4399,2779],"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",[4403,4405,4408,4411,4413,4416,4419,4422,4424,4427,4430,4433,4436,4439,4442,4445,4448,4451,4454,4457,4460,4463,4465,4468,4471,4474,4477,4479,4482,4485,4488,4491,4493,4496,4499,4502,4505,4508,4511,4514,4517,4520,4523,4526,4529,4532,4535,4538,4541,4543,4546,4549,4552,4555,4558,4560,4563,4566,4569,4572,4575,4578,4581,4583,4586,4589,4592,4595,4598,4601,4604,4607,4610,4613,4616,4619,4622,4625,4628,4631,4634,4637,4640,4643,4646,4649,4652,4655,4658,4661,4664,4667,4670,4673,4676,4679,4682,4685,4688,4690,4693,4696,4699,4702,4703,4705,4708,4711,4714,4717,4720,4723,4726,4729,4732,4735,4738,4741,4744,4747,4750,4753,4756,4759,4762,4765,4768,4771,4774,4777,4778,4781,4783,4786,4789,4792,4795,4798,4801,4804,4807,4810],{"slug":9,"name":4404},"Jennifer Abel",{"slug":4406,"name":4407},"allmendinger","Otto Allmendinger",{"slug":4409,"name":4410},"antony","Ben Antony",{"slug":3809,"name":4412},"Joachim Arrasz",{"slug":4414,"name":4415},"bauer","David Bauer",{"slug":4417,"name":4418},"bechtold","Janine Bechtold",{"slug":4420,"name":4421},"boersig","Jasmin Börsig",{"slug":4423,"name":3567},"buch",{"slug":4425,"name":4426},"buchloh","Aljona Buchloh",{"slug":4428,"name":4429},"burgard","Julia Burgard",{"slug":4431,"name":4432},"caspar-schwedes","Caspar Schwedes",{"slug":4434,"name":4435},"christina-schmitt","Christina Schmitt",{"slug":4437,"name":4438},"clausen","Michael Clausen",{"slug":4440,"name":4441},"contargo_poetzsch","Thomas Pötzsch",{"slug":4443,"name":4444},"damrath","Sebastian Damrath",{"slug":4446,"name":4447},"daniel","Markus Daniel",{"slug":4449,"name":4450},"dasch","Julia Dasch",{"slug":4452,"name":4453},"denman","Joffrey Denman",{"slug":4455,"name":4456},"dfuchs","Daniel Fuchs",{"slug":4458,"name":4459},"dobler","Max Dobler",{"slug":4461,"name":4462},"dobriakov","Vladimir Dobriakov",{"slug":4464,"name":4464},"dreiqbik",{"slug":4466,"name":4467},"dschaefer","Denise Schäfer",{"slug":4469,"name":4470},"dschneider","Dominik Schneider",{"slug":4472,"name":4473},"duerlich","Isabell Duerlich",{"slug":4475,"name":4476},"dutkowski","Bernd Dutkowski",{"slug":4478,"name":4478},"eifler",{"slug":4480,"name":4481},"essig","Tim Essig",{"slug":4483,"name":4484},"ferstl","Maximilian Ferstl",{"slug":4486,"name":4487},"fey","Prisca Fey",{"slug":4489,"name":4490},"frank","Leonard Frank",{"slug":2472,"name":4492},"Arnold Franke",{"slug":4494,"name":4495},"frischer","Nicolette Rudmann",{"slug":4497,"name":4498},"fuchs","Petra Fuchs",{"slug":4500,"name":4501},"gari","Sarah Gari",{"slug":4503,"name":4504},"gast","Gast",{"slug":4506,"name":4507},"graf","Johannes Graf",{"slug":4509,"name":4510},"grammlich","Daniela Grammlich",{"slug":4512,"name":4513},"guthardt","Sabrina Guthardt",{"slug":4515,"name":4516},"haeussler","Johannes Häussler",{"slug":4518,"name":4519},"hammann","Daniel Hammann",{"slug":4521,"name":4522},"heetel","Julian Heetel",{"slug":4524,"name":4525},"heft","Florian Heft",{"slug":4527,"name":4528},"heib","Sebastian Heib",{"slug":4530,"name":4531},"heisler","Ida Heisler",{"slug":4533,"name":4534},"helm","Patrick Helm",{"slug":4536,"name":4537},"herbold","Michael Herbold",{"slug":4539,"name":4540},"hofmann","Peter Hofmann",{"slug":4126,"name":4542},"Florian Hopf",{"slug":4544,"name":4545},"jaud","Alina Jaud",{"slug":4547,"name":4548},"jayasinghe","Robin De Silva Jayasinghe",{"slug":4550,"name":4551},"jbuch","Jonathan Buch",{"slug":4553,"name":4554},"junghanss","Gitta Junghanß",{"slug":4556,"name":4557},"kadyietska","Khrystyna Kadyietska",{"slug":4294,"name":4559},"Marc Kannegiesser",{"slug":4561,"name":4562},"karoly","Robert Károly",{"slug":4564,"name":4565},"karrasz","Katja Arrasz-Schepanski",{"slug":4567,"name":4568},"kaufmann","Florian Kaufmann",{"slug":4570,"name":4571},"kesler","Mike Kesler",{"slug":4573,"name":4574},"kirchgaessner","Bettina Kirchgäßner",{"slug":4576,"name":4577},"klem","Yannic Klem",{"slug":4579,"name":4580},"klenk","Timo Klenk",{"slug":2786,"name":4582},"Tobias Knell",{"slug":4584,"name":4585},"knoll","Anna-Lena Knoll",{"slug":4587,"name":4588},"knorre","Matthias Knorre",{"slug":4590,"name":4591},"koenig","Melanie König",{"slug":4593,"name":4594},"kraft","Thomas Kraft",{"slug":4596,"name":4597},"krupicka","Florian Krupicka",{"slug":4599,"name":4600},"kuehn","Christian Kühn",{"slug":4602,"name":4603},"lange","Christian Lange",{"slug":4605,"name":4606},"larrasz","Luca Arrasz",{"slug":4608,"name":4609},"leist","Sascha Leist",{"slug":4611,"name":4612},"lihs","Michael Lihs",{"slug":4614,"name":4615},"linsin","David Linsin",{"slug":4617,"name":4618},"maniyar","Christian Maniyar",{"slug":4620,"name":4621},"martin","Björnie",{"slug":4623,"name":4624},"martin-koch","Martin Koch",{"slug":4626,"name":4627},"matt","Tobias Matt",{"slug":4629,"name":4630},"mennerich","Christian Mennerich",{"slug":4632,"name":4633},"menz","Alexander Menz",{"slug":4635,"name":4636},"meseck","Frederick Meseck",{"slug":4638,"name":4639},"messner","Oliver Messner",{"slug":4641,"name":4642},"michael-ploed","Michael Plöd",{"slug":4644,"name":4645},"mies","Marius Mies",{"slug":4647,"name":4648},"mihai","Alina Mihai",{"slug":4650,"name":4651},"moeller","Jörg Möller",{"slug":4653,"name":4654},"mohr","Rebecca Mohr",{"slug":4656,"name":4657},"moretti","David Moretti",{"slug":4659,"name":4660},"mueller","Sven Müller",{"slug":4662,"name":4663},"muessig","Alexander Müssig",{"slug":4665,"name":4666},"neupokoev","Grigory Neupokoev",{"slug":4668,"name":4669},"nussbaecher","Carmen Nussbächer",{"slug":4671,"name":4672},"ochs","Pascal Ochs",{"slug":4674,"name":4675},"oelhoff","Jan Oelhoff",{"slug":4677,"name":4678},"oengel","Yasin Öngel",{"slug":4680,"name":4681},"oezsoy","Enis Özsoy",{"slug":4683,"name":4684},"posch","Maya Posch",{"slug":4686,"name":4687},"ralfmueller","Ralf Müller",{"slug":4689,"name":4689},"redakteur",{"slug":4691,"name":4692},"reich","Michael Reich",{"slug":4694,"name":4695},"reinhard","Karl-Ludwig Reinhard",{"slug":4697,"name":4698},"rmueller","Rebecca Müller",{"slug":4700,"name":4701},"rosum","Jan Rosum",{"slug":2065,"name":2065},{"slug":3452,"name":4704},"Sascha Rüssel",{"slug":4706,"name":4707},"sauter","Moritz Sauter",{"slug":4709,"name":4710},"schaefer","Julian Schäfer",{"slug":4712,"name":4713},"scherer","Petra Scherer",{"slug":4715,"name":4716},"schlicht","Anne Schlicht",{"slug":4718,"name":4719},"schmidt","Jürgen Schmidt",{"slug":4721,"name":4722},"schneider","Tobias Schneider",{"slug":4724,"name":4725},"seber","Benjamin Seber",{"slug":4727,"name":4728},"sommer","Marc Sommer",{"slug":4730,"name":4731},"speaker-fels","Jakob Fels",{"slug":4733,"name":4734},"speaker-gierke","Oliver Gierke",{"slug":4736,"name":4737},"speaker-krupa","Malte Krupa",{"slug":4739,"name":4740},"speaker-mader","Jochen Mader",{"slug":4742,"name":4743},"speaker-meusel","Tim Meusel",{"slug":4745,"name":4746},"speaker-milke","Oliver Milke",{"slug":4748,"name":4749},"speaker-paluch","Mark Paluch",{"slug":4751,"name":4752},"speaker-schad","Jörg Schad",{"slug":4754,"name":4755},"speaker-schalanda","Jochen Schalanda",{"slug":4757,"name":4758},"speaker-schauder","Jens Schauder",{"slug":4760,"name":4761},"speaker-unterstein","Johannes Unterstein",{"slug":4763,"name":4764},"speaker-wolff","Eberhard Wolff",{"slug":4766,"name":4767},"speaker-zoerner","Stefan Zörner",{"slug":4769,"name":4770},"stefan-belger","Stefan Belger",{"slug":4772,"name":4773},"steinegger","Roland Steinegger",{"slug":4775,"name":4776},"stern","sternchen synyx",{"slug":4119,"name":4119},{"slug":4779,"name":4780},"szulc","Mateusz Szulc",{"slug":2157,"name":4782},"Tamara Tunczinger",{"slug":4784,"name":4785},"theuer","Tobias Theuer",{"slug":4787,"name":4788},"thieme","Sandra Thieme",{"slug":4790,"name":4791},"thies-clasen","Marudor",{"slug":4793,"name":4794},"toernstroem","Olle Törnström",{"slug":4796,"name":4797},"ullinger","Max Ullinger",{"slug":4799,"name":4800},"ulrich","Stephan Ulrich",{"slug":4802,"name":4803},"wagner","Stefan Wagner",{"slug":4805,"name":4806},"weigel","Andreas Weigel",{"slug":4808,"name":4809},"werner","Fabian Werner",{"slug":4811,"name":4812},"wolke","Sören Wolke",["Reactive",4814],{"$scookieConsent":4815,"$ssite-config":4817},{"functional":4816,"analytics":4816},false,{"_priority":4818,"env":4822,"name":4823,"url":4824},{"name":4819,"env":4820,"url":4821},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",4827],{"category-management":-1,"authors":-1},"/blog/tags/management"]