/** * Copyright (c) 2009 - 2016 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.resteasy.filter; import org.candlepin.common.paging.Page; import org.candlepin.common.paging.PageRequest; import org.candlepin.model.AbstractHibernateObject; import org.candlepin.model.CandlepinQuery; import org.candlepin.model.ResultIterator; import org.candlepin.resteasy.JsonProvider; import com.google.inject.Inject; import com.google.inject.Provider; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.criterion.Order; import org.jboss.resteasy.annotations.interception.ServerInterceptor; import org.jboss.resteasy.core.ServerResponse; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.interception.PostProcessInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import javax.persistence.EntityManager; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.StreamingOutput; /** * The CandlepinQueryInterceptor handles the streaming of a query and applies any paging * configuration. */ @javax.ws.rs.ext.Provider @ServerInterceptor public class CandlepinQueryInterceptor implements PostProcessInterceptor { private static Logger log = LoggerFactory.getLogger(CandlepinQueryInterceptor.class); protected JsonProvider jsonProvider; protected Provider<EntityManager> emProvider; @Inject public CandlepinQueryInterceptor(JsonProvider jsonProvider, Provider<EntityManager> emProvider) { this.jsonProvider = jsonProvider; this.emProvider = emProvider; } /** * Opens a new session from the current session's session factory. * * @return * a newly opened session */ protected Session openSession() { Session currentSession = (Session) this.emProvider.get().getDelegate(); SessionFactory factory = currentSession.getSessionFactory(); return factory.openSession(); } @Override public void postProcess(ServerResponse response) { Object entity = response.getEntity(); if (entity instanceof CandlepinQuery) { final PageRequest pageRequest = ResteasyProviderFactory.getContextData(PageRequest.class); final Session session = this.openSession(); final CandlepinQuery query = (CandlepinQuery) entity; final ObjectMapper mapper = this.jsonProvider .locateMapper(Object.class, MediaType.APPLICATION_JSON_TYPE); // Use a separate session so we aren't at risk of lazy loading or interceptors closing // our cursor mid-stream. query.useSession(session); // Apply any paging config we may have if (pageRequest != null) { // Impl note: // Sorting will always be required (for consistency) if a page request object is // present -- either isPaging() will be true, or we'll have ordering config. String sortField = pageRequest.getSortBy() != null ? pageRequest.getSortBy() : AbstractHibernateObject.DEFAULT_SORT_FIELD; PageRequest.Order order = pageRequest.getOrder() != null ? pageRequest.getOrder() : PageRequest.DEFAULT_ORDER; query.addOrder(order == PageRequest.Order.DESCENDING ? Order.desc(sortField) : Order.asc(sortField) ); if (pageRequest.isPaging()) { query.setFirstResult((pageRequest.getPage() - 1) * pageRequest.getPerPage()); query.setMaxResults(pageRequest.getPerPage()); // Create a page object for the link header response Page page = new Page(); page.setMaxRecords(query.getRowCount()); // This is expensive :( page.setPageRequest(pageRequest); // Note: we don't need to store the page data in the page ResteasyProviderFactory.pushContext(Page.class, page); } } // Set the output streamer that will stream our query result response.setEntity(new StreamingOutput() { @Override public void write(OutputStream stream) throws IOException, WebApplicationException { JsonGenerator generator = mapper.getJsonFactory().createGenerator(stream); ResultIterator<Object> iterator = query.iterate(); generator.writeStartArray(); while (iterator.hasNext()) { mapper.writeValue(generator, iterator.next()); } generator.writeEndArray(); generator.flush(); generator.close(); iterator.close(); session.close(); } }); } } }