io.starter.datamodel.ContentData.java Source code

Java tutorial

Introduction

Here is the source code for io.starter.datamodel.ContentData.java

Source

package io.starter.datamodel;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

import io.starter.model.Acl;
import io.starter.model.AclExample;
import io.starter.model.AclExample.Criteria;
import io.starter.model.Category;
import io.starter.model.Content;
import io.starter.model.ContentCategoryIdx;
import io.starter.model.ContentExample;
import io.starter.model.User;
import io.starter.util.ImageUtils;
import io.starter.util.Logger;
import io.starter.util.S3FS;
import io.starter.util.SystemConstants;

import org.apache.ibatis.session.SqlSession;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.jets3t.service.ServiceException;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;

import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;

/**
 * simple content model implementation
 * 
 * Content
 * 
 * fetch list of allowed content items (cacheable array of ints) fetch by (top,
 * bottom, alpha, mime type) X list (alt queries allowed?) post content rate
 * content comments ARE content items, rateable, shared with current share list
 * of comment target send messages to share group
 * 
 * @author John McMahon Copyright 2013 Starter Inc. -- all rights reserved.
 * @since 1
 * @version .1
 * 
 */
@Path("/content/{contentId}")
public class ContentData extends Model implements SystemConstants {

    private static final int NUM_RETURN_ROWS = 10;
    private static final int OPTYPE_SET_PUBLIC = 0;
    private static final int OPTYPE_SET_PRIVATE = 1;
    private static final int OPTYPE_SET_TAKEOVER_OWNERSHIP = 2;

    public ContentData() {
        Logger.debug("ContentData constructed");
    }

    /**
     * return a list of content objects from the passed-in query
     * 
     * @param query
     * @param servletRequest
     * @param servletResponse
     * @throws IOException
     * @throws ServletException
     * @throws JSONException
     * @throws org.json.JSONException 
     */
    @GET
    @Path("list")
    @Produces(MediaType.APPLICATION_JSON)
    public String list(@QueryParam("query") String query, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse)
            throws IOException, ServletException, JSONException, org.json.JSONException {

        List results = null;

        int begin = 0;
        if (servletRequest.getParameter("offset") != null) {
            try {
                int offs = Integer.parseInt(servletRequest.getParameter("offset"));
                begin = offs;
            } catch (Exception x) {
                Logger.log("Could not parse list position from: " + servletRequest.getParameter("offset"));
            }
        }

        if (begin != 0) //  || (!query.equalsIgnoreCase("top") && (query.indexOf("userId") == -1)&& (query.indexOf("new") == -1))) // get
            results = getCachedContentList(query, servletRequest);

        User user = null;

        Object u = servletRequest.getAttribute(SESSION_VAR_USER);

        boolean categorySelected = false;
        int catId = -1;
        int userId = -1;
        String mimeType = null;
        boolean restrictToLocal = false;

        try {
            catId = Integer.parseInt(query);
            categorySelected = true;
        } catch (Exception e) {
            ;
        }

        try {
            if (query.indexOf("userId") > -1) {
                String uid = query.substring(query.indexOf("userId=") + 7);
                userId = Integer.parseInt(uid);
            } else if (query.indexOf("mimetype") > -1) {

                // web video picture sound joke
                // audio/mp4 image/jpeg image/png
                // video/quicktime, text/plain text/html
                String mtid = query.substring(query.indexOf("mimetype=") + 9);

                if (mtid.equalsIgnoreCase("video")) {
                    mimeType = "video/quicktime";
                } else if (mtid.equalsIgnoreCase("picture")) {
                    mimeType = "image";
                } else if (mtid.equalsIgnoreCase("web")) {
                    mimeType = "text/html";
                } else if (mtid.equalsIgnoreCase("joke")) {
                    mimeType = "text/html";
                    restrictToLocal = true; // this allows us to differentiate
                    // jokes with remote web clips
                } else if (mtid.equalsIgnoreCase("sound")) {
                    mimeType = "audio/mp4";
                }
            }
        } catch (Exception e) {
            ;
        }

        // login the anon user to get public content
        if (u == null)
            throw new ServletException("No User in Request -- Cannot fetch content list anonymously.");
        user = (User) u;

        // TODO: test this for performance hit...
        user.clearCachedAuthorization();

        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);

