Changeset 5675


Ignore:
Timestamp:
Jun 28, 2011, 3:19:45 PM (10 years ago)
Author:
Nicklas Nordborg
Message:
 
Location:
trunk
Files:
4 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/build.xml

    r5635 r5675  
    13431343      <property name="base.version" value="${base.version}${base.versionsuffix}"/>
    13441344    </ant>
    1345      <copy todir="${docbook.html.out}">
    1346          <fileset dir="${docbook.src}" includes="css/*.*" />
    1347          <fileset dir="${docbook.src}" includes="script/*.*" />
     1345    <copy todir="${docbook.html.out}">
     1346      <fileset dir="${docbook.src}" includes="css/*.*" />
     1347      <fileset dir="${docbook.src}" includes="script/*.*" />
     1348      <fileset dir="${docbook.src}" includes="examples/*.*" />
    13481349     </copy>
    13491350   
  • trunk/doc/historical/development/coding/item/index.html

    r4889 r5675  
    44
    55  Copyright (C) 2005 Nicklas Nordborg
    6   Copyright (C) 2006 Jari Häkkinen, Nicklas Nordborg
     6  Copyright (C) 2006 Jari Hkkinen, Nicklas Nordborg
    77
    88  This file is part of BASE - BioArray Software Environment.
     
    2828</head>
    2929
    30 <body>
     30<body class="old">
    3131
    3232<div class="navigation">
     
    3939
    4040  <h1>Coding rules and guidelines for item classes</h1>
     41  <div class="warning">
     42    NOTE! This document is outdated and has been replaced with newer
     43    documentation. See <a href="../../../../html/developerdoc/core_ref/core_ref.rules.itemclass.html">General coding style guidelines</a>
     44  </div>
    4145
    4246  <div class="abstract">
  • trunk/doc/src/docbook/developerdoc/core_ref.xml

    r5673 r5675  
    23362336    <sect2 id="core_ref.rules.itemclass">
    23372337      <title>Item-class rules</title>
     2338     
    23382339      <para>
    2339         This documentation is only available in the old format.
    2340         See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/coding/item/index.html"
    2341           >http://base.thep.lu.se/chrome/site/doc/historical/development/coding/item/index.html</ulink>
     2340        This document contains important information about item classes for the BASE developer.
     2341        Item classes are classes that handles the business logic for the data classes in
     2342        the <code>net.sf.basedb.core.data</code> package. In general there is one item class for
     2343        each data class. When extending the database and creating new classes it is important
     2344        that it follows the design of the already existing code.
    23422345      </para>
     2346     
     2347      <sect3 id="core_ref.rules.itemclass.basic">
     2348        <title>Basic class and interface hierarchy</title>
     2349     
     2350        <para>
     2351          To simplify the development of items, we have created a set of abstract classes
     2352          and interfaces. A real class for an item must inherit from one of those classes
     2353          and may implement any of the interfaces if needed. The strucure is similar to the
     2354          structure found in the <code>net.sf.basedb.core.data</code> package
     2355          (See <xref linkend="api_overview.data_api" />).
     2356        </para>
     2357
     2358        <figure id="core_ref.figures.basic">
     2359          <title>Basic class and interface hierarchy</title>
     2360          <screenshot>
     2361            <mediaobject>
     2362              <imageobject>
     2363                <imagedata
     2364                  align="center"
     2365                  fileref="figures/uml/corelayer.basic.png" format="PNG" />
     2366              </imageobject>
     2367            </mediaobject>
     2368          </screenshot>
     2369        </figure>
     2370     
     2371      </sect3>
     2372     
     2373      <sect3 id="core_ref.rules.itemclass.permissions">
     2374        <title>Access permissions</title>
     2375        <para>
     2376          Each item class must be prepared to handle the access permissions for the
     2377          logged in user. The base classes will do most of the required work, but
     2378          not everything. There are four cases which the item class must be aware of:
     2379        </para>
     2380       
     2381        <itemizedlist>
     2382          <listitem><para>Initialise permissions in the <methodname>initPermissions()</methodname> method.</para></listitem>
     2383          <listitem><para>Check for <emphasis>write</emphasis> permission in settter methods.</para></listitem>
     2384          <listitem><para>Check for <emphasis>use</emphasis> permission when creating associations to other items.</para></listitem>
     2385          <listitem><para>Make sure the <methodname>getQuery()</methodname> method returns only items with at least read permission.</para></listitem>
     2386        </itemizedlist>
     2387       
     2388        <bridgehead id="core_ref.rules.itemclass.initpermissions">Initialise permissions</bridgehead>
     2389       
     2390        <para>
     2391          The permissions for an item are initialised by a call to the <methodname>initPermissions()</methodname>
     2392          method. This method is called as soon as the item becomes attached to a <classname
     2393          docapi="net.sf.basedb.core">DbControl</classname> object, which is responsible for managing items
     2394          in the database. The <methodname>initPermissions()</methodname> method shuld be overridden by
     2395          subclasses that needs to grant or deny permissions that is not granted or denied by default.
     2396          When overriding the <methodname>initPermissions()</methodname> method it is important to:
     2397        </para>
     2398       
     2399        <itemizedlist>
     2400          <listitem>
     2401            <para>
     2402              Combine the additional permissions with those that was passed as parameters.
     2403              Use the binary OR operator ( | ) with the result from the <code>Permission.grant()</code>
     2404              and <code>Permission.deny()</code> methods to do this.
     2405            </para>
     2406          </listitem>
     2407          <listitem>
     2408            <para>
     2409              Call <code>super.initPermissions()</code>. Otherwise, no permissions will be set all,
     2410              resulting in an <classname docapi="net.sf.basedb.core">PermissionDeniedException</classname> almost immediately.
     2411            </para>
     2412          </listitem>
     2413        </itemizedlist>
     2414       
     2415        <para>
     2416          Here is an example from the <classname docapi="net.sf.basedb.core">OwnedItem</classname> class.
     2417          If the currently logged in user is the same as the owner of the item, <constant>DELETE</constant>,
     2418          <constant>SET_OWNER</constant> and <constant>SET_PERMISSION</constant> permissions are granted.
     2419          Remember that delete permission also implies <constant>READ</constant>, <constant>USE</constant>
     2420          and <constant>WRITE</constant> permissions.
     2421        </para>
     2422       
     2423        <programlisting language="java">
     2424<![CDATA[
     2425// OwnedItem.java
     2426void initPermissions(int granted, int denied)
     2427{
     2428   UserData owner = getData().getOwner();
     2429   // owner may be null for new items
     2430   if (owner != null && owner.getId() == getSessionControl().getLoggedInUserId())
     2431   {
     2432      granted |= Permission.grant(Permission.DELETE, Permission.SET_OWNER,
     2433         Permission.SET_PERMISSION);
     2434   }
     2435   super.initPermissions(granted, denied);
     2436}       
     2437]]>
     2438</programlisting>
     2439       
     2440       
     2441        <para>
     2442          Here is another example for <classname docapi="net.sf.basedb.core">News</classname> items,
     2443          which grants read permission to anyone (even if not logged in) if today is between
     2444          the start and end date of the news entry:
     2445        </para>
     2446       
     2447        <programlisting language="java">
     2448<![CDATA[
     2449// News.java
     2450void initPermissions(int granted, int denied)
     2451   throws BaseException
     2452{
     2453   long today = new Date().getTime();
     2454   long startDate = getData().getStartDate().getTime();
     2455   long endDate = getData().getEndDate() == null ? 0 : getData().getEndDate().getTime()+24*3600*1000;
     2456   if (startDate <= today && (endDate == 0 || today <= endDate))
     2457   {
     2458      granted |= Permission.grant(Permission.READ);
     2459   }
     2460   super.initPermissions(granted, denied);
     2461}
     2462]]>
     2463</programlisting>
     2464       
     2465        <para>
     2466          A third example from the <classname docapi="net.sf.basedb.core">Help</classname> class which
     2467          is a child item to <classname docapi="net.sf.basedb.core">Client</classname>. Normally you will
     2468          get <constant>READ</constant> permission on all child items if you have <constant>READ</constant>
     2469          permission on the parent item, and <constant>CREATE</constant>, <constant>WRITE</constant> and
     2470          <constant>DELETE</constant> permissions if you have <constant>WRITE</constant> permission on the parent item.
     2471          In this case you don't have to override the <methodname>initPermissions()</methodname> method
     2472          if the child class extends the <classname docapi="net.sf.basedb.core">ChildItem</classname> class.
     2473          Instead, it should implement the <methodname>getSharedParent()</methodname>
     2474          method. The <methodname>ChildItem.initPermissions()</methodname> will take care of checking the
     2475          permissions on the parent instead of on the child. Note that this only works if the parent itself
     2476          hasn't overridden the <methodname>initPermissions()</methodname> method, since that method is never
     2477          called in this case.
     2478        </para>
     2479       
     2480        <programlisting language="java">
     2481// Help.java
     2482public class Help
     2483   extends ChildItem
     2484   implements Nameable
     2485
     2486...
     2487
     2488SharedData getSharedParent()
     2489{
     2490   return getData().getClient();
     2491}
     2492</programlisting>
     2493       
     2494        <bridgehead>Permissions granted by the base classes</bridgehead>
     2495       
     2496        <variablelist>
     2497          <varlistentry>
     2498            <term><classname docapi="net.sf.basedb.core">BasicItem</classname></term>
     2499            <listitem>
     2500              <para>
     2501              This class will grant or deny permissions as the are defined by the roles the logged
     2502              in user is a member of. If a subclass extend directly from this class, it is common
     2503              that the <methodname>initPermissions()</methodname> method needs to be overridden.
     2504              </para>
     2505            </listitem>
     2506          </varlistentry>
     2507       
     2508          <varlistentry>
     2509            <term><classname docapi="net.sf.basedb.core">ChildItem</classname></term>
     2510            <listitem>
     2511              <para>
     2512              This class grant <constant>READ</constant> permission if the logged in user has
     2513              <constant>READ</constant> permission on the parent item, and <constant>CREATE</constant>,
     2514              <constant>WRITE</constant> and <constant>DELETE</constant>
     2515              permission if the logged in user has <constant>WRITE</constant> (configurable)
     2516              permission on the parent item.
     2517              </para>
     2518            </listitem>
     2519          </varlistentry>
     2520       
     2521          <varlistentry>
     2522            <term><classname docapi="net.sf.basedb.core">OwnedItem</classname></term>
     2523            <listitem>
     2524              <para>
     2525              The owner of an item gets <constant>DELETE</constant>, <constant>SET_OWNER</constant>
     2526              and <constant>SET_PERMISSION</constant> permissions. Delete permission also implies read,
     2527              use and write permissions. Subclasses to this class usually doesn't have to overide the
     2528              <methodname>initPermissions()</methodname> method.
     2529              </para>
     2530            </listitem>
     2531          </varlistentry>
     2532       
     2533          <varlistentry>
     2534            <term><classname docapi="net.sf.basedb.core">SharedItem</classname></term>
     2535            <listitem>
     2536              <para>
     2537              The logged in user get permissions as specified in the associated <classname docapi="net.sf.basedb.core">ItemKey</classname>
     2538              and/or <classname docapi="net.sf.basedb.core">ProjectmKey</classname>. Subclasses to this class usually doesn't
     2539              have to overide the <methodname>initPermissions()</methodname> method.
     2540              </para>
     2541            </listitem>
     2542          </varlistentry>
     2543        </variablelist>
     2544       
     2545        <bridgehead>Checking for write permission in setter methods</bridgehead>
     2546       
     2547        <para>
     2548          An item class is required to check for <constant>WRITE</constant> permission in each
     2549          method that modifies the state from a public method. Example:
     2550        </para>
     2551       
     2552        <programlisting language="java">
     2553public void setName(String name)
     2554   throws PermissionDeniedException
     2555{
     2556   checkPermission(Permission.WRITE);
     2557   // ... rest of code
     2558}
     2559</programlisting>
     2560       
     2561        <warning>
     2562          <para>
     2563            If you forget this, an unauthorised user may be able to change the properties of an item.
     2564            <constant>WRITE</constant> permissions are not checked in any other central place in the
     2565            core code. Place the permission check on the first line in the method, before any data validation.
     2566            This will make it easier to spot places where the permission check is forgotten.
     2567          </para>
     2568        </warning>
     2569       
     2570        <bridgehead>Checking for use permission when creating associations</bridgehead>
     2571        <para>
     2572          An item class is required to check for <constant>USE</constant> permission on associated objects
     2573          in each method that modifies the association from a public method. Example from the
     2574          <classname docapi="net.sf.basedb.core">Protocol</classname> class:
     2575        </para>
     2576       
     2577        <programlisting language="java">
     2578public void setFile(File file)
     2579   throws PermissionDeniedException
     2580{
     2581   checkPermission(Permission.WRITE);
     2582   if (file != null) file.checkPermission(Permission.USE);
     2583   getData().setFile(file == null ? null : file.getData());
     2584}
     2585</programlisting>
     2586        <warning>
     2587          <para>
     2588            If you forget this, an unauthorised user may be able to change the association of an item.
     2589            <constant>USE</constant> permissions are not checked in any other central place in the core
     2590            code. Place the permission check as early in the method as possible after it has been validated
     2591            that the value isn't null.
     2592          </para>
     2593        </warning>
     2594       
     2595        <bridgehead id="core_ref.rules.itemclass.getquery">Making sure the getQuery() method only returns items with read permission</bridgehead>
     2596       
     2597        <para>
     2598          This method can be one of the most complex ones of the entire class. The query it generates
     2599          must always be compatible with the <methodname>initPermissions()</methodname> method. Ie.
     2600          it must not return any items for which the <methodname>initPermissions()</methodname> method doesn't
     2601          grant <constant>READ</constant> permission. And the other way around, if the <methodname>initPermissions()</methodname>
     2602          method grants <constant>READ</constant> permission to and item, the query should be able to return it.
     2603          The simplest case is if you doesn't override the <methodname>initPermissions()</methodname> method in
     2604          such a way that it affects <constant>READ</constant> permissions. In this case you can just create a
     2605          query and return it as it is. The query implementation will take care of the rest.
     2606        </para>
     2607       
     2608        <programlisting language="java">
     2609<![CDATA[
     2610// Client.java
     2611public static ItemQuery<Client> getQuery()
     2612{
     2613   return new ItemQuery<Client>(Client.class);
     2614}
     2615]]>
     2616</programlisting>
     2617
     2618        <para>
     2619          A common case is when an item is the child of another item. Usually the parent is a
     2620          <classname docapi="net.sf.basedb.core">Shareable</classname> item which means that we optimally
     2621          should check the item and project keys on the parent when returning the children. But, this is a
     2622          rather complex operation, so in this case we have choosen a different approach. The
     2623          <methodname>getQuery()</methodname> method of child items must take a parameter of the parent type.
     2624          The query can the safely return all children of that parent, since having a reference to the parent item,
     2625          means that <constant>READ</constant> permission is granted. A null value for the parent is allowed, but
     2626          then we fall back to check for role permissions only (with the help of a <classname
     2627          docapi="net.sf.basedb.core">ChildFilter</classname> object).
     2628        </para>
     2629        <programlisting language="java">
     2630<![CDATA[
     2631// Help.java
     2632private static final QueryRuntimeFilter RUNTIME_FILTER =
     2633   new QueryRuntimeFilterFactory.ChildFilter(Item.HELP, Item.CLIENT);
     2634
     2635public static ItemQuery<Help> getQuery(Client client)
     2636{
     2637   ItemQuery<Help> query = null;
     2638   if (client != null)
     2639   {
     2640      query = new ItemQuery<Help>(Help.class, null);
     2641      query.restrictPermanent(
     2642         Restrictions.eq(
     2643            Hql.property("client"),
     2644            Hql.entity(client)
     2645         )
     2646      );
     2647   }
     2648   else
     2649   {
     2650      query = new ItemQuery<Help>(Help.class, RUNTIME_FILTER);
     2651   }
     2652   return query;
     2653}
     2654]]>
     2655</programlisting>
     2656
     2657        <para>
     2658          There are many other variants of the <methodname>getQuery()</methodname> method, for example all items
     2659          having to with the authentication, <classname docapi="net.sf.basedb.core">User</classname>,
     2660          <classname docapi="net.sf.basedb.core">Group</classname>, <classname docapi="net.sf.basedb.core">Role</classname>, etc.
     2661          must check the logged in user's membership. We don't show any more examples here. Take a look in the source code
     2662          if you want more information. You can also read <xref linkend="api_overview.query_api" /> for more examples.
     2663        </para>
     2664
     2665      </sect3>
     2666     
     2667      <sect3 id="core_ref.rules.itemclass.validation">
     2668        <title>Data validation</title>
     2669       
     2670        <para>
     2671          An item class must validate all data that is passed to it as parameters. There are three types of validation:
     2672        </para>
     2673       
     2674        <orderedlist>
     2675          <listitem>
     2676            <para>
     2677            Validation of properties that are independent of other properties. For example, the length of a string or the value of number.
     2678            </para>
     2679          </listitem>
     2680          <listitem>
     2681            <para>
     2682            Validation of properties that depends on other properties on the same object. For example, we have
     2683            properties for the row and column counts, and then an array of linked objects for each position.
     2684            </para>
     2685          </listitem>
     2686          <listitem>
     2687            <para>
     2688            Validation of properties that depends on the values of other objects. For example, the login of a
     2689            user must be unique among all users.
     2690            </para>
     2691          </listitem>
     2692        </orderedlist>
     2693       
     2694        <para>
     2695          For each of these types of validation we have choosen a strategy that is as simple as possible
     2696          and doesn't force us to complex requirements on the code for objects. First, we may note that
     2697          case 1 is very common, case 2 is very uncommon, and case 3 is just a bit more common than case 2.
     2698        </para>
     2699       
     2700        <bridgehead>Case 1 validation</bridgehead>
     2701       
     2702        <para>
     2703          For case 1 we choose to make the validation in the set method for each property. Example:
     2704        </para>
     2705       
     2706        <programlisting language="java">
     2707public void setName(String name)
     2708   throws InvalidDataException
     2709{
     2710   checkPermission(Permission.WRITE);
     2711   // Null is not allowed
     2712   if (name == null) throw new InvalidUseOfNullException("name");
     2713   // The name must not be too long
     2714   if (name.length > MAX_NAME_LENGTH)
     2715   {
     2716      throw new StringTooLongException("name", name, MAX_NAME_LENGTH);
     2717   }
     2718   getData().setName(name);
     2719}
     2720// Note! In this case we should actually use NameableUtil instead
     2721</programlisting>
     2722     
     2723        <para>
     2724          This will take care of all case 1 validation except that we cannot check properties that
     2725          doesn't allow null values if the method never is called. To solve this problem we have
     2726          two strategies:
     2727        </para>
     2728       
     2729        <itemizedlist>
     2730          <listitem>
     2731            <para>
     2732            Provide a default value that is set in the constructor. For example the name
     2733            of a new user can be initilised to "New user".
     2734            </para>
     2735          </listitem>
     2736          <listitem>
     2737            <para>
     2738              Use constructor methods with parameters for required objects.
     2739            </para>
     2740          </listitem>
     2741        </itemizedlist>
     2742       
     2743        <para>
     2744          Which strategy to use is decided from case to case. Failure to validate a property will usually
     2745          result in a database exception, so no real harm is done, except that we don't want to show the
     2746          ugly error messages to our users. The <classname docapi="net.sf.basedb.core">News</classname>
     2747          class uses a mix of the two strategies:
     2748        </para>
     2749       
     2750        <programlisting language="java">
     2751// News.java
     2752public static News getNew(DbControl dc, Date startDate, Date newsDate)
     2753{
     2754   News n = dc.newItem(News.class);
     2755   n.setName("New news");
     2756   n.setStartDate(startDate);
     2757   n.setNewsDate(newsDate);
     2758   n.getData().setEntryDate(new Date());
     2759   return n;
     2760}
     2761...
     2762public void setStartDate(Date startDate)
     2763   throws PermissionDeniedException, InvalidDataException
     2764{
     2765   checkPermission(Permission.WRITE);
     2766   getData().setStartDate(DateUtil.setNotNullDate(startDate, "startDate"));
     2767}
     2768...
     2769</programlisting>
     2770     
     2771        <bridgehead>Case 2 validation</bridgehead>
     2772       
     2773        <para>
     2774          This case requires interception of saves and updates and a call to the <methodname>validate()</methodname>
     2775          method on the item. This automatically done on items which implements the <interfacename
     2776          docapi="net.sf.basedb.core">Validatable</interfacename> interface. Internally this functionality is
     2777          implemented by the <classname docapi="net.sf.basedb.core">DbControl</classname> class, which keeps a
     2778          "commit queue" that holds all loaded items that implements the <interfacename
     2779          docapi="net.sf.basedb.core">Validatable</interfacename> interface. When <methodname>DbControl.commit()</methodname>
     2780          is called, the queue is iterated and the <methodname>validate()</methodname> method is called for each item.
     2781          Here is another example from the <classname docapi="net.sf.basedb.core">News</classname> class which must
     2782          validate that the three dates (startDate, newsData and endData) are in proper order:
     2783        </para>
     2784        <programlisting language="java">
     2785<![CDATA[
     2786// News.java
     2787void validate()
     2788   throws InvalidDataException, BaseException
     2789{
     2790   super.validate();
     2791   Date startDate = getData().getStartDate();
     2792   Date newsDate = getData().getNewsDate();
     2793   Date endDate = getData().getEndDate();
     2794   if (startDate.after(newsDate))
     2795   {
     2796      throw new InvalidDataException("Invalid date. startDate is after newsDate.");
     2797   }
     2798   if (endDate != null && newsDate.after(endDate))
     2799   {
     2800      throw new InvalidDataException("Invalid date. newsDate is after endDate.");
     2801   }
     2802}
     2803]]>
     2804</programlisting>
     2805       
     2806        <bridgehead>Case 3 validation</bridgehead>
     2807        <para>
     2808          Usually, we do not bother with checking for this case, but delegates to the database
     2809          to do the check. The reason that we do not bother to check for this case is that we can't
     2810          be sure to succeed even if we first check the database. It is possible that during the time
     2811          between our check and the actual insert or update, another transaction has already inserted
     2812          another object into the database that violates the check. This is not perfect and the error
     2813          messages are a bit ugly, but under the circumstances it is the best we can do.
     2814        </para>
     2815      </sect3>
     2816     
     2817      <sect3 id="core_ref.rules.itemclass.transactions">
     2818        <title>Participating in transactions</title>
     2819       
     2820        <para>
     2821          Sometimes it is neccessary for an item to intercept certain events. For example,
     2822          the <classname docapi="net.sf.basedb.core">File</classname> object needs to know
     2823          if a transaction has been completed or rollbacked so it can clean up temporary
     2824          files that have been used. We have created the <interfacename
     2825          docapi="net.sf.basedb.core">Transactional</interfacename> interface, which is a tagging
     2826          interface that tells the core to call certain methods on the item at certain events.
     2827          The interface doesn't contain any methods, the item class needs to override methods
     2828          from the <classname docapi="net.sf.basedb.core">BasicItem</classname> class. The following
     2829          events/methods have been defined:
     2830        </para>
     2831       
     2832        <note>
     2833          <para>
     2834          The methods are always called for new items and items that are about to be
     2835          deleted. It is only neccessary for an item to implement the <interfacename
     2836          docapi="net.sf.basedb.core">Transactional</interfacename> interface if it
     2837          needs to act on <constant>UPDATE</constant> events.
     2838          </para>
     2839        </note>
     2840       
     2841        <variablelist>
     2842          <varlistentry>
     2843            <term>onBeforeCommit(Action)</term>
     2844            <listitem>
     2845              <para>
     2846                This method is called before a commit is issued to Hibernate. It should be used by an
     2847                item when it needs to update dependent objects before anything is written to the database.
     2848                Note that nothing has been sent to the database yet and new items has not got an id when
     2849                this method is called. If you override this method you must call <methodname>super.onBeforeCommit()</methodname>
     2850                to allow the superclass to do whatever it needs to do. Here is an example from the
     2851                <classname docapi="net.sf.basedb.core">OwnedItem</classname> class which sets the owner to the
     2852                currently logged in user, if no owner has been explicitely specified:
     2853              </para>
     2854             
     2855              <programlisting language="java">
     2856<![CDATA[
     2857void onBeforeCommit(Transactional.Action action)
     2858   throws NotLoggedInException, BaseException
     2859{
     2860   super.onBeforeCommit(action);
     2861   if (action == Transactional.Action.CREATE && getData().getOwner() == null)
     2862   {
     2863      org.hibernate.Session session = getDbControl().getHibernateSession();
     2864      int loggedInuserId = getSessionControl().getLoggedInUserId();
     2865      UserData owner =
     2866         HibernateUtil.loadData(session, UserData.class, loggedInuserId);
     2867      if (owner == null) throw new NotLoggedInException();
     2868      getData().setOwner(owner);
     2869   }
     2870}
     2871]]>
     2872</programlisting>
     2873            </listitem>
     2874          </varlistentry>
     2875         
     2876          <varlistentry>
     2877            <term>setProjectDefaults(Project)</term>
     2878            <listitem>
     2879              <para>
     2880                This method is called before inserting new items into the database
     2881                to allow items to propagate default values from the active project.
     2882                The method is only called when a project is active. Subclasses should
     2883                always call <methodname>super.setProjectDefaults()</methodname>
     2884                and should only set default values that hasn't been explicitely set
     2885                by client code (including <code>setFoo(null)</code> calls).
     2886              </para>
     2887             
     2888              <note>
     2889                <para>
     2890                With few exceptions a project can only hold <classname docapi="net.sf.basedb.core">ItemSubtype</classname>
     2891                items as default values. This means that the item that is going to use the default
     2892                value should implement the <interfacename docapi="net.sf.basedb.core">Subtypeable</interfacename>
     2893                interface and list the other related item types in the <code>@SubtypableRelatedItems</code>
     2894                annotation.
     2895                </para>
     2896              </note>
     2897             
     2898              <programlisting language="java">
     2899<![CDATA[
     2900// DerivedBioAssaySet.java
     2901@Override
     2902@SubtypableRelatedItems({Item.PHYSICALBIOASSAY, Item.DERIVEDBIOASSAYSET, Item.SOFTWARE, Item.HARDWARE, Item.PROTOCOL})
     2903public ItemSubtype getItemSubtype()
     2904{
     2905  return getDbControl().getItem(ItemSubtype.class, getData().getItemSubtype());
     2906}
     2907
     2908/**
     2909  Set protocol, hardware and software from project default settings.
     2910*/
     2911@Override
     2912void setProjectDefaults(Project activeProject)
     2913  throws BaseException
     2914{
     2915  super.setProjectDefaults(activeProject);
     2916  if (!hasPermission(Permission.WRITE)) return;
     2917   
     2918  DbControl dc = getDbControl();
     2919  if (!protocolHasBeenSet)
     2920  {
     2921    ProtocolData protocol =
     2922      (ProtocolData)activeProject.findDefaultRelatedData(dc, this, Item.PROTOCOL, false);
     2923    if (protocol != null)
     2924    {
     2925      getData().setProtocol(protocol);
     2926      protocolHasBeenSet = true;
     2927    }
     2928  }
     2929  if (!hardwareHasBeenSet)
     2930  {
     2931    HardwareData hardware =
     2932      (HardwareData)activeProject.findDefaultRelatedData(dc, this, Item.HARDWARE, false);
     2933    if (hardware != null)
     2934    {
     2935      getData().setHardware(hardware);
     2936      hardwareHasBeenSet = true;
     2937    }
     2938  }
     2939  if (!softwareHasBeenSet)
     2940  {
     2941    SoftwareData software =
     2942      (SoftwareData)activeProject.findDefaultRelatedData(dc, this, Item.SOFTWARE, false);
     2943    if (software != null)
     2944    {
     2945      getData().setSoftware(software);
     2946      softwareHasBeenSet = true;
     2947    }
     2948  }
     2949}
     2950]]>
     2951</programlisting>
     2952            </listitem>
     2953          </varlistentry>
     2954
     2955          <varlistentry>
     2956            <term>onAfterInsert()</term>
     2957            <listitem>
     2958              <para>
     2959                This method is called on all items directly after Hibernate has inserted
     2960                it into the database. This method can be used in place of the
     2961                <methodname>onBeforeCommit()</methodname> in case the id is needed.
     2962              </para>
     2963            </listitem>
     2964          </varlistentry>
     2965
     2966          <varlistentry>
     2967            <term>onAfterCommit(Action)</term>
     2968            <listitem>
     2969              <para>
     2970                This method is called after a successful commit has been issued to Hibernate. It should be
     2971                used by an item which needs to do additional processing. For example the
     2972                <classname docapi="net.sf.basedb.core">File</classname> object may need to cleanup temporary files.
     2973                This method should not use the database and it must not fail, since it is impossible to rollback
     2974                anything that has already been committed to the database. If the method fails, it should log
     2975                an exception with the <methodname>Application.log()</methodname> method.
     2976              </para>
     2977            </listitem>
     2978          </varlistentry>
     2979
     2980          <varlistentry>
     2981            <term>onRollback(Action)</term>
     2982            <listitem>
     2983              <para>
     2984                This method is called after an unsuccessful commit has been issued to Hibernate. The same rules as
     2985                for the <methodname>onAfterCommit()</methodname> method applies to this method.
     2986              </para>
     2987            </listitem>
     2988          </varlistentry>
     2989        </variablelist>
     2990       
     2991        <para>
     2992          Internally this functionality is implemented by the <classname docapi="net.sf.basedb.core">DbControl</classname>
     2993          class, which keeps a "commit queue" that holds all new objects, all objects that are about to be deleted
     2994          and all objects that implements the <interfacename docapi="net.sf.basedb.core">Transactional</interfacename>
     2995          interface. When <methodname>DbControl.commit()</methodname> is called, the queue is iterated and
     2996          <methodname>onBeforeCommit()</methodname> is called for each item, and then
     2997          either <methodname>onAfterCommit()</methodname> or <methodname>onRollback()</methodname>.
     2998          The <classname docapi="net.sf.basedb.core.Transactional">Action</classname> parameter is of an
     2999          enumeration type which can hae three different values:
     3000        </para>
     3001
     3002
     3003        <itemizedlist>
     3004          <listitem>
     3005            <para>
     3006            <constant>CREATE</constant>: This is a new item which is saved to the database for the first time.
     3007            </para>
     3008          </listitem>
     3009          <listitem>
     3010            <para>
     3011            <constant>UPDATE</constant>: This is an existing item, which has been modified.
     3012            </para>
     3013          </listitem>
     3014          <listitem>
     3015            <para>
     3016            <constant>DELETE</constant>: This is an existing item, which is now being deleted from the database
     3017            </para>
     3018          </listitem>
     3019        </itemizedlist>
     3020
     3021      </sect3>
     3022     
     3023      <sect3 id="core_ref.rules.itemclass.template">
     3024        <title>Template code for item classes</title>
     3025       
     3026        <para>
     3027          The <ulink url="../../examples/AnyItem.java.txt">AnyItem.java</ulink> and <ulink
     3028          url="../../examples/AChildItem.java.txt">AChildItem.java</ulink> files
     3029          contains two complete item classes with lots of template methods. Please copy and paste as much as you want
     3030          from these, but do not forget to change the specific details.
     3031        </para>
     3032       
     3033        <bridgehead>Class declaration</bridgehead>
     3034       
     3035        <para>
     3036          An item class should extend one of the four classes: <classname docapi="net.sf.basedb.core">BasicItem</classname>,
     3037          <classname docapi="net.sf.basedb.core">OwnedItem</classname>, <classname docapi="net.sf.basedb.core">SharedItem</classname>
     3038          and <classname docapi="net.sf.basedb.core">CommonItem</classname>. Which one depends on what combination of
     3039          interfaces are needed for that item. The most common situation is probably to extend the <classname
     3040          docapi="net.sf.basedb.core">CommonItem</classname> class. Do not forget to include the GNU licence and
     3041          copyright statement. Also note that the corresponding data layer class is specified as a
     3042          generics parameter of the superclass.
     3043        </para>
     3044       
     3045        <programlisting language="java">
     3046<![CDATA[
     3047/*
     3048  $Id $
     3049
     3050  Copyright (C) 2011 Your name
     3051
     3052  This file is part of BASE - BioArray Software Environment.
     3053  Available at http://base.thep.lu.se/
     3054
     3055  BASE is free software; you can redistribute it and/or
     3056  modify it under the terms of the GNU General Public License
     3057  as published by the Free Software Foundation; either version 3
     3058  of the License, or (at your option) any later version.
     3059
     3060  BASE is distributed in the hope that it will be useful,
     3061  but WITHOUT ANY WARRANTY; without even the implied warranty of
     3062  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     3063  GNU General Public License for more details.
     3064
     3065  You should have received a copy of the GNU General Public License
     3066  along with BASE. If not, see <http://www.gnu.org/licenses/>.
     3067*/
     3068package net.sf.basedb.core;
     3069import net.sf.basedb.core.data.AnyData;
     3070/**
     3071  This class is used to represent an AnyItem in BASE.
     3072
     3073  @author Your name
     3074  @since 3.0
     3075  @see AnyData
     3076  @base.modified $Date$
     3077*/
     3078public class AnyItem
     3079  extends CommonItem<AnyData>
     3080{
     3081  ...
     3082}
     3083]]>
     3084</programlisting>
     3085
     3086        <bridgehead>Static methods and fields</bridgehead>
     3087       
     3088        <variablelist>
     3089          <varlistentry>
     3090            <term>getNew(DbControl)</term>
     3091            <listitem>
     3092              <para>
     3093              This method is used to create a new item. The new item must be created by calling the
     3094              <methodname>DbControl.newItem()</methodname>.
     3095              </para>
     3096             
     3097              <programlisting language="java">
     3098<![CDATA[
     3099/**
     3100  Create a new <code>AnyItem</code> item.
     3101 
     3102  @param dc The <code>DbControl</code> which will be used for
     3103    permission checking and database access
     3104  @return The new <code>AnyItem</code> item
     3105  @throws BaseException If there is an error
     3106*/
     3107public static AnyItem getNew(DbControl dc)
     3108  throws BaseException
     3109{
     3110  AnyItem a = dc.newItem(AnyItem.class);
     3111  a.setName("New any item");
     3112  return a;
     3113}
     3114]]>
     3115</programlisting>
     3116              <para>
     3117                The method must initialise all not-null properties to a sensible default values or it may take values as parameters:
     3118              </para>
     3119              <programlisting language="java">
     3120<![CDATA[
     3121// User.java
     3122public static User getNew(DbControl dc, String login, String password)
     3123   throws BaseException
     3124{
     3125   User u = dc.newItem(User.class);
     3126   u.setName("New user");
     3127   u.setLogin(login);
     3128   u.setPassword(password);
     3129   int defaultQuotaId = SystemItems.getId(Quota.DEFAULT);
     3130   org.hibernate.Session session = dc.getHibernateSession();
     3131   QuotaData defaultQuota =
     3132      HibernateUtil.loadData(session, QuotaData.class, defaultQuotaId);
     3133   u.getData().setQuota(defaultQuota);
     3134   return u;
     3135}
     3136]]>
     3137</programlisting>
     3138              <para>
     3139                When the default value is an association to another item, use the data object
     3140                (<classname docapi="net.sf.basedb.core.data">QuotaData</classname>) not the item object
     3141                (<classname docapi="net.sf.basedb.core">Quota</classname>) to create the association.
     3142                The reason for this is that the logged in user may not have read permission to the default
     3143                object. Ie. The logged in user may have permission to create users, but not permission to
     3144                read quota.
     3145              </para>
     3146
     3147            </listitem>
     3148          </varlistentry>
     3149       
     3150          <varlistentry>
     3151            <term>getById(DbControl, int)</term>
     3152            <listitem>
     3153              <para>
     3154                This method is used to load an item from the database when the id of that item is known.
     3155                Use the <methodname>DbControl.loadItem()</methodname> method to load the item. If the item
     3156                is not found an <classname docapi="net.sf.basedb.core">ItemNotFoundException</classname> should
     3157                be thrown.
     3158              </para>
     3159             
     3160              <programlisting language="java">
     3161<![CDATA[
     3162/**
     3163  Get an <code>AnyItem</code> item when you know the id.
     3164 
     3165  @param dc The <code>DbControl</code> which will be used for
     3166    permission checking and database access.
     3167  @param id The id of the item to load
     3168  @return The <code>AnyItem</code> item
     3169  @throws ItemNotFoundException If an item with the specified
     3170    id is not found
     3171  @throws PermissionDeniedException If the logged in user doesn't
     3172    have read permission to the item
     3173  @throws BaseException If there is another error
     3174*/
     3175public static AnyItem getById(DbControl dc, int id)
     3176  throws ItemNotFoundException, PermissionDeniedException, BaseException
     3177{
     3178  AnyItem a = dc.loadItem(AnyItem.class, id);
     3179  if (a == null) throw new ItemNotFoundException("AnyItem[id="+id+"]");
     3180  return a;
     3181}
     3182]]>
     3183</programlisting>
     3184             
     3185            </listitem>
     3186          </varlistentry>
     3187       
     3188          <varlistentry>
     3189            <term>getQuery()</term>
     3190            <listitem>
     3191              <para>
     3192              See <xref linkend="core_ref.rules.itemclass.getquery" />.
     3193              </para>
     3194            </listitem>
     3195          </varlistentry>
     3196        </variablelist>
     3197       
     3198        <bridgehead>Constructors</bridgehead>
     3199       
     3200        <para>
     3201          Each item class needs only one constructor, which takes an object of the corresponding
     3202          data class as a parameter. The constructor should never be invoked directly. Use the
     3203          <methodname>DbControl.newItem()</methodname> method.
     3204        </para>
     3205
     3206        <programlisting language="java">
     3207<![CDATA[
     3208AnyItem(AnyData anyData)
     3209{
     3210  super(anyData);
     3211}
     3212]]>
     3213</programlisting>
     3214
     3215        <bridgehead>Core methods</bridgehead>
     3216       
     3217        <variablelist>
     3218          <varlistentry>
     3219            <term>isUsed()</term>
     3220            <listitem>
     3221              <para>
     3222                This method is defined by the <classname docapi="net.sf.basedb.core">BasicItem</classname> class and
     3223                is called whenever we need to know if there are other items referencing the current item. The main
     3224                use case is to let client applications know if it is safe to delete an object or not.
     3225                The default implementation checks <classname docapi="net.sf.basedb.core">AnyToAny</classname>
     3226                links between items. A subclass must override this method if it can be referenced by other items.
     3227                A subclass should always call <methodname>super.isUsed()</methodname> as a last check if it is not
     3228                used by any other item. The method should check if it is beeing used (referenced by) some other item.
     3229                For example, a <classname docapi="net.sf.basedb.core">Tag</classname> is used if there is an
     3230                <classname docapi="net.sf.basedb.core">Extract</classname> with that tag. The simplest way to check
     3231                if the item is used is to use a predefined query that counts the number of references.
     3232              </para>
     3233             
     3234              <programlisting language="java">
     3235<![CDATA[
     3236/**
     3237  Check if:
     3238  <ul>
     3239  <li>Some {@link Extract}:s are marked with this tag
     3240  </ul>
     3241*/
     3242public boolean isUsed()
     3243  throws BaseException
     3244{
     3245  org.hibernate.Session session = getDbControl().getHibernateSession();
     3246  org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session,
     3247    "GET_EXTRACTS_FOR_TAG", "count(*)");
     3248  /*
     3249    SELECT {1}
     3250    FROM ExtractData ext
     3251    WHERE ext.tag = :tag
     3252  */
     3253  query.setEntity("tag", this.getData());
     3254  boolean used = HibernateUtil.loadData(Long.class, query) > 0;
     3255  return used || super.isUsed();
     3256}
     3257]]>
     3258</programlisting>
     3259             
     3260              <para>
     3261                Sometimes it may be harder to decide what counts as using an item or not. Some examples:
     3262              </para>
     3263             
     3264              <itemizedlist>
     3265                <listitem>
     3266                  <para>
     3267                    An event for a sample does not count as using the sample, since they hava a
     3268                    parent-child relationship. Ie. deleting the sample will also delete all events
     3269                    associated with it. On the other hand, the protocol registered for the event counts
     3270                    as using the protocol, because deleting the protocol should not delete all events.
     3271                  </para>
     3272                </listitem>
     3273                <listitem>
     3274                  <para>
     3275                    As a general rule, if one item is used by a second item, then the second item cannot
     3276                    be used by the first. It could lead to situations where it would be impossible to
     3277                    delete either one of them.
     3278                  </para>
     3279                </listitem>
     3280              </itemizedlist>
     3281            </listitem>
     3282          </varlistentry>
     3283
     3284          <varlistentry>
     3285            <term>getUsingItems()</term>
     3286            <listitem>
     3287              <para>
     3288                Find all items that are referencing this one. This method is related to the
     3289                <methodname>isUsed()</methodname> method and is defined in the <classname docapi="net.sf.basedb.core">BasicItem</classname>
     3290                class. The default implementation load all items linked via an <classname docapi="net.sf.basedb.core">AnyToAny</classname>
     3291                link that has the <code>usingTo</code> flag set to true. A subclass must override this method if it can
     3292                be referenced to be used by other items. A subclass should always call <methodname>super.getUsingItems()</methodname>
     3293                first and then add extra items to the Set returned by that call. For example, a <classname docapi="net.sf.basedb.core">Tag</classname>
     3294                should load all <classname docapi="net.sf.basedb.core">Extract</classname>:s with that tag.
     3295              </para>
     3296              <programlisting language="java">
     3297<![CDATA[
     3298/**
     3299  Get all:
     3300  <ul>
     3301  <li>{@link Extract}:s marked with this tag
     3302  <ul>
     3303*/
     3304@Override
     3305public Set<ItemProxy> getUsingItems()
     3306{
     3307  Set<ItemProxy> using = super.getUsingItems();
     3308  org.hibernate.Session session = getDbControl().getHibernateSession();
     3309   
     3310  // Extracts
     3311  org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session,
     3312      "GET_EXTRACTS_FOR_TAG", "ext.id");
     3313  /*
     3314    SELECT {1}
     3315    FROM ExtractData ext
     3316    WHERE ext.tag = :tag
     3317  */
     3318  query.setEntity("tag", this.getData());
     3319  addUsingItems(using, Item.EXTRACT, query);
     3320  return using;
     3321}
     3322]]>
     3323</programlisting>
     3324            </listitem>
     3325          </varlistentry>
     3326         
     3327          <varlistentry>
     3328            <term>initPermissions(int, int)</term>
     3329            <listitem>
     3330              <para>
     3331                See <xref linkend="core_ref.rules.itemclass.initpermissions" />.
     3332              </para>
     3333            </listitem>
     3334          </varlistentry>
     3335       
     3336          <varlistentry>
     3337            <term>validate() </term>
     3338            <listitem>
     3339              <para>
     3340                See <xref linkend="core_ref.rules.itemclass.validation" />.
     3341              </para>
     3342            </listitem>
     3343          </varlistentry>
     3344       
     3345          <varlistentry>
     3346            <term>onBeforeCommit(Action)</term>
     3347            <term>setProjectDefaults(Project)</term>
     3348            <term>onAfterInsert()</term>
     3349            <term>onAfterCommit(Action) </term>
     3350            <term>onRollback(Action)</term>
     3351            <listitem>
     3352              <para>
     3353                See <xref linkend="core_ref.rules.itemclass.transactions"/>.
     3354              </para>
     3355            </listitem>
     3356          </varlistentry>
     3357        </variablelist>
     3358       
     3359        <bridgehead>Getter and setter methods</bridgehead>
     3360       
     3361        <para>
     3362          The get methods for basic property types are usually very simple. All that is needed is to
     3363          return the value. Be aware of date values though, they are mutable and must be copied.
     3364        </para>
     3365         
     3366        <programlisting language="java">
     3367<![CDATA[
     3368/**
     3369   Get the value of the string property.
     3370*/
     3371public String getStringProperty()
     3372{
     3373   return getData().getStringProperty();
     3374}
     3375
     3376/**
     3377   Get the value of the int property.
     3378*/
     3379public int getIntProperty()
     3380{
     3381   return getData().getIntProperty();
     3382}
     3383
     3384/**
     3385   Get the value of the boolean property.
     3386*/
     3387public boolean isBooleanProperty()
     3388{
     3389   return getData().isBooleanProperty();
     3390}
     3391
     3392/**
     3393   Get the value of the date property.
     3394   @return A date object or null if unknown
     3395*/
     3396public Date getDateProperty()
     3397{
     3398   return DateUtil.copy(getData().getDateProperty());
     3399}
     3400]]>
     3401</programlisting>
     3402
     3403          <para>
     3404            The set methods must always check for <constant>WRITE</constant> permission and validate
     3405            the parameters. There are plenty of utility method to help with this.
     3406          </para>
     3407         
     3408          <programlisting language="java">
     3409<![CDATA[
     3410/**
     3411   The maximum length of the string property. Check the length
     3412   agains this value before calling {@link #setStringProperty(String)}
     3413   to avoid exceptions.
     3414*/
     3415public static final int MAX_STRINGPROPERTY_LENGTH =
     3416   AnyData.MAX_STRINGPROPERTY_LENGTH;
     3417
     3418/**
     3419   Set the value of the string property. Null values are not
     3420   allowed and the length must be shorter than
     3421   {@link #MAX_STRINGPROPERTY_LENGTH}.
     3422   @param value The new value
     3423   @throws PermissionDeniedException If the logged in user
     3424      doesn't have write permission
     3425   @throws InvalidDataException If the value is null or too long
     3426*/
     3427public void setStringProperty(String value)
     3428   throws PermissionDeniedException, InvalidDataException
     3429{
     3430   checkPermission(Permission.WRITE);
     3431   getData.setStringProperty(
     3432      StringUtil.setNotNullString(value, "stringProperty", MAX_STRINGPROPERTY_LENGTH)
     3433   );
     3434}
     3435
     3436/**
     3437   Set the value of the int property. The value mustn't be less than
     3438   zero.
     3439   @param value The new value
     3440   @throws PermissionDeniedException If the logged in user
     3441      doesn't have write permission
     3442   @throws InvalidDataException If the value is less than zero
     3443*/
     3444
     3445public void setIntProperty(int value)
     3446   throws PermissionDeniedException, InvalidDataException
     3447{
     3448   checkPermission(Permission.WRITE);
     3449   getData.setIntProperty(
     3450      IntegerUtil.checkMin(value, "intProperty", 0)
     3451   );
     3452}
     3453
     3454/**
     3455   Set the value of the boolean property.
     3456   @param value The new value
     3457   @throws PermissionDeniedException If the logged in user
     3458      doesn't have write permission
     3459*/
     3460public void setBooleanProperty(boolean value)
     3461   throws PermissionDeniedException
     3462{
     3463   checkPermission(Permission.WRITE);
     3464   getData.setBooleanProperty(value);
     3465}
     3466
     3467/**
     3468   Set the value of the date property. Null values are allowed.
     3469   @param value The new value
     3470   @throws PermissionDeniedException If the logged in user
     3471      doesn't have write permission
     3472*/
     3473public void setDateProperty(Date value)
     3474   throws PermissionDeniedException
     3475{
     3476   checkPermission(Permission.WRITE);
     3477   getData().setDateProperty(DateUtil.setNullableDate(value, "dateProperty"));
     3478}
     3479]]>
     3480</programlisting>
     3481
     3482        <bridgehead>Many-to-one associations</bridgehead>
     3483       
     3484        <para>
     3485          Many-to-one associations require sligthly more work. First of all, the item must be connected to a
     3486          <classname docapi="net.sf.basedb.core">DbControl</classname> since it is used to load the information
     3487          from the database and crete the new item object. Secondly, we must make sure to check for use
     3488          permission on the referenced object in the setter method.
     3489        </para>
     3490       
     3491        <programlisting language="java">
     3492<![CDATA[
     3493/**
     3494   Get the associated other item.
     3495   @return The OtherItem item
     3496   @throws PermissionDeniedException If the logged in user
     3497      doesn't have read permission
     3498   @throws BaseException If there is another error
     3499*/
     3500public OtherItem getOtherItem()
     3501   throws PermissionDeniedException, BaseException
     3502{
     3503   return getDbControl().getItem(OtherItem.class, getData().getOtherItem());
     3504}
     3505
     3506/**
     3507   Set the associated item. Null is not allowed.
     3508   @param other The other item
     3509   @throws PermissionDeniedException If the logged in user
     3510      doesn't have write permission
     3511   @throws InvalidDataException If the other item is null
     3512   @throws BaseException If there is another error
     3513*/
     3514public void setOtherItem(OtherItem other)
     3515   throws PermissionDeniedException, InvalidDataException, BaseException
     3516{
     3517   checkPermission(Permission.WRITE);
     3518   if (otherItem == null) throw new InvalidUseOfNullException("otherItem");
     3519   getData().setOtherItem(otherItem.getData());
     3520}
     3521]]>
     3522</programlisting>
     3523
     3524        <bridgehead>One-to-many and many-to-many associations</bridgehead>
     3525        <para>
     3526          If the association is a one-to-many or many-to-many it becomes a little more complicated again.
     3527          There are many types of such associations and how they are handled usually depends on if the
     3528          are set:s, map:s, list:s or any other type of collections. In all cases we need methods for adding
     3529          and removing items, and a method that returns a <interfacename docapi="net.sf.basedb.core.query">Query</interfacename>
     3530          that can list all associated items. The first example if for parent/child relationship, which is a one-to-many
     3531          association where the children are mapped as a set.
     3532        </para>
     3533       
     3534        <programlisting language="java">
     3535<![CDATA[
     3536/**
     3537   Create a child item for this any item.
     3538   @return The new AChildItem object
     3539   @throws PermissionDeniedException If the logged in user doesn't have
     3540      write permission
     3541   @throws BaseException If there is another error
     3542*/
     3543public AChildItem newChildItem()
     3544   throws PermissionDeniedException, BaseException
     3545{
     3546   checkPermission(Permission.WRITE);
     3547   return AChildItem.getNew(getDbControl(), this);
     3548}
     3549
     3550/**
     3551   Get a query that will return all child items for this any item.
     3552   @return A {@link Query} object
     3553*/
     3554public ItemQuery<AChildItem> getChildItems()
     3555{
     3556  return AChildItem.getQuery(this);
     3557}
     3558]]>
     3559</programlisting>
     3560        <para>
     3561          The second example is for the many-to-many associations between users and roles, which is also mapped as a set.
     3562        </para>
     3563        <programlisting language="java">
     3564<![CDATA[
     3565// Role.java
     3566/**
     3567   Add a user to this role.
     3568   @param user The user to add
     3569   @throws PermissionDeniedException If the logged in user doesn't
     3570      have write permission for the role and
     3571      use permission for the user
     3572   @throws InvalidDataException If the user is null
     3573*/
     3574public void addUser(User user)
     3575   throws PermissionDeniedException, InvalidDataException
     3576{
     3577   checkPermission(Permission.WRITE);
     3578   if (user == null) throw new InvalidUseOfNullException("user");
     3579   user.checkPermission(Permission.USE);
     3580   getData().getUsers().add(user.getData());
     3581}
     3582
     3583/**
     3584   Remove a user from this role.
     3585   @param user The user to remove
     3586   @throws PermissionDeniedException If the logged in user doesn't
     3587      have write permission for the role and
     3588      use permission for the user
     3589   @throws InvalidDataException If the user is null
     3590*/
     3591public void removeUser(User user)
     3592   throws PermissionDeniedException, InvalidDataException
     3593{
     3594   checkPermission(Permission.WRITE);
     3595   if (user == null) throw new InvalidUseOfNullException("user");
     3596   user.checkPermission(Permission.USE);
     3597   getData().getUsers().remove(user.getData());
     3598}
     3599
     3600/**
     3601   Check if the given user is member of this role or not.
     3602   @param user The user to check
     3603   @return TRUE if the user is member, FALSE otherwise
     3604*/
     3605public boolean isMember(User user)
     3606{
     3607   return getData().getUsers().contains(user.getData());
     3608}
     3609
     3610/**
     3611   Get a query that returns the users that
     3612   are members of this role. This query excludes users that the logged
     3613   in user doesn't have permission to read.
     3614   @see User#getQuery()
     3615*/
     3616public ItemQuery<User> getUsers()
     3617{
     3618   ItemQuery<User> query = User.getQuery();
     3619   query.joinPermanent(
     3620      Hql.innerJoin("roles", Item.ROLE.getAlias())
     3621   );
     3622   query.restrictPermanent(
     3623      Restrictions.eq(
     3624         Hql.alias(Item.ROLE.getAlias()),
     3625         Hql.entity(this)
     3626      )
     3627   );
     3628   return query;
     3629}
     3630
     3631// User.java
     3632/**
     3633   Get a query that returns the roles where this user is a
     3634   member. The query excludes roles that the logged in user doesn't have
     3635   permission to read.
     3636   @see Role#getQuery()
     3637*/
     3638public ItemQuery<Role> getRoles()
     3639{
     3640   ItemQuery<Role> query = Role.getQuery();
     3641   query.joinPermanent(
     3642      Hql.innerJoin("users", Item.USER.getAlias())
     3643   );
     3644   query.restrictPermanent(
     3645      Restrictions.eq(
     3646         Hql.alias(Item.USER.getAlias()),
     3647         Hql.entity(this)
     3648      )
     3649   );
     3650   return query;
     3651}
     3652]]>
     3653</programlisting>
     3654        <para>
     3655          Note that we have a query method in both classes, but the association can only be changed from the
     3656          <classname docapi="net.sf.based.core">Role</classname>. We recommend that modifier methods are put
     3657          in one of the classes only. The last example is the many-to-many relation between projects and users
     3658          which is a map to the permission for the user in the project.
     3659        </para>
     3660       
     3661        <programlisting language="java">
     3662<![CDATA[
     3663// Project.java
     3664/**
     3665   Grant a user permissions to this project. Use an empty set
     3666   or null to remove the user from this project.
     3667
     3668   @param user The user
     3669   @param permissions The permissions to grant, or null to revoke all permissions
     3670   @throws PermissionDeniedException If the logged in user doesn't have
     3671      write permission for the project
     3672   @throws InvalidDataException If the user is null
     3673   @see Permission
     3674*/
     3675public void setPermissions(User user, Set<Permission> permissions)
     3676   throws PermissionDeniedException, InvalidDataException
     3677{
     3678   checkPermission(Permission.WRITE);
     3679   if (user == null) throw new InvalidUseOfNullException("user");
     3680   if (permissions == null || permissions.isEmpty())
     3681   {
     3682      getData().getUsers().remove(user.getData());
     3683   }
     3684   else
     3685   {
     3686      getData().getUsers().put(user.getData(), Permission.grant(permissions));
     3687   }
     3688}
     3689
     3690/**
     3691   Get the permissions for a user in this project.
     3692   @param user The user for which we want to get the permission
     3693   @return A set containing the granted permissions, or an
     3694      empty set if no permissions have been granted
     3695   @throws InvalidDataException If the user is null
     3696   @see Permission
     3697*/
     3698public Set<Permission> getPermissions(User user)
     3699   throws InvalidDataException
     3700{
     3701   if (user == null) throw new InvalidUseOfNullException("user");
     3702   return Permission.fromInt(getData().getUsers().get(user.getData()));
     3703}
     3704
     3705/**
     3706   Get a query that returns the users that
     3707   are members of this project. This query excludes users that the logged
     3708   in user doesn't have permission to read.
     3709   @see User#getQuery()
     3710*/
     3711public ItemQuery<User> getUsers()
     3712{
     3713   ItemQuery<User> query = User.getQuery();
     3714   query.joinPermanent(
     3715      Hql.innerJoin("projects", Item.PROJECT.getAlias())
     3716   );
     3717   query.restrictPermanent(
     3718      Restrictions.eq(
     3719         Hql.alias(Item.PROJECT.getAlias()),
     3720         Hql.entity(this)
     3721      )
     3722   );
     3723   return query;
     3724}
     3725
     3726// User.java
     3727/**
     3728   Get a query that returns the projects where this user is a
     3729   member. The query excludes projects that the logged in user doesn't have
     3730   permission to read. The query doesn't include projects where this user is
     3731   the owner.
     3732   @see Project#getQuery()
     3733*/
     3734public ItemQuery<Project> getProjects()
     3735{
     3736   ItemQuery<Project> query = Project.getQuery();
     3737   query.joinPermanent(
     3738      Hql.innerJoin("users", Item.USER.getAlias())
     3739   );
     3740   query.restrictPermanent(
     3741      Restrictions.eq(
     3742         Hql.index(Item.USER.getAlias(), null),
     3743         Hql.entity(this)
     3744      )
     3745   );
     3746   return query;
     3747}
     3748]]>
     3749</programlisting>
     3750        <para>
     3751          As you can see from these examples, the code is very different depending on the type of
     3752          association. We don't give any more examples here, but if you are unsure you should look
     3753          in the source code to get more inspiration.
     3754        </para>
     3755      </sect3>
    23433756    </sect2>   
     3757
    23443758    <sect2 id="core_ref.rules.batchclass">
    23453759      <title>Batch-class rules</title>
     
    23473761        TODO
    23483762      </para>
    2349     </sect2>   
     3763    </sect2>
     3764   
    23503765    <sect2 id="core_ref.rules.testclass">
    23513766      <title>Test-class rules</title>
     
    23533768        TODO
    23543769      </para>
    2355     </sect2>   
     3770    </sect2>
    23563771  </sect1>
    23573772 
Note: See TracChangeset for help on using the changeset viewer.