org.fracturedatlas.athena.web.manager.RecordManager.java Source code

Java tutorial

Introduction

Here is the source code for org.fracturedatlas.athena.web.manager.RecordManager.java

Source

/*
    
ATHENA Project: Management Tools for the Cultural Sector
Copyright (C) 2010, Fractured Atlas
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/
    
 */
package org.fracturedatlas.athena.web.manager;

import com.sun.jersey.api.NotFoundException;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.lang.StringUtils;
import org.fracturedatlas.athena.apa.ApaAdapter;
import org.fracturedatlas.athena.apa.exception.ApaException;
import org.fracturedatlas.athena.client.PTicket;
import org.fracturedatlas.athena.web.exception.ObjectNotFoundException;
import org.fracturedatlas.athena.apa.impl.jpa.TicketProp;
import org.fracturedatlas.athena.id.IdAdapter;
import org.fracturedatlas.athena.search.AthenaSearch;
import org.fracturedatlas.athena.search.AthenaSearchConstraint;
import org.fracturedatlas.athena.search.Operator;
import org.fracturedatlas.athena.web.exception.AthenaException;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.User;

public class RecordManager {

    @Autowired
    ApaAdapter apa;
    Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    @javax.ws.rs.core.Context
    ApplicationContext applicationContext;

    @Autowired
    SecurityContextHolderStrategy contextHolderStrategy;

    public static final String ID_DELIMITER = ",";

    public PTicket getTicket(String type, Object id) {
        return apa.getRecord(type, id);
    }

    public void deleteTicket(PTicket t) {
        apa.deleteRecord(t);
    }

    public void deleteTicket(String type, Object id) {
        apa.deleteRecord(type, id);
    }

    public void deletePropertyFromTicket(String type, String propName, Object ticketId)
            throws ObjectNotFoundException {
        TicketProp prop = apa.getTicketProp(propName, type, ticketId);

        if (prop == null) {
            //no prop found, try and figure out why so we can return a sensible 404
            PTicket t = apa.getRecord(type, ticketId);
            if (t == null) {
                throw new ObjectNotFoundException("JpaRecord with id [" + ticketId + "] was not found");
            } else {
                throw new ObjectNotFoundException(
                        "Property with name [" + propName + "] was not found on ticket with id [" + ticketId + "]");
            }
        }

        apa.deleteTicketProp(prop);
    }

    public Collection<PTicket> findSubResources(String parentType, Object id, String childType,
            Map<String, List<String>> queryParams) {

        //Check to see if the parent record exists
        PTicket ticket = apa.getRecord(parentType, id);
        if (ticket == null) {
            throw new NotFoundException(StringUtils.capitalize(parentType) + " witn id [" + id + "] was not found");
        }

        //load the plugin.  If the plugin is found, let it do its thing.
        AthenaSubResource plugin = null;
        try {
            plugin = (AthenaSubResource) applicationContext.getBean(childType + "SubResource");
        } catch (NoSuchBeanDefinitionException noBean) {
            //it's okay
        }
        if (plugin != null) {
            String username = getCurrentUsername();
            return plugin.execute(parentType, id, childType, queryParams, username);
        } else {
            //If no plugin was found, look for sub-resources in apa
            //TODO: move this somewhere sensible
            String parentField = parentType + "Id";

            AthenaSearch athenaSearch = new AthenaSearch.Builder(
                    new AthenaSearchConstraint(parentField, Operator.EQUALS, IdAdapter.toString(id)))
                            .type(childType).build();

            return apa.findTickets(athenaSearch);
        }
    }