        if (results == null) {
            Logger.debug("Content List Cache Miss for: " + query);

            if (categorySelected) { // filtered by category id
                results = session.selectList("io.starter.dao.ContentMapper.selectObjByCategory", catId);
            } else { // return list by other means
                ContentExample example = new ContentExample();
                io.starter.model.ContentExample.Criteria criteria = example.createCriteria();

                if (query.equalsIgnoreCase("lbc")) { // location based starter
                    criteria.andLatitudeIsNotNull();
                    criteria.andLatitudeNotEqualTo(0.0d);
                    criteria.andLongitudeIsNotNull();
                    criteria.andLongitudeNotEqualTo(0.0d);
                } else if (query.indexOf("mimetype") > -1) { // mime type
                    // based
                    if (mimeType.indexOf("/") == -1) { // fuzzy match
                        criteria.andMimeTypeLike("%" + mimeType + "%");
                    } else { // exact match
                        criteria.andMimeTypeEqualTo(mimeType);
                    }
                } else if (userId > -1) {
                    criteria.andUserIdEqualTo(userId);

                }

                if (!user.isAdmin()) {
                    criteria.andFlagGreaterThanOrEqualTo(FLAG_NONE); // any
                }

                example.setOrderByClause("post_date DESC");
                example.setDistinct(true);
                results = session.selectList("io.starter.dao.ContentMapper.selectObjByExample", example);

                // check if it's a local URL
                List retu = new ArrayList();
                Iterator itx = results.iterator();
                while (itx.hasNext()) {
                    Content dx = (Content) itx.next();
                    String rez = dx.getUrl();

                    if (restrictToLocal) {
                        if (rez.indexOf("http") == -1) {
                            retu.add(dx);
                        }
                        ;
                    } else {
                        // if(rez.indexOf("http") == 0){
                        retu.add(dx);
                        // };
                    }
                }
                results = retu;

                // SORT by Top rated
                if (query.equalsIgnoreCase("top")) {
                    Object[] ret = results.toArray();
                    Arrays.sort(ret);
                    results = Arrays.asList(ret);
                }

            }
            // add results list to caches
            addCachedContentList(query, results, servletRequest);
        }

        // call every time we return a list to avoid returning unauthorized
        // stuff
        List filteredResults = this.removeUnauthorized(servletRequest, results, SECURITY_ACL_READ,
                SECURITY_TARGET_TYPE_CONTENT);

        JSONArray j = new JSONArray();

        // add the 'isowner' request-specific field
        Iterator its = filteredResults.iterator();
        int countr = 0;
        while (its.hasNext()) {
            Content cx = (Content) its.next();

            if ((countr >= begin) && (countr < (begin + NUM_RETURN_ROWS))) {
                int uid = cx.getUserId();
                try {
                    User udd = UserData.getUserObjectByID(uid, servletRequest, servletResponse);
                    if (udd != null) {
                        cx.setUser(udd);
                    }
                } catch (Exception e) {
                    Logger.warn("ContentData.list: Could not get cached User object: " + e.toString());
                }

                // handle ownership here.
                JSONObject job = new JSONObject(cx);

                if (user != null) {
                    Integer uidx = user.getId();
                    if ((uidx != null && (uid == uidx)) || (user.isAdmin()))
                        job.put("isowner", true);
                    else
                        job.put("isowner", false);
                } else {
                    job.put("isowner", false);
                }
                job.put("isdeleted", (cx.getFlag() == FLAG_DELETED));

                // content display wrapper
                job.put("showWrapper", ContentData.getWrapper(cx.getId(), session));

                // set pretty date
                SimpleDateFormat sdf1 = new SimpleDateFormat(PRETTY_DATE_FORMAT);
                String postPrettyDate = sdf1.format(cx.getPostDate());
                job.put("prettyDate", postPrettyDate);

                // how many reshares
                job.put("reshare_ct", cx.getRepostCount());

                // how many ratings
                job.put("rating_ct", cx.getRatings().size());

                j.put(job);
            }

            countr++;
        }

