@Override
    public Iterator<RosterItem> getItems(String username) {
        final LinkedList<RosterItem> cachedValue = rosterItemCache.get( username );
        if ( cachedValue != null ) {
            return cachedValue.iterator();
        }
        LinkedList<RosterItem> itemList = new LinkedList<>();
        Map<Long, RosterItem> itemsByID = new HashMap<>();
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            // Load all the contacts in the roster
            con = DbConnectionManager.getConnection();
            pstmt = con.prepareStatement(LOAD_ROSTER);
            pstmt.setString(1, username);
            rs = pstmt.executeQuery();
            while (rs.next()) {
                // Create a new RosterItem (ie. user contact) from the stored information
                // First parse out the presence, if it's there.
                Presence presence = null;
                if (rs.getString(7) != null) {
                    Element root = SAXReaderUtil.readRootElement(rs.getString(7));
                    presence = new Presence(root);
                }
                RosterItem item = new RosterItem(rs.getLong(2),
                        new JID(rs.getString(1)),
                        RosterItem.SubType.getTypeFromInt(rs.getInt(3)),
                        RosterItem.AskType.getTypeFromInt(rs.getInt(4)),
                        RosterItem.RecvType.getTypeFromInt(rs.getInt(5)),
                        rs.getString(6),
                        null);
                if (presence != null) {
                    item.setStoredSubscribeStanza(presence);
                }
                // Add the loaded RosterItem (ie. user contact) to the result
                itemList.add(item);
                itemsByID.put(item.getID(), item);
            }
            // Close the statement and result set
            DbConnectionManager.fastcloseStmt(rs, pstmt);
            // Set null to pstmt to be sure that it's not closed twice. It seems that
            // Sybase driver is raising an error when trying to close an already closed statement.
            // it2000 comment: TODO interesting, that's the only place with the sybase fix
            // it2000 comment: one should move this in closeStatement()
            pstmt = null;

            // Load the groups for the loaded contact
            if (!itemList.isEmpty()) {
                pstmt = con.prepareStatement(String.format(LOAD_ROSTER_ITEM_GROUPS, DbConnectionManager.getDatabaseType().escapeIdentifier("rank")));
                pstmt.setString(1, username);
                rs = pstmt.executeQuery();
                while (rs.next()) {
                    itemsByID.get(rs.getLong(1)).getGroups().add(rs.getString(2));
                }
            }

            rosterItemCache.put( username, itemList );
        }
        catch (SQLException | ExecutionException e) {
            Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        finally {
            DbConnectionManager.closeConnection(rs, pstmt, con);
        }
        return itemList.iterator();
    }
Defines the provider methods required for creating, reading, updating and deleting roster
items.<p>

Rosters are another user resource accessed via the user or chatbot's long ID. A user/chatbot
may have zero or more roster items and each roster item may have zero or more groups. Each
roster item is additionally keyed on a XMPP jid. In most cases, the entire roster will be read
in from memory and manipulated or sent to the user. However some operations will need to retrieve
specific roster items rather than the entire roster.

@author Iain Shigeoka
xmppserver/src/main/java/org/jivesoftware/openfire/roster/DefaultRosterItemProvider.java