    private String getCurrentUsername() {
        Authentication authentication = contextHolderStrategy.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() != null
                && User.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
            User user = (User) authentication.getPrincipal();
            return user.getUsername();
        } else {
            return null;
        }
    }

    public Set<PTicket> findTickets(String type, MultivaluedMap<String, String> queryParams) {

        List<String> values = null;
        Operator operator;
        String value;
        Set<String> valueSet = null;
        AthenaSearch apaSearch = new AthenaSearch();
        apaSearch.setType(type);
        for (String fieldName : queryParams.keySet()) {
            values = queryParams.get(fieldName);

            if (values == null || values.size() == 0) {
                throw new AthenaException("Found no values for search parameter [" + fieldName + "]");
            }

            for (String operatorPrefixedValue : values) {
                if (StringUtils.isBlank(operatorPrefixedValue)) {
                    throw new AthenaException("Found no values for search parameter [" + fieldName + "]");
                }
                if (fieldName.startsWith("_")) {
                    apaSearch.setSearchModifier(fieldName, operatorPrefixedValue);
                } else {
                    int start = 0;

                    if (operatorPrefixedValue.length() < 2) {
                        operator = Operator.EQUALS;
                        value = operatorPrefixedValue;
                    } else {
                        //If the operator isn't found, this defaults to equals
                        operator = Operator.fromType(operatorPrefixedValue.substring(0, 2));
                        start = 2;

                        if (operator == null) {
                            operator = Operator.EQUALS;
                            start = 0;
                        }
                        value = operatorPrefixedValue.substring(start, operatorPrefixedValue.length());
                    }
                    if (StringUtils.isBlank(value)) {
                        throw new AthenaException("Found no values for search parameter [" + fieldName + "]");
                    }

                    valueSet = parseValues(value);
                    apaSearch.addConstraint(fieldName, operator, valueSet);
                }
            }
        }

        return apa.findTickets(apaSearch);
    }

    static Set<String> parseValues(String valueString) {
        HashSet<String> values = new HashSet<String>();
        valueString = StringUtils.trimToEmpty(valueString);
        valueString = StringUtils.strip(valueString, "()");
        valueString = StringUtils.trimToEmpty(valueString);
        CharacterIterator it = new StringCharacterIterator(valueString);
        boolean inString = false;
        int begin = 0;
        int end = 0;
        int numValues = 0;
        StringBuilder sb = new StringBuilder();
        // Iterate over the characters in the forward direction
        for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
            if (ch == '\"') {
                inString = true;
                ch = it.next();
                sb = new StringBuilder();
                for (; ch != CharacterIterator.DONE; ch = it.next()) {
                    if (ch == '\\') {
                        // skip any " in a string
                        sb.append(ch);
                        ch = it.next();
                    } else if (ch == '\"') {
                        break;
                    }
                    sb.append(ch);
                }
                inString = false;
                values.add(StringUtils.trimToEmpty(sb.toString()));
            } else if (ch == ',') {
                // new value
            } else if (" \t\n\r".indexOf(ch) > -1) {
                //skip whitespace
            } else {
                // not a comma, whitespace or a string start
                sb = new StringBuilder();
                for (; ch != CharacterIterator.DONE; ch = it.next()) {
                    if (ch == ',') {
                        break;
                    }
                    sb.append(ch);
                }
                inString = false;
                values.add(StringUtils.trimToEmpty(sb.toString()));

            }
        }

        return values;
    }

    /**
     * TODO: This method saves the records, then if one save fails it rolls back and deletes any records that it has saved to this point.
     * This is an edge case where it saves a record, some other client modifies the record, then this thread deletes that record in a rollback.
     * @param type
     * @param records
     * @return
     */
    public List<PTicket> createRecords(String type, List<PTicket> records) {
        List<PTicket> outRecords = new ArrayList<PTicket>();
        try {
            for (PTicket record : records) {
                outRecords.add(apa.saveRecord(type, record));
            }
        } catch (RuntimeException e) {
            logger.error("Exception while saving records [{}]", e);
            logger.error("Rolling back");
            for (PTicket t : outRecords) {
                try {
                    apa.deleteRecord(t);
                } catch (Exception exception) {
                    logger.error("Could not rollback record [{}]", t);
                    logger.error("[{}]", e.getMessage());
                }
            }
            throw e;
        }

        return outRecords;
    }

    public PTicket createRecord(String type, PTicket record) {
        return apa.saveRecord(type, record);
    }

    public PTicket updateRecord(String type, PTicket record) {

        PTicket ticket = apa.getRecord(type, record.getId());

        if (ticket == null) {
            throw new NotFoundException();
        }

        return apa.saveRecord(type, record);
    }

    public List<PTicket> updateRecords(String type, List<String> idList, PTicket patch)
            throws ObjectNotFoundException {
        List<PTicket> outRecords = new ArrayList<PTicket>();
        patch.setId(null);
        logger.debug("Applying [{}] to [{}]", patch, idList);
        for (String id : idList) {
            logger.debug("Applying patch to [{}]", id);

            try {
                outRecords.add(apa.patchRecord(id, type, patch));
            } catch (ApaException ae) {
                throw new ObjectNotFoundException(ae.getMessage());
            }
        }

        return outRecords;
    }

    public PTicket updateRecord(String type, PTicket record, String idToUpdate) {

        PTicket ticket = apa.getRecord(type, idToUpdate);

        if (idToUpdate == null || ticket == null) {
            throw new NotFoundException();
        }

        logger.debug("Updating record [{}]", ticket);
        logger.debug("With record record [{}]", record);

        if (!IdAdapter.isEqual(ticket.getId(), record.getId())) {
            throw new AthenaException(
                    "Requested update to [" + idToUpdate + "] but sent record with id [" + record.getId() + "]");
        }

        return apa.saveRecord(type, record);
    }

    public ApaAdapter getApa() {
        return apa;
    }

    public void setApa(ApaAdapter apa) {
        this.apa = apa;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public SecurityContextHolderStrategy getContextHolderStrategy() {
        return contextHolderStrategy;
    }

    public void setContextHolderStrategy(SecurityContextHolderStrategy contextHolderStrategy) {
        this.contextHolderStrategy = contextHolderStrategy;
    }
}