        // return the JSON result
        return j.toString();
    }

    /**
     * upload a new content item in the system
     * 
     * @param author
     * @param device
     * @param copyright
     * @param description
     * @param url
     * @param servletRequest
     * @param servletResponse
     * @throws Exception
     */
    @PUT
    @Path("upload")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes({ MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_OCTET_STREAM })
    public String upload(@FormDataParam("binary_content") FormDataContentDisposition fileDetail,
            @FormDataParam("binary_content") InputStream uploadedInputStream,
            @FormDataParam("userImage") int userImage, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws Exception {

        User u = (User) servletRequest.getSession(true).getAttribute(SESSION_VAR_USER);
        /*
         * TODO: remove mediadir? String mediadir =
         * servletRequest.getSession().getServletContext()
         * .getInitParameter("media.dir");
         */
        String uploadedFileLocation = fileDetail.getFileName();

        // save it
        File[] uploadedFileset = saveToFile(uploadedInputStream, uploadedFileLocation, u.getId());

        Logger.log(
                "UserData.upload() File uploaded via Jersey based RESTFul Webservice to: " + uploadedFileLocation);

        if (userImage > 0) {
            String f = fileDetail.getFileName();
            u.setAvatarImage(f);

            SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);
            int rowsUpdated = session.update("io.starter.dao.UserMapper.updateByPrimaryKey", u);

            u = UserData.getUserObjectByID(u.getId(), servletRequest, servletResponse);
            u.setAvatarImage(f);

            session.commit();
            if (rowsUpdated < 1)
                throw new ServletException("UserData.upload() could not setAvatarImage()");
        }
        // return Response.status(200).entity(output).build();

        // return the JSON result
        JSONObject j = new JSONObject();

        String fnamey = uploadedFileLocation.substring(0, uploadedFileLocation.lastIndexOf("."));
        String fnamex = uploadedFileLocation.substring(uploadedFileLocation.lastIndexOf("."));

        String fna = "/" + fnamey + "/Standard" + fnamex;

        fna = fnamey + fnamex;
        j.put("filename", fna);

        j.put("mimeType", servletRequest.getSession().getServletContext().getMimeType(fna));

        return j.toString();
    }

    /**
     * 
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws JSONException
     * @throws IOException
     * @throws ServletException
     */
    @DELETE
    @Path("delete")
    public void delete(@PathParam("contentId") int id, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws IOException, ServletException {

        // TODO: checkaccess
        // Content cxdel = getContentObject(servletRequest, servletResponse);
        // String contentToDelete = cxdel.getUrl();

        // TODO: S3FS is our friend
        // S3FS s3fs = new S3FS();
        // s3fs.deleteFile(S3_STARTER_MEDIA_BUCKET,);

        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);

        try {

            // delete content acls
            AclExample a = new AclExample();
            Criteria c = a.createCriteria();
            c.andTargetIdEqualTo(id);
            c.andTargetTypeEqualTo(SystemConstants.SECURITY_TARGET_TYPE_CONTENT);

            session.delete("io.starter.dao.AclMapper.deleteByExample", a);
            session.commit();

            // delete content record
            session.delete("io.starter.dao.ContentMapper.deleteByPrimaryKey", id);
            session.commit();

        } catch (Exception e) {
            // if there are cascading FK constraints then we need to mark this
            // content item deleted (not actually delete it)
            // this way we preserve the integrity of the system
            // TODO: discuss handling deletion of content in re: orphan data

            Content cxdel = getContentObject(servletRequest, id);

            session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);

            try {
                cxdel.setFlag(FLAG_DELETED);
                session.update("io.starter.dao.ContentMapper.updateByPrimaryKey", cxdel);
                session.commit();
            } catch (Exception x) {
                Logger.error("Total failure in ContentData.delete trying to mark content item as deleted: "
                        + x.toString());
                // hmmm total failure
            }

        }

        super.deleteds.add(id);
        // update caches
        super.removeContentFromCaches(id, servletRequest);

    }

    /**
     * make the content item private and only accessible to the content owner
     * and administrators
     * 
     * @param id
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws IOException
     * @throws ServletException
     */
    @PUT
    @Path("makeprivate")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public String makeprivate(@PathParam("contentId") int id, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws IOException, ServletException {

        return resetAclsForContent(OPTYPE_SET_PRIVATE, servletRequest, id);

    }

    /**
     * make the content item public and accessible read-only to all users and
     * anonymous viewers
     * 
     * @param id
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws IOException
     * @throws ServletException
     */
    @PUT
    @Path("makepublic")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public String makepublic(@PathParam("contentId") int id, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws IOException, ServletException {

        return resetAclsForContent(OPTYPE_SET_PUBLIC, servletRequest, id);
    }

    /**
     * do the work of deleting all existing acls for content, inserting a new
     * owner acl, and optionally inserting necessary new ACL(s) based on the
     * OP_TYPE
     * 
     * @param optype
     * @param servletRequest
     * @param id
     * @return
     * @throws ServletException
     */
    private String resetAclsForContent(int optype, ServletRequest servletRequest, Integer id)
            throws ServletException {
        // DELETE all ACLs except the User's OWNER ACL
        Object u = servletRequest.getAttribute(SESSION_VAR_USER);

        if (u == null)
            throw new ServletException("No User in Request -- Anonymous users cannot modify content permissions.");

        User user = (User) u;
        int uid = user.getId();

        user.clearCachedAuthorizationForAllUsers();

        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);
        int rowsDeleted = 0;

        // delete content acls
        AclExample ax = new AclExample();
        Criteria c = ax.createCriteria();
        c.andTargetIdEqualTo(id);
        c.andTargetTypeEqualTo(SECURITY_TARGET_TYPE_CONTENT);
        session.delete("io.starter.dao.AclMapper.deleteByExample", ax);
        session.commit();

        Acl a = new Acl();
        // give permission to current session user -- only one allowed to do
        // this
        a.setPrincipleId(user.getId());
        a.setPrincipleType(SECURITY_PRINCIPAL_TYPE_USER);

        // allow them to
        a.setPermission(SECURITY_ACL_OWNER);

        // to this thing
        a.setTargetId(id);
        a.setTargetType(SECURITY_TARGET_TYPE_CONTENT);

        int rowsInserted = session.insert("io.starter.dao.AclMapper.insert", a);

        if (rowsInserted < 1)
            throw new ServletException("Could not make Content object private: setting Owner ACL failed.");

        if (optype == OPTYPE_SET_PUBLIC) { // make public

            Acl ae = new Acl();
            // give permission to current session user -- only one allowed
            // to do
            // this
            ae.setPrincipleId(SECURITY_ROLE_EVERYONE);
            ae.setPrincipleType(SECURITY_PRINCIPAL_TYPE_ROLE);

            // allow them to
            ae.setPermission(SystemConstants.SECURITY_ACL_APPEND); // required
            // to allow
            // comments/rating

            // to this thing
            ae.setTargetId(id);
            ae.setTargetType(SECURITY_TARGET_TYPE_CONTENT);

            rowsInserted = session.insert("io.starter.dao.AclMapper.insert", ae);
            session.commit();

            ae = new Acl();
            ae.setPrincipleId(SECURITY_ROLE_EVERYONE);
            ae.setPrincipleType(SECURITY_PRINCIPAL_TYPE_ROLE);

            // allow them to
            ae.setPermission(SystemConstants.SECURITY_ACL_READ); // need to see

            // to this thing
            ae.setTargetId(id);
            ae.setTargetType(SECURITY_TARGET_TYPE_CONTENT);

            rowsInserted += session.insert("io.starter.dao.AclMapper.insert", ae);
            session.commit();
            if (rowsInserted < 2)
                throw new ServletException(
                        "Could not make Content object private: setting Everyone READ-ONLY and APPEND ACLs failed.");

        } else if (optype == OPTYPE_SET_TAKEOVER_OWNERSHIP) { // take over
            // ownership
            // (administrators)
            if (!user.isAdmin())
                throw new ServletException(
                        "Could not take over Content object ownership: Only Administrators can do this.");

            Acl ax1 = new Acl();
            // give permission to current session user -- only one allowed
            // to do
            // this
            ax1.setPrincipleId(1);
            ax1.setPrincipleType(SECURITY_PRINCIPAL_TYPE_USER);

            // allow them to
            ax1.setPermission(SECURITY_ACL_OWNER);

            // to this thing
            ax1.setTargetId(id);
            ax1.setTargetType(SECURITY_TARGET_TYPE_CONTENT);

            rowsInserted = session.insert("io.starter.dao.AclMapper.insert", ax1);

            if (rowsInserted < 1)
                throw new ServletException(
                        "Could not take over Content object ownership: setting Owner ACL failed.");

        }
        session.commit();

        // return the result
        return "true";
    }

    /**
     * create a new content item in the system
     * 
     * @param author
     * @param device
     * @param copyright
     * @param description
     * @param url
     * @param categories
     * @param servletRequest
     * @param servletResponse
     * @param longitude
     * @param latitude
     * @param width
     * @param height
     * @return
     * @throws IOException
     * @throws ServletException
     * @throws JSONException
     * @throws org.json.JSONException 
     */
    @PUT
    @Path("create")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public String create(@FormParam("author") int author, @FormParam("device") int device,
            @FormParam("copyright") String copyright, @FormParam("description") String description,
            @FormParam("url") String url, @FormParam("categories") String categories,
            @Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse,
            @FormParam("longitude") Double longitude, @FormParam("latitude") Double latitude,
            @FormParam("mimeType") String mimeType, @FormParam("width") Long width,
            @FormParam("height") Long height)
            throws IOException, ServletException, JSONException, org.json.JSONException {

        Object u = servletRequest.getAttribute(SESSION_VAR_USER);

        if (u == null)
            throw new ServletException("No User in Request -- Cannot create anonymously owned content.");

        User user = (User) u;
        int uid = user.getId();

        url = fixContentURLs(url);

        user.getSubject().getSession(true);

        Content content = new Content();
        content.setUserId(uid);
        content.setAuthor(author);
        content.setAuthorDevice(device);
        content.setCopyright(copyright);
        content.setDescription(description);
        content.setLongitude(longitude);
        content.setLatitude(latitude);
        content.setWidth(width);
        content.setHeight(height);
        content.setUrl(url);
        content.setMimeType(mimeType);
        content.setFlag(FLAG_NONE);
        content.setPostDate(new Date(System.currentTimeMillis()));
        content.setLicense(
                "Content has been uploaded by end-users and is provided for personal non-commercial viewing under fair use laws of the United States, and additionally as satirical works protected speech under the First Amendment.");

        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);
        int rowsInserted = 0;

        rowsInserted = session.insert("io.starter.dao.ContentMapper.insert", content);

        session.commit();

        if (rowsInserted > 0) {
            ContentData.setContentOwnerPermissions(content, user, session);

            String dq = "userId=" + user.getId();
            removeCachedContentList(dq, servletRequest);
        }

        // set the categories for the object..
        setCategories(content.getId(), categories, session);

        content = this.getContentObject(servletRequest, content.getId());

        // return the JSON result
        JSONObject j = new JSONObject(content);

        // handle ownership here.
        if ((content.getUserId() == uid) || (user.isAdmin()))
            j.put("isowner", true);
        else
            j.put("isowner", false);

        return j.toString();
    }

    /**
     * helper method to 'improve' random webclip URLS, ie: YouTube embeds
     * 
     * @param url
     * @return
     */
    public static String fixContentURLs(String url) {
        // handle youtube webclips... use the proper URL for best display...

        String youEmbed = "http://www.youtube-nocookie.com/embed/";
        if (url.toLowerCase().contains(youEmbed)) // good!
            return url;

        youEmbed = "http://www.youtube.com/embed/";
        if (url.toLowerCase().contains(youEmbed)) // good!
            return url;

        String youBroken = "http://youtu.be/"; // bad
        String youBroken2 = "http://m.youtube.com/"; // bad

        if (url.toLowerCase().contains(youBroken)) { // ok replace!
            url = url.substring(youBroken.length());
        } else if (url.toLowerCase().contains(youBroken2)) { // ok replace!
            url = url.substring(url.indexOf("v=") + 2);
            url = url.substring(0, 11);
        } else {
            return url;
        }

        // matched a youtube url
        url = youEmbed + url;

        return url;
    }

    /**
     * handle the details of ownership ACLs and cache reset
     * 
     * @param content
     * @param user
     * @param session
     * @throws ServletException
     */
    public static void setContentOwnerPermissions(Content content, User user, SqlSession session)
            throws ServletException {
        // share a content item with the share group
        Acl a = new Acl();
        // give permission to
        a.setPrincipleId(user.getId());
        a.setPrincipleType(SECURITY_PRINCIPAL_TYPE_USER);

        // allow them to
        a.setPermission(SECURITY_ACL_OWNER);

        // to this thing
        a.setTargetId(content.getId());
        a.setTargetType(SECURITY_TARGET_TYPE_CONTENT);

        int rowsInserted = session.insert("io.starter.dao.AclMapper.insert", a);

        session.commit();

        if (rowsInserted > 0) {
            ;
        } else {
            throw new ServletException(
                    "Null rows inserted in ACL could not assign Ownership ACL to new Content object.");
        }

        user.clearCachedAuthorization();

        WildcardPermission owner = new WildcardPermission(SystemConstants.SECURITY_TARGET_TYPE_CONTENT + ":"
                + SystemConstants.SECURITY_ACL_OWNER + ":" + content.getId());

        assert (user.checkAccess(owner));

        // return content;
    }

    /**
     * assign categories to an item
     * 
     * @param obj
     * @param cats
     * @throws ServletException
     */
    public static void setCategories(Integer objId, String cats, SqlSession session) throws ServletException {

        int rowsInserted = 0;

        StringTokenizer toker = new StringTokenizer(cats, ",");

        while (toker.hasMoreElements()) {

            String st = toker.nextElement().toString();
            if (st.contains("[")) {
                st = st.substring(1);
            }

            if (st.contains("]")) {

                st = st.substring(0, st.length() - 1);

            }
            Integer categoryId = Integer.parseInt(st);

            ContentCategoryIdx cat = new ContentCategoryIdx();
            cat.setCategoryId(categoryId);
            cat.setContentId(objId);

            rowsInserted = session.insert("io.starter.dao.ContentCategoryIdxMapper.insert", cat);

            session.commit();

        }

        if (rowsInserted > 0) {
            ;
        } else {
            throw new ServletException(
                    "Null rows inserted in ContentCategory IDX -- could not assign categories to Content object.");
        }

    }

    // save uploaded file to new location
    private File[] saveToFile(InputStream uploadedInputStream, String uploadedFileLocation, int userId)
            throws ServiceException, IOException {

        uploadedFileLocation = S3_STARTER_MEDIA_FOLDER + "/" + userId + "/" + uploadedFileLocation;

        Logger.log("ContentData saveToFile. bucket:" + S3_STARTER_MEDIA_BUCKET + " file: " + uploadedFileLocation);

        // S3FS is our friend
        S3FS s3fs = new S3FS();
        s3fs.uploadToBucket(S3_STARTER_MEDIA_BUCKET, new DataInputStream(uploadedInputStream),
                uploadedFileLocation);

        // do special file ninja kungfu
        // add back the slash
        uploadedFileLocation = "/" + uploadedFileLocation;
        return saveStarterFile(uploadedFileLocation, userId);

    }

    /**
     * rate the content item
     * 
     * @param flag
     * @param raterId
     * @param value
     * @param review
     * @param url
     * @param servletRequest
     * @param servletResponse
     * @throws IOException
     * @throws JSONException
     * @PUT
     * @Path("rate")
     * @Produces(MediaType.APPLICATION_JSON)
     * @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public String
     *                                                  rate(@PathParam(
     *                                                  "contentId") int id,
     * @FormParam("flag") int flag, @FormParam("raterId") int raterId,
     * @FormParam("rating") int value, @FormParam("review") String review,
     * @FormParam("url") String url,
     * @Context HttpServletRequest servletRequest,
     * @Context HttpServletResponse servletResponse) throws IOException,
     *          JSONException {
     * 
     *          ContentRating contentRating = new ContentRating();
     *          contentRating.setFlag(flag);
     *          contentRating.setContentId(Integer.valueOf(id));
     *          contentRating.setReview(review); contentRating.setType(1);
     *          contentRating.setRaterId(raterId); contentRating.setValue((long)
     *          value);
     * 
     *          SqlSession session = (SqlSession) servletRequest
     *          .getAttribute(SESSION_VAR_SQLSESSION);
     * 
     *          session.insert("io.starter.dao.ContentRatingMapper.insert",
     *          contentRating);
     * 
     *          Content content = getContentObject(servletRequest,
     *          servletResponse, id);
     * 
     *          JSONObject ret = new JSONObject(); ret.put("rating",
     *          this.getAverageRating(id, session));
     * 
     *          // return the updated rating for the content object return
     *          ret.toString(); }
     */

    /**
     * returns the average rating for the content item
     * 
     * this is the content "score"
     * 
     * @param id
     * @param servletRequest
     * @throws IOException
     * @throws JSONException
     * @throws ServletException
     * @throws org.json.JSONException 
     */
    @GET
    @Path("rating")
    @Produces(MediaType.APPLICATION_JSON)
    public String getRating(@PathParam("contentId") int id, @Context HttpServletRequest servletRequest)
            throws IOException, JSONException, ServletException, org.json.JSONException {

        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);

        Content content = session.selectOne("io.starter.dao.ContentMapper.selectObjByPrimaryKey", id);

        JSONObject job = new JSONObject();
        job.put("rating", content.getRating());
        return job.toString();
    }

    /**
     * show the content
     * 
     * @param contentId
     * @throws Exception
     */
    @GET
    @Path("get")
    @Produces(MediaType.APPLICATION_JSON)
    public String get(@PathParam("contentId") int id, @Context HttpServletResponse servletResponse,
            @Context HttpServletRequest servletRequest) throws Exception {

        Content c = getContentObject(servletRequest, id);

        // replace with full-throated user object
        // TODO: check whether mybatis should lose this object map
        int uid = c.getUserId();
        User uxx = UserData.getUserObjectByID(uid, servletRequest, servletResponse);

        uxx.setPassword(null);
        uxx.setPasswordSalt(null);
        uxx.setEmail(null);
        c.setUser(uxx);

        JSONObject job = new JSONObject(c);
        SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);
        Object u = servletRequest.getAttribute(SESSION_VAR_USER);

        if (u == null)
            throw new ServletException("No User in Request - ContentData.get()");

        // get ratings for content in there
        float rtr = c.getRating();
        String rval = String.valueOf(rtr);
        job.put("rating", rval);
        // set pretty date
        SimpleDateFormat sdf1 = new SimpleDateFormat(PRETTY_DATE_FORMAT);
        String postPrettyDate = sdf1.format(c.getPostDate());
        job.put("prettyDate", postPrettyDate);

        // content display wrapper
        job.put("showWrapper", ContentData.getWrapper(id, session));

        User user = (User) u;
        int uidx = user.getId();

        if ((uid == uidx) || (user.isAdmin()))
            job.put("isowner", true);
        else
            job.put("isowner", false);

        job.put("isdeleted", (c.getFlag() == FLAG_DELETED));

        return job.toString();
    }

    /**
     * TODO: figure out if this approach makes sense.
     * 
     * password protection of upload directory vs. this type of stream handling.
     * 
     * Could not fix this error:
     * 
     * May 15, 2013 4:49:57 PM com.sun.jersey.spi.container.ContainerRequest
     * getEntity SEVERE: A message body reader for Java class
     * javax.servlet.http.HttpServletResponse, and Java type interface
     * javax.servlet.http.HttpServletResponse, and MIME media type
     * application/octet-stream was not found. The registered message body
     * readers compatible with the MIME media type are: application/octet-stream
     * -> com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
     * com.sun.jersey.core.impl.provider.entity.FileProvider
     * com.sun.jersey.core.impl.provider.entity.InputStreamProvider
     * com.sun.jersey.core.impl.provider.entity.DataSourceProvider
     * com.sun.jersey.core.impl.provider.entity.RenderedImageProvider
     * 
     * returns the media file
     * 
     * @param image
     * @param servletRequest
     * @return
     * @throws IOException
     * @GET
     * @Path("media/{imagename ") public void getMedia(@PathParam("imagename")
     *                         String image, HttpServletResponse
     *                         servletResponse, HttpServletRequest
     *                         servletRequest) throws IOException { if
     *                         (!isSafeToOpenFile(image)) { throw new
     *                         IllegalArgumentException
     *                         ("Cannot open the media file."); }
     * 
     *                         String mediadir = (String)
     *                         servletRequest.getSession()
     *                         .getServletContext().getInitParameter
     *                         ("media.dir"); String uploadedFileLocation =
     *                         mediadir + image; File f = new
     *                         File(uploadedFileLocation); if (!f.exists()) {
     *                         throw new WebApplicationException(404); }
     * 
     *                         BufferedOutputStream bot = new
     *                         BufferedOutputStream(
     *                         servletResponse.getOutputStream()); bot.write(new
     *                         FileInputStream(f).read()); bot.flush();
     *                         bot.close();
     * 
     *                         }
     */

    /**
     * show the content
     * 
     * @param contentId
     * @throws IOException
     * @throws ServletException
     */
    @GET
    @Path("description")
    @Produces(MediaType.TEXT_PLAIN)
    public String getDescription(@PathParam("contentId") int id, @Context HttpServletRequest servletRequest,
            @Context HttpServletResponse servletResponse) throws IOException, ServletException {

        Content content = getContentObject(servletRequest, id);

        String ret = "NO CONTENTO";
        String desc = content.getDescription();
        if (desc != null)
            ret = desc;
        return ret;
    }

    /**
     * Get a Content object from this request
     * 
     * @param servletRequest
     * @param id
     * @return
     * @throws IOException
     * @throws ServletException
     */
    static Content getContentObject(HttpServletRequest servletRequest, int id)
            throws IOException, ServletException {

        // get cached
        Content content = getCachedContent(id, servletRequest);
        if (content == null) {

            Logger.debug("Content Object Cache Miss for content id: " + id);
            // apply some filters

            SqlSession session = (SqlSession) servletRequest.getAttribute(SESSION_VAR_SQLSESSION);

            content = session.selectOne("io.starter.dao.ContentMapper.selectObjByPrimaryKey", id);

            // add to cache
            addContentToCaches(id, content, servletRequest);
        } else {

            Logger.debug("Content Object Cache Hit for content id: " + id);
        }
        return content;
    }

    /**
     * determine whether to show a wrapper and if so, which one
     * 
     * currently we only have NSFW implemented as it is core
     * 
     * 'Rated G' : 0 'Rated PG' : -1 'Rated R' : -2 'N.S.F.W.' : -3 'Rated X' :
     * -4
     * 
     * 
     * @param session
     * 
     * @return
     */
    public static String getWrapper(int id, SqlSession session) {

        try {
            List<Category> results = session.selectList("io.starter.dao.ContentMapper.getCategoriesForContent", id);

            Iterator its = results.iterator();
            while (its.hasNext()) {
                Category cat = (Category) its.next();
                int rating = cat.getSuitabilityRating();
                if (rating < -2)
                    return "true"; // TODO: in the future implement custom
            }

        } catch (NullPointerException e) {
            ; // no categories
        }
        return "false";

    }

    /**
     * AWESOME IMAGE UTILS AREA
     */

    /**
     * returns 3 versions of an input image:
     * 
     * TRY THESE: 0: standardized 'medium' filesize 400w scaled 1: image blurred
     * 512 w back 2: image sharp 157@2x thumb 3: image tiny 64@2x thumb
     * 
     * 
     * @param input
     * @return
     * @throws IOException
     * @throws ServiceException
     */
    public static File[] getStarterPics(Content c) throws ServiceException, IOException {
        // get image
        String uploadedFileLocation = c.getUrl();
        int userId = c.getUserId();

        return saveStarterFile(uploadedFileLocation, userId);
    }

    /**
     * the heavy lifting
     * 
     * 
     * @param uploadedFileLocation
     * @param userId
     * @return
     * @throws ServiceException
     * @throws IOException
     */
    public static File[] saveStarterFile(String uploadedFileLocation, int userId)
            throws ServiceException, IOException {

        final S3FS s3fs = new S3FS();
        File fx = new File(SystemConstants.S3_STARTER_SERVICE + SystemConstants.S3_STARTER_MEDIA_BUCKET + "/"
                + uploadedFileLocation);
        File[] modfiles = null;
        // intercept and process the incoming IMAGE files
        if (uploadedFileLocation.toLowerCase().endsWith(".png")
                || uploadedFileLocation.toLowerCase().endsWith(".jpg")
                || uploadedFileLocation.toLowerCase().endsWith(".gif")
                || uploadedFileLocation.toLowerCase().endsWith(".jpeg")) {

            uploadedFileLocation = S3_STARTER_SERVICE + S3_STARTER_MEDIA_BUCKET + uploadedFileLocation;

            fx = new File(uploadedFileLocation);
            modfiles = ImageUtils.getStarterPics(fx, userId);
            // upload with nice names
            for (File modfile : modfiles) {
                fx = new File(uploadedFileLocation);
                String fnstart = modfile.getPath().substring(modfile.getPath().lastIndexOf("/"));

                String fnamx = fx.getName();
                fnamx = fnamx.substring(0, fnamx.lastIndexOf("."));

                fnstart = S3_STARTER_MEDIA_FOLDER + "/" + userId + "/" + fnamx + fnstart;
                s3fs.uploadToBucket(S3_STARTER_MEDIA_BUCKET, new DataInputStream(new FileInputStream(modfile)),
                        fnstart);
                Logger.log("ContentData saveToFile. saved Starter modified image files:" + S3_STARTER_MEDIA_BUCKET
                        + " file: " + fnstart);
            }

        } else { // non image content... folderize

            String fnend = uploadedFileLocation;
            fnend = fnend.substring(fnend.lastIndexOf(".")); // mimetype
            String fnamx = uploadedFileLocation;
            fnamx = fnamx.substring(fnamx.lastIndexOf("/") + 1); // filename
            fnamx = fnamx.substring(0, fnamx.lastIndexOf("."));

            if (((fnend.toLowerCase().endsWith(".mov")) || (fnend.toLowerCase().endsWith(".html"))
                    || (fnend.toLowerCase().endsWith(".m4a")))) {

                File fin = File.createTempFile("ck-", fnend);
                fx = ImageUtils.readFromUrl(fx.getPath(), fin);
                fin.deleteOnExit();
                modfiles = new File[1];
                modfiles[0] = fin;

            }

            String fnstart = uploadedFileLocation.substring(0, uploadedFileLocation.lastIndexOf(".") - 1);
            fnstart = S3_STARTER_MEDIA_FOLDER + "/" + userId + "/" + fnamx;

            uploadedFileLocation = fnstart + "/" + "Standard" + fnend;

            s3fs.uploadToBucket(S3_STARTER_MEDIA_BUCKET, new DataInputStream(new FileInputStream(fx)),
                    uploadedFileLocation);

            Logger.log("ContentData saveToFile. saved Starter modified image files:" + S3_STARTER_MEDIA_BUCKET
                    + " file: " + uploadedFileLocation);

        }
        return modfiles;
    }
}