dandy-api/src/main/java/org/workhabit/drupal/api/entity/drupal7/DrupalUser.java
public class DrupalUser implements DrupalEntity {
    private int uid;
    private String name;
    private String mail;
    private Date access;
    private Date created;
    private Date login;
    private String password;

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail = mail;
    }

    public Date getAccess() {
        return access;
    }

    public void setAccess(Date access) {
        this.access = access;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getLogin() {
        return login;
    }

    public void setLogin(Date login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @SuppressWarnings({"SameReturnValue"})
    public String getIdFieldName() {
        return "uid";
    }

    public String getId() {
        return String.valueOf(getUid());
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/impl/DrupalSiteContextInstanceState.java
public interface DrupalSiteContextInstanceState extends Serializable
{
    ArrayList<GenericCookie> getCookies();

    DrupalUser getUser();

    String getSession();
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/exceptions/DrupalSaveException.java
@SuppressWarnings({"UnusedDeclaration"})
public class DrupalSaveException extends DrupalFetchException {

    public DrupalSaveException(JSONObject objectResult) {
        super(objectResult);
    }

    public DrupalSaveException(Exception e) {
        super(e);
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/exceptions/DrupalLogoutException.java
public class DrupalLogoutException extends Exception {
    public DrupalLogoutException(Exception e) {
        this.initCause(e);
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/entity/drupal7/DrupalTaxonomyTerm.java
@SuppressWarnings({"UnusedDeclaration"})
public class DrupalTaxonomyTerm implements DrupalEntity
{
    private int tid;
    private int vid;
    private String name;
    private String description;
    private int weight;
    private int depth;
    private List<Integer> parents;
    private int nodeCount;

    public DrupalTaxonomyTerm()
    {

    }

    public int getVid()
    {
        return vid;
    }

    public void setVid(int vid)
    {
        this.vid = vid;
    }

    public int getTid()
    {
        return tid;
    }

    public void setTid(int tid)
    {
        this.tid = tid;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getDescription()
    {
        return description;
    }

    public void setDescription(String description)
    {
        this.description = description;
    }

    public int getNodeCount()
    {
        return nodeCount;
    }

    public void setNodeCount(int node_count)
    {
        this.nodeCount = node_count;
    }

    public int getWeight()
    {
        return weight;
    }

    public void setWeight(int weight)
    {
        this.weight = weight;
    }

    public int getDepth()
    {
        return depth;
    }

    public void setDepth(int depth)
    {
        this.depth = depth;
    }

    public List<Integer> getParents()
    {
        return parents;
    }

    public void setParents(List<Integer> parents)
    {
        this.parents = parents;
    }

    public String getId()
    {
        return Integer.toString(tid);
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/entity/drupal7/DrupalNode.java
public class DrupalNode implements DrupalEntity
{
    private int nid;
    private int uid;
    private String type;
    private Boolean status;
    private String title;
    private int comment;
    private Boolean promote;
    private Boolean moderate;
    private Boolean sticky;
    private Map<String, List<DrupalBody>> body;
    private String log;
    private Date revisionTimestamp;
    private int format;
    private String name;
    private String picture;
    private String data;
    private Date lastCommentTimestamp;
    private String lastCommentName;
    private int commentCount;
    private String language;

    // no need to serialize taxonomy
    private HashMap<Integer, DrupalTaxonomyTerm> taxonomy;
    private Date created;
    private Date changed;

    private Map<String, DrupalField> fields;

    public DrupalNode()
    {

    }

    public int getNid()
    {
        return nid;
    }

    public void setNid(int nid)
    {
        this.nid = nid;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public int getUid()
    {
        return uid;
    }

    public void setUid(int uid)
    {
        this.uid = uid;
    }

    public String getTitle()
    {
        return title;
    }

    public void setTitle(String title)
    {
        this.title = title;
    }

    public int getComment()
    {
        return comment;
    }

    public void setComment(int comment)
    {
        this.comment = comment;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public Boolean isPromote()
    {
        return promote;
    }

    public void setPromote(Boolean promote)
    {
        this.promote = promote;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public Boolean isModerate()
    {
        return moderate;
    }

    public void setModerate(Boolean moderate)
    {
        this.moderate = moderate;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public Boolean isSticky()
    {
        return sticky;
    }

    public void setSticky(Boolean sticky)
    {
        this.sticky = sticky;
    }

    public Map<String, List<DrupalBody>> getBody()
    {
        return body;
    }

    public void setBody(Map<String, List<DrupalBody>> body)
    {
        this.body = body;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public String getLog()
    {
        return log;
    }

    public void setLog(String log)
    {
        this.log = log;
    }

    public Date getRevisionTimestamp()
    {
        return revisionTimestamp;
    }

    public void setRevisionTimestamp(Date revisionTimestamp)
    {
        this.revisionTimestamp = revisionTimestamp;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public int getFormat()
    {
        return format;
    }

    public void setFormat(int format)
    {
        this.format = format;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public String getPicture()
    {
        return picture;
    }

    public void setPicture(String picture)
    {
        this.picture = picture;
    }

    public String getData()
    {
        return data;
    }

    public void setData(String data)
    {
        this.data = data;
    }

    public Date getLastCommentTimestamp()
    {
        return lastCommentTimestamp;
    }

    public void setLastCommentTimestamp(Date lastCommentTimestamp)
    {
        this.lastCommentTimestamp = lastCommentTimestamp;
    }

    public String getLastCommentName()
    {
        return lastCommentName;
    }

    public void setLastCommentName(String lastCommentName)
    {
        this.lastCommentName = lastCommentName;
    }

    public int getCommentCount()
    {
        return commentCount;
    }

    public void setCommentCount(int commentCount)
    {
        this.commentCount = commentCount;
    }

    public HashMap<Integer, DrupalTaxonomyTerm> getTaxonomy()
    {
        return taxonomy;
    }

    public void setTaxonomy(HashMap<Integer, DrupalTaxonomyTerm> taxonomy)
    {
        this.taxonomy = taxonomy;
    }

    public Date getCreated()
    {
        return created;
    }

    public void setCreated(Date created)
    {
        this.created = created;
    }

    public Date getChanged()
    {
        return changed;
    }

    public void setChanged(Date changed)
    {
        this.changed = changed;
    }

    public Map<String, DrupalField> getFields()
    {
        return fields;
    }

    public void setFields(Map<String, DrupalField> fields)
    {
        this.fields = fields;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public DrupalField getField(String fieldName)
    {
        if (fields == null) {
            return null;
        }
        if (!fields.containsKey(fieldName)) {
            return null;
        }
        return fields.get(fieldName);
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void addField(String fieldName, HashMap<String, String> values)
    {
        if (this.fields == null) {
            this.fields = new HashMap<String, DrupalField>();
        }
        if (this.fields.containsKey(fieldName)) {
            DrupalField field = fields.get(fieldName);
            field.getValues().add(values);
        }
        else {
            DrupalField drupalField = new DrupalField();
            drupalField.setName(fieldName);

            ArrayList<HashMap<String, String>> valueList = new ArrayList<HashMap<String, String>>();
            valueList.add(values);
            drupalField.setValues(valueList);
            this.fields.put(fieldName, drupalField);
        }
    }

    public String getId()
    {
        return Integer.toString(getNid());
    }

    public String getType()
    {
        return type;
    }

    public void setType(String type)
    {
        this.type = type;
    }

    public Boolean getStatus()
    {
        return status;
    }

    public void setStatus(Boolean status)
    {
        this.status = status;
    }

    public String getLanguage()
    {
        return language;
    }

    public void setLanguage(String language)
    {
        this.language = language;
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/support/GenericCookie.java
public class GenericCookie implements Serializable
{
    private String comment;
    private String domain;
    private Date expiryDate;
    private String name;
    private String path;
    private boolean secure;
    private int version;
    private String value;

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public void setSecure(boolean secure) {
        this.secure = secure;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getComment() {
        return comment;
    }

    public String getDomain() {
        return domain;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }

    public String getName() {
        return name;
    }

    public String getPath() {
        return path;
    }

    public boolean isSecure() {
        return secure;
    }

    public int getVersion() {
        return version;
    }

    public String getValue() {
        return value;
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/entity/drupal7/DrupalComment.java
@SuppressWarnings({"UnusedDeclaration"})
public class DrupalComment implements DrupalEntity
{
    private int nid;
    private int cid;
    private int uid;
    private String subject;
    private String comment;
    private String name;
    private String mail;
    private Date timestamp;
    private Boolean status;
    private Date changed;
    private Date created;
    private Map<String, List<DrupalBody>> commentBody;

    public int getNid()
    {
        return nid;
    }

    public void setNid(int nid)
    {
        this.nid = nid;
    }

    public int getCid()
    {
        return cid;
    }

    public void setCid(int cid)
    {
        this.cid = cid;
    }

    public int getUid()
    {
        return uid;
    }

    public void setUid(int uid)
    {
        this.uid = uid;
    }

    public String getSubject()
    {
        return subject;
    }

    public void setSubject(String subject)
    {
        this.subject = subject;
    }

    public String getComment()
    {
        return comment;
    }

    public void setComment(String comment)
    {
        this.comment = comment;
    }

    public String getId()
    {
        return String.valueOf(cid);
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public String getMail()
    {
        return mail;
    }

    public void setMail(String mail)
    {
        this.mail = mail;
    }

    public Date getTimestamp()
    {
        return timestamp;
    }

    public void setTimestamp(Date timestamp)
    {
        this.timestamp = timestamp;
    }

    public Boolean isStatus()
    {
        return status;
    }

    public void setStatus(Boolean status)
    {
        this.status = status;
    }

    public Date getChanged()
    {
        return changed;
    }

    public void setChanged(Date changed)
    {
        this.changed = changed;
    }

    public Date getCreated()
    {
        return created;
    }

    public void setCreated(Date created)
    {
        this.created = created;
    }

    public Map<String, List<DrupalBody>> getCommentBody()
    {
        return commentBody;
    }

    public void setCommentBody(Map<String,List<DrupalBody>> commentBody)
    {
        this.commentBody = commentBody;
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/exceptions/DrupalFetchException.java
public class DrupalFetchException extends Exception {
    private JSONObject objectResult;
    private String message;

    @SuppressWarnings({"UnusedDeclaration"})
    public DrupalFetchException(String message) {
        this.message = message;
    }

    public DrupalFetchException(JSONObject objectResult) {

        this.objectResult = objectResult;
        try {
            this.message = this.objectResult.getString("#data");
        } catch (JSONException e) {
            this.message = e.getMessage();
        }
    }
    public DrupalFetchException(Exception e) {
        initCause(e);
        this.message = String.format("%s: %s", e.getClass().getName(), e.getMessage());
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public JSONObject getObjectResult() {
        return objectResult;
    }


    public String getMessage() {
        return message;
    }
}
dandy-api/src/main/java/org/workhabit/drupal/api/site/exceptions/DrupalLoginException.java
@SuppressWarnings({"CanBeFinal"})
public class DrupalLoginException extends Exception {

    private Throwable t;

    public DrupalLoginException(Throwable t) {
        this.t = t;
    }

    @Override
    public String getMessage() {
        return t.getMessage();
    }

    @Override
    public Throwable getCause()
    {
        return t.getCause();
    }
}


Question: package org.workhabit.drupal.api.site;
import org.workhabit.drupal.api.entity.drupal7.DrupalComment;
import org.workhabit.drupal.api.entity.drupal7.DrupalNode;
import org.workhabit.drupal.api.entity.drupal7.*;
import org.workhabit.drupal.api.entity.drupal7.DrupalTaxonomyTerm;
import org.workhabit.drupal.api.entity.drupal7.DrupalUser;
import org.workhabit.drupal.api.site.exceptions.DrupalFetchException;
import org.workhabit.drupal.api.site.exceptions.DrupalLoginException;
import org.workhabit.drupal.api.site.exceptions.DrupalLogoutException;
import org.workhabit.drupal.api.site.exceptions.DrupalSaveException;
import org.workhabit.drupal.api.site.impl.DrupalSiteContextInstanceState;
import org.workhabit.drupal.api.site.support.GenericCookie;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;



/**
 * Copyright 2009 - WorkHabit, Inc. - acs
 * Date: Oct 11, 2010, 5:10:31 PM
 */
@SuppressWarnings({"UnusedDeclaration"})
public interface DrupalSiteContext
{
    void connect() throws DrupalFetchException;

    /**
     * logs out the currently logged in user (via user.logout)
     *
     * @throws DrupalLogoutException if there was an error logging out, or if there's no currently logged in user.
     */
    void logout() throws DrupalLogoutException;

    /**
     * returns a list of nodes provided by a node view (via views.get)
     *
     * @param viewName the name of the drupal view to call
     * @return a list of nodes.
     * @throws DrupalFetchException if there's an error during the request.
     */
    List<DrupalNode> getNodeView(String viewName) throws DrupalFetchException;

    /**
     * overload of getNodeView() that passes a list of arguments to the specified view. Useful for views that take an
     * optional argument.
     *
     * @param viewName      the name of the drupal view to call
     * @param viewArguments a "/" separated list of arguments.
     * @return a list of nodes
     * @throws DrupalFetchException if there was an error during the request.
     */
    List<DrupalNode> getNodeView(String viewName, String viewArguments) throws DrupalFetchException;

    /**
     * overload of getNodeView() that accepts a limit and offset for paging.  This is the preferred form.
     *
     * @param viewName      the name of the view
     * @param viewArguments a list of arguments to pass to the view.  If there are no arguments, pass null.
     * @param offset        an integer specifying the offset (from zero) to return.
     * @param limit         the number of results to return.
     * @return a list of nodes
     * @throws DrupalFetchException if there was an error during the request.
     */
    List<DrupalNode> getNodeView(String viewName, String viewArguments, int offset, int limit) throws DrupalFetchException;

    /**
     * returns the node for the give nid (via node.get)
     *
     * @param nid the ID of the node to return
     * @return a populated DrupalNode object
     * @throws DrupalFetchException if there's an error during the request.
     */
    DrupalNode getNode(int nid) throws DrupalFetchException;

    /**
     * fetches a Comment by CID (via comment.load)
     *
     * @param cid the comment ID
     * @return a populated DrupalComment object
     * @throws DrupalFetchException if there's an error during the request.
     */
    DrupalComment getComment(int cid) throws DrupalFetchException;

    /**
     * Save a drupal comment (via comment.save)
     *
     * @param comment the comment object to save.  Note that this MUST have a nid associated with it.  If cid is 0, it
     *                will create a new comment. If cid is a positive integer, then it will update an existing comment (provided
     *                the currently logged in user has permission to do so).
     * @return the cid of the saved comment, or 0 if there was a problem saving.
     * @throws DrupalFetchException if there's an error saving the comment.
     */
    int saveComment(final DrupalComment comment) throws DrupalFetchException;


    /**
     * Logs in a user by username and password (invokes the drupal Service user.login)
     *
     * @param username the user's username
     * @param password the user's plaintext password
     * @return a DrupalUser object if the request was successful.
     * @throws DrupalLoginException if there's an error logging in the user (e.g. username/password mismatch)
     * @throws DrupalFetchException if there's an error during the request.
     */
Answer:     DrupalUser login(String username, String password) throws DrupalLoginException, DrupalFetchException;