AgilElephant Koodipolku iteraation muokkauksessa Tekijä: Pauli Vesterinen Omistaja: ElectricSeven Aihe: Koodipolku iteraation muokkauksessa Sivu 1 of 15
Dokumentti Historia Muutoshistoria Revision Numero Revision Päiväys Yhteenveto muutoksista Revision tekijä 1.0 04.10.04 Ensimmäinen versio - 1.1 27.10.04 Katselmoijat lisätty. - 1.2 09.01.05 Muutoksia dokumentti historiaan. Esa Mommo 1.3 18.02.05 Koodipolku iteraation muokkauksessa Pauli Vesterinen Hyväksyjät Tämä dokumentti vaatii seuraavien henkilöiden hyväksymiset Nimi Juha Kaarlas Tehtävä Projektipäällikkö Katselmoinnit Tämä dokumentti vaatii seuraavien henkilöiden katselmoinnin Nimi Juha Kaarlas Tehtävä Projektipäällikkö Jakelu Tämä dokumentti jaetaan seuraaville henkilöille Nimi Asiakas Juha Kaarlas Tehtävä koodipolkuun tutustuminen informatiivinen Aihe: Koodipolku iteraation muokkauksessa Sivu 2 of 15
Sisällysluettelo 1. Esittely...4 1.1 Tarkoitus...4 1.2 Kuvaus...4 1.3 Viittaukset...4 2. Iteraation muokkaaminen...5 2.1 Iteraation muokkaaminen käyttäjän näkökulmasta...5 2.2 Iteraation muokkaus kooditasolla...5 3. Koodilistaukset...6 3.1 viewiteration.jsp...6 3.2 modifyiteration.jsp...7 3.3 gethorizondetails.java...9 3.4 cyclemanagerbean.java (vain metodit, jotka mainittu)...11 3.5 modifyiteration.java...13 Aihe: Koodipolku iteraation muokkauksessa Sivu 3 of 15
1. Esittely 1.1 Tarkoitus Koodipolun tarkoituksena on luoda katsaus järjestelmän rakenteeseen esimerkin avulla. Polun avulla selviää, miten joku toiminto kulkee läpi eri luokkien kautta järjestelmässä. 1.2 Kuvaus Tässä dokumentissa käydään läpi esimerkin avulla AgilElephant -järjestelmän rakennetta sisäisesti. Esimerkkinä toimii iteraation muokkaus, josta käy ilmi mitä luokkia ja mitä metodeja kutsutaan ja miten niitä käytetään. Luokat ovat tarkemman tutkimisen mahdollistamiseksi liitetty tekstiin. 1.3 Viittaukset Koodiesimerkit joihin järjestelmässä viitataan, on AgilElephant -järjestelmästä otettuja luokkia. Niiden versiot ovat senhetkiset tämän dokumentin luontihetkellä. Luokkien sisältö saattaa muuttua dokumentin luomisajankohdan jälkeen. Tarvitaan lisäksi: AgilElephant Technical specification, techspec.doc Aihe: Koodipolku iteraation muokkauksessa Sivu 4 of 15
2. Iteraation muokkaaminen 2.1 Iteraation muokkaaminen käyttäjän näkökulmasta Iteraation muokkaus tapahtuu järjestelmässä kirjautumalla ensin sisään, sitten valitsemalla joko pääsivulta tai portfolion kautta iteraation ja klikkaamalla Actions -laatikosta Modify iteration. Tämän jälkeen avautuu sivu, jonka lomakkeeseen esitäyttyy iteraation tiedot. Tietoja voi muokata suoraan ja painaa lopuksi Save -painiketta, jolloin tiedot tallennetaan kyseiseen iteraatioon ja palataan iteraation tietojen tarkastelusivulle. 2.2 Iteraation muokkaus kooditasolla Aloitetaan koodin tutkiminen iteraation tiedot esittävältä viewiteration.jsp?iterationid=1 sivulta. Tässä tarkastellaan iteraatiota, jonka id tässä on 1. Kaikki jsp -sivut löytyvät polusta: agil/code/web/jsps/. Modify iteration -linkki ohjaa iteraation id:n modifyiteration.jsp - sivulle parametrina: modifyiteration.jsp?iterationid=1 Muokkaussivu (modifyiteration.jsp) ottaa parametrina saamansa iteraation id:n talteen requestista. Sitten samaisella jsp-sivulla käytetään tagia, joka lataa kyseisen iteraation tiedot esitäytettäväksi lomakkeen kenttiin: <agile:gethorizondetails horizonid="<%=iid%>"> Tämä luokka löytyy polusta: agil/code/web/src/fi/hut/soberit/agile/web/taglib/ GetHorizonDetails.java käyttää CycleManager- ja UserManager beaneja, jotka löytyvät: agil/code/app/src/fi/hut/soberit/agile/logic/beans/ Beaneihin luodaan yhteys dostarttag() -metodissa. Samaisessa metodissa haetaan myös cyclemanagerbean -luokan gettimehorizondetails() -metodin avulla iteraatio-olio tietoineen käytettäväksi, parametrinä iteraation id. Tämä tapahtuu hibernaten get() -metodilla, joka palauttaa olion. Iteraatio on Timehorizon -luokan olio, Timehorizon.java löytyy polusta: agil/code/hibernate/src/fi/hut/soberit/agile/hibernate/ GetHorizonDetails.java -luokan dostarttag() -metodissa asetetaan jsp-sivulla käytettävät attribuutit hakemalla niiden tiedot Timehorizon.java -luokan get -metodeilla. Nyt modifyiteration.jsp -sivun lomakkeeseen(modifyiterationform) voidaan ladata iteraation tietoja <%=attribuutinnimi%> -tagien avulla. Kalenterin käyttämät scriptit löytyvät polusta: agil/code/web/jsps/script/cal/ Kun tiedot on muutettu kenttiin halutuiksi ja painetaan save, lähetetään tiedot servletin ModifyIteration.java käsiteltäviksi. Tämä servlet löytyy polusta: agil/code/web/src/fi/hut/soberit/agile/web/servlets/ ModifyIteration.java -luokassa otetaan sitten talteen jsp-sivun lähettämät tiedot handlerequest() -metodissa. Tässä metodissa luodaan yhteys cyclemanager -beaniin, joka palauttaa iteraatio-olion hibernatea käyttäen. Nyt tähän iteraatio-olioon voidaan tallentaa sen tietoja erilaisten set- metodien avulla. Tiedot tallennetaan kantaan kutsumalla cyclemanager -beanin modifyiteration() -metodia, joka kirjoittaa kantaan käyttämällä hibernatea. ModifyIteration.java - servletin lopussa on vielä uudelleenohjauskäsky, jolla serveri tietää ohjata viewiteration.jsp -sivulle, näyttämään muutetut arvot. Aihe: Koodipolku iteraation muokkauksessa Sivu 5 of 15
3. Koodilistaukset 3.1 viewiteration.jsp <%@ include file="page_start.jsp" %> <%@ include file="page_header.jsp" %> <% Integer iid = new Integer(0); iid = new Integer(request.getParameter("iterationId")); catch (Exception e) { if (iid.intvalue()!= 0) { %> <agile:gethorizondetails horizonid="<%=iid%>"> <% pagecontext.setattribute("newissuelinkbacklog", horizonbacklog); %> </agile:gethorizondetails> <% %> <%@ include file="main_links.jsp" %> <!-- actions: contains page specific actions --> <div id="actions"><div> <h1>actions</h1> <a href="modifyiteration.jsp?iterationid=<%=iid%>">modify iteration</a> <a href="setiterationmembers.jsp?iterationid=<%=iid%>">set iteration members</a> <a href="removehorizon?iterationid=<%=iid%>" onclick="returnconfirm('are you sure you want to remove this iteration?')" />Remove iteration</a> </div></div> <!-- main content --> <div id="main"><div> <% Integer iterationbacklogid = new Integer(0); %> <agile:gethorizondetails horizonid="<%=iid%>"> <% iterationbacklogid = horizonbacklog; %> <p><a href="viewrelease.jsp?releaseid=<%=horizonparenthorizonid%>"><%=horizonparenthorizonna me%> release</a> <agile:listreleaseproducts releaseid="<%=horizonparenthorizonid%>"> <a href="viewproduct.jsp?productid=<%=productid%>"><%=productname%></a> </agile:listreleaseproducts> </p> <h1><%=horizonname%> iteration (<%=horizonstart%> - <%=horizonend%>)</h1></p> <p><b>goals:</b> <%=horizongoals%></p> <p><b>qa Goals:</b> <%=horizonqagoals%></p> <p><b>iteration leader:</b> <%=horizonleadname%></p> </agile:gethorizondetails> <h3>backlog items in iteration backlog</h3> <form method="post" action="bulkmoveissues"> <input type="hidden" name="iterationid" value="<%=iid%>" /> <input type="hidden" name="backlogid" value="<%=iterationbacklogid%>" /> <input type="hidden" name="callingpage" value="viewiteration.jsp" /> <table> <th>backlog item ID</th> <th>summary</th> <th>assignee</th> <th>reporter</th> <th>priority</th> <th>status</th> Aihe: Koodipolku iteraation muokkauksessa Sivu 6 of 15
<th>completeness</th> <th>move</th> <agile:listbacklogissues backlogid="<%=iterationbacklogid%>"> <td><a href="viewissue.jsp?issueid=<%=issueid%>"><%=issuevisibleid%></a></td> <td><%=issuesummary%></td> <td><%=issueassigneename%></td> <td><%=issuereportername%></td> <td><%=instanceitemprioritytext%></td> <td><%=instanceitemstatustext%></td> <td><%=issuecompleteness%></td> <td><input class="checkbox" type="checkbox" name="itemid<%=issueid%>" /></td> </agile:listbacklogissues> </table> Move selected items to another backlog <select name="backlogtoid" class="input150"> <agile:listmoveablebacklogs iterationid="<%=iid%>"> <option value="<%=moveablebacklogid%>"><%=moveablebacklogname%> <% if (moveablebacklogtype.equals(fi.hut.soberit.agile.hibernate.backlog.increment)) { %> (i) <% else if (moveablebacklogtype.equals(fi.hut.soberit.agile.hibernate.backlog.release)) { %> (r) <% else { %> (p) <% %> </agile:listmoveablebacklogs> </select> <input type="submit" class="button" name="submit" value="move" /> </form> <p> <a href="edititerationeffort.jsp?iterationid=<%=iid%>">change view to effort editing</a> </p> </div></div> <%@ include file="page_footer.jsp" %> <%@ include file="page_end.jsp" %> 3.2 modifyiteration.jsp <%@ include file="page_start.jsp" %> <%@ include file="page_header.jsp" %> <%@ include file="main_links.jsp" %> <% Integer iid = new Integer(0); iid = new Integer(request.getParameter("iterationId")); catch (Exception e) { %> <!-- actions: contains page specific actions --> <div id="actions"><div> <h1>actions</h1> </div></div> <!-- main content --> <div id="main"><div> <h1>modify iteration</h1> Aihe: Koodipolku iteraation muokkauksessa Sivu 7 of 15
<agile:printerror /> <agile:gethorizondetails horizonid="<%=iid%>"> <form method="post" name="modifyiterationform" action="modifyiteration"> <input type="hidden" name="iterationid" value="<%=iid%>" /> <input type="hidden" name="callingpage" value="modifyiteration.jsp" /> <table> <td>iteration name</td> <td><input class="input250" value="<%=horizonname%>" type="text" name="iterationname"/></td> <td>iteration start date</td> <td><input class="input250" id="iterationstartdate" value="<%=horizonstart%>" type="text" name="iterationstartdate"/><button id="iterationstartdatetrigger">...</button></td> <td>iteration end date</td> <td><input class="input250" id="iterationenddate" value="<%=horizonend%>" type="text" name="iterationenddate"/><button id="iterationenddatetrigger">...</button></td> <td>iteration leader</td> <td> <select name="iterationlead" class="input250"> <agile:listusers> <option value="<%=userid%>" <% if (userid.equals(horizonleadid)) { %>SELECTED<% %>><%=fullname%> </agile:listusers> </select> </td> <td>goals</td> <td><textarea name="goals" style="width:250px;height:100px"><%=horizongoals%></textarea></td> <td>qa Goals</td> <td><textarea name="qagoals" style="width:250px;height:100px"><%=horizonqagoals%></textarea></td> <td colspan="2" align="right"><input type="submit" class="button" name="submit" value="save" /></td> </table> </form> <script type="text/javascript"> Calendar.setup( { inputfield : "iterationstartdate", button : "iterationstartdatetrigger" ); Calendar.setup( { ); </script> inputfield : "iterationenddate", button : "iterationenddatetrigger" Aihe: Koodipolku iteraation muokkauksessa Sivu 8 of 15
</agile:gethorizondetails> <script type="text/javascript"> document.modifyiterationform.iterationname.focus(); </script> </div></div> <%@ include file="page_footer.jsp" %> <%@ include file="page_end.jsp" %> 3.3 gethorizondetails.java package fi.hut.soberit.agile.web.taglib; import java.io.*; import java.util.*; import javax.naming.*; import javax.rmi.portableremoteobject; import java.rmi.remoteexception; import javax.servlet.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import javax.servlet.http.*; import fi.hut.soberit.agile.web.*; import fi.hut.soberit.agile.logic.*; import fi.hut.soberit.agile.logic.beans.*; import fi.hut.soberit.agile.hibernate.*; import org.apache.log4j.logger; * Returns details regarding given timehorizon public class GetHorizonDetails extends TagSupport { * Standard log4j logger private Logger log = Logger.getLogger(getClass()); * The id of the horizon whose information to retrieve private Integer horizonid; * Sets the id of the horizon whose information to retrieve. Called automatically by the container (Tomcat) public void sethorizonid(integer horizonid) { this.horizonid = horizonid; * Returns the id of the timehorizon whose information to retrieve. Never called but required by the spec. public Integer gethorizonid() { return horizonid; * Retrieves the given timehorizon Aihe: Koodipolku iteraation muokkauksessa Sivu 9 of 15
public int dostarttag() throws JspException { HttpServletRequest request = (HttpServletRequest)pageContext.getRequest(); CycleManager cyclemanager = null; UserManager usermanager = null; InitialContext context = new InitialContext(); Object obj = context.lookup("cyclemanager"); CycleManagerHome cyclemanagerhome = (CycleManagerHome)PortableRemoteObject.narrow(obj, CycleManagerHome.class); cyclemanager = cyclemanagerhome.create(webhelper.getserversessionid(request)); Timehorizon horizon = cyclemanager.gettimehorizondetails(horizonid); String parenthorizonname = ""; if (horizon.getparenthorizon()!= null) { Timehorizon parenthorizon = cyclemanager.gettimehorizondetails(horizon.getparenthorizon()); parenthorizonname = parenthorizon.getname(); String horizonworktypename = ""; Integer horizonworktypeid = horizon.getworktype()!= null? horizon.getworktype() : new Integer(-1); if (horizonworktypeid.intvalue()!= -1) { HorizonWorkType worktype = cyclemanager.gethorizonworktypedetails(horizonworktypeid); horizonworktypename = worktype.getname(); cyclemanager.remove(); cyclemanager = null; obj = context.lookup("usermanager"); UserManagerHome usermanagerhome = (UserManagerHome)PortableRemoteObject.narrow(obj, UserManagerHome.class); usermanager = usermanagerhome.create(webhelper.getserversessionid(request)); User lead = null; if (horizon.gethorizonlead()!= null) lead = usermanager.getuserdetails(horizon.gethorizonlead()); usermanager.remove(); usermanager = null; pagecontext.setattribute("horizonid", horizonid); pagecontext.setattribute("horizonname", horizon.getname()); pagecontext.setattribute("horizonbacklog", horizon.getbacklog()); if (horizon.getparenthorizon()!= null) { pagecontext.setattribute("horizonparenthorizonid", horizon.getparenthorizon()); else { pagecontext.setattribute("horizonparenthorizonid", new Integer(0)); pagecontext.setattribute("horizonparenthorizonname", parenthorizonname); pagecontext.setattribute("horizontype", horizon.gettype()); pagecontext.setattribute("releaseworktypeid", horizonworktypeid); Aihe: Koodipolku iteraation muokkauksessa Sivu 10 of 15
pagecontext.setattribute("releaseworktypename", horizonworktypename); if (horizon.getduration()!= null) pagecontext.setattribute("horizonduration", horizon.getduration()); pagecontext.setattribute("horizonstart", WebHelper.dateToString(request, horizon.getstarttime())); pagecontext.setattribute("horizonend", WebHelper.dateToString(request, horizon.getendtime())); pagecontext.setattribute("horizongoals", horizon.getgoals()!= null? horizon.getgoals() : ""); pagecontext.setattribute("horizonqagoals", horizon.getqagoals()!= null? horizon.getqagoals() : ""); if (lead!= null) { pagecontext.setattribute("horizonleadid", lead.getuser_id()); pagecontext.setattribute("horizonleadname", WebHelper.formFullName(request, lead)); else { pagecontext.setattribute("horizonleadid", new Integer(0)); pagecontext.setattribute("horizonleadname", ""); return EVAL_BODY_INCLUDE; catch (InvalidSessionException e) { log.error("the session " + WebHelper.getServerSessionId(request) + " is invalid"); catch (DatastoreException e) { log.error("datastore exception occured while trying to get timehorizon details: " + e.getmessage()); catch (Exception e) { log.error("unknown exception occured while trying to get timehorizon details: " + e.getmessage()); if (cyclemanager!= null) { cyclemanager.remove(); catch (Exception e) { if (usermanager!= null) { usermanager.remove(); catch (Exception e) { return SKIP_BODY; public int doendtag() { return EVAL_PAGE; 3.4 cyclemanagerbean.java (vain metodit, jotka mainittu) * Returns the details of given timehorizon (release or iteration). * @param timehorizonid Id of the timehorizon Aihe: Koodipolku iteraation muokkauksessa Sivu 11 of 15
* @return An object representing all properties of a timehorizon, null if not found * @throws RemoteException * @throws DatastoreException public Timehorizon gettimehorizondetails(integer timehorizonid) throws RemoteException, DatastoreException { if (serversession == null) return null; Session hibernatesession = null; hibernatesession = serversession.openhibernatesession(); Timehorizon timehorizon = (Timehorizon)hibernateSession.get(Timehorizon.class, timehorizonid); hibernatesession.close(); hibernatesession = null; // what if null: timehorizon not found? return timehorizon; catch (HibernateException e) { log.error("failed to read timehorizon details: " + e.getmessage()); if (hibernatesession!= null) hibernatesession.close(); catch (Exception e1) { throw new DatastoreException(e.getMessage(), e); * Modifies the properties of the given iteration. * @param iteration the iteration to be modified public void modifyiteration(timehorizon iteration) throws RemoteException, DatastoreException { if (serversession == null) return; /* verify start date, end date and duration first? Session hibernatesession = null; hibernatesession = serversession.openhibernatesession(); hibernatesession.update(iteration); hibernatesession.flush(); hibernatesession.close(); hibernatesession = null; catch (HibernateException e) { log.error("failed to update iteration properties: " + e.getmessage()); if (hibernatesession!= null) hibernatesession.close(); catch (Exception e1) { throw new DatastoreException(e.getMessage(), e); Aihe: Koodipolku iteraation muokkauksessa Sivu 12 of 15
3.5 modifyiteration.java package fi.hut.soberit.agile.web.servlets; import javax.servlet.*; import javax.servlet.http.*; import javax.rmi.portableremoteobject; import java.rmi.remoteexception; import javax.naming.initialcontext; import java.io.*; import java.util.*; import fi.hut.soberit.agile.logic.beans.*; import fi.hut.soberit.agile.hibernate.*; import fi.hut.soberit.agile.web.*; import fi.hut.soberit.agile.util.md5; import org.apache.log4j.logger; * ModifyIteration servlet is used to modify the properties of a iteration. On error the * user is sent back to the requesting page. On success the user is sent to viewiteration.jsp page. public class ModifyIteration extends HttpServlet { * Standard log4j logger private Logger log = Logger.getLogger(getClass()); * Handler for GET HTTP requests. Just dispatches the request to handlerequest. * @param req The standard HttpServletRequest * @param resp The standard HttpServletResponse public void doget(httpservletrequest req, HttpServletResponse resp) throws IOException, ServletException { handlerequest(req, resp); * Handler for POST HTTP requests. Just dispatches the request to handlerequest. * @param req The standard HttpServletRequest * @param resp The standard HttpServletResponse public void dopost(httpservletrequest req, HttpServletResponse resp) throws IOException, ServletException { handlerequest(req, resp); * Handler for POST and GET HTTP requests. Tries to modify the properties of given iteration * @param req The standard HttpServletRequest * @param resp The standard HttpServletResponse public void handlerequest(httpservletrequest req, HttpServletResponse resp) throws IOException, ServletException { RequestReader requestreader = new RequestReader(req); if (requestreader.getstring("cancel")!= null) { Aihe: Koodipolku iteraation muokkauksessa Sivu 13 of 15
WebHelper.bounce(req, resp); return; String iterationname = requestreader.getstring("iterationname"); Integer iterationid = requestreader.getinteger("iterationid"); Date startdate = WebHelper.stringToDate(req, requestreader.getstring("iterationstartdate")); Date enddate = WebHelper.stringToDate(req, requestreader.getstring("iterationenddate")); String goals = requestreader.getstring("goals"); String qagoals = requestreader.getstring("qagoals"); Integer iterationlead = requestreader.getinteger("iterationlead"); if (iterationname == null iterationname.length() == 0) { WebHelper.bounceWithError(req, resp, "Please specify a name"); return; if (iterationid == null) { WebHelper.bounceWithError(req, resp, "No iteration id specified"); return; CycleManager cyclemanager = null; // Retrieve a home interface to the CycleManagerBean and create a new remote interface InitialContext context = new InitialContext(); Object obj = context.lookup("cyclemanager"); CycleManagerHome cyclemanagerhome = (CycleManagerHome)PortableRemoteObject.narrow(obj, CycleManagerHome.class); String serversessionid = WebHelper.getServerSessionId(req); cyclemanager = cyclemanagerhome.create(serversessionid); Timehorizon iteration = cyclemanager.gettimehorizondetails(iterationid); iteration.setname(iterationname); iteration.setstarttime(startdate); iteration.setendtime(enddate); iteration.setgoals(goals); iteration.setqagoals(qagoals); iteration.sethorizonlead(iterationlead); cyclemanager.modifyiteration(iteration); cyclemanager.remove(); cyclemanager = null; String redirecturl = "viewiteration.jsp?iterationid=" + iterationid; resp.sendredirect(resp.encodeurl(redirecturl)); catch (Exception e) { if (cyclemanager!= null) cyclemanager.remove(); catch (Exception e1) { log.error("failed to modify iteration: " + e.getmessage()); Aihe: Koodipolku iteraation muokkauksessa Sivu 14 of 15
WebHelper.bounceWithError(req, resp, "Error: could not modify iteration: " + e.getmessage()); Aihe: Koodipolku iteraation muokkauksessa Sivu 15 of 15