/* * #%L * server * %% * Copyright (C) 2012 - 2015 valdasraps * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * <http://www.gnu.org/licenses/lgpl-3.0.html>. * #L% */ package lt.emasina.resthub.server.handler; import java.math.BigDecimal; import java.sql.SQLException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import lombok.Getter; import lombok.ToString; import lombok.extern.log4j.Log4j; import lt.emasina.resthub.server.app.BaseResource; import lt.emasina.resthub.server.cache.CacheStats; import lt.emasina.resthub.server.cache.CcBase; import lt.emasina.resthub.server.exception.ClientErrorException; import lt.emasina.resthub.server.exporter.Exporter; import lt.emasina.resthub.server.factory.CacheFactory; import lt.emasina.resthub.server.factory.ResourceFactory; import lt.emasina.resthub.server.query.Query; import lt.emasina.resthub.server.query.QueryParameter; import net.sf.ehcache.Cache; import net.sf.ehcache.Element; import org.apache.commons.lang.builder.HashCodeBuilder; import org.hibernate.SQLQuery; import org.hibernate.type.BigDecimalType; import org.hibernate.type.DateType; import org.hibernate.type.StringType; import org.json.JSONArray; import org.json.JSONException; import org.restlet.data.Form; import org.restlet.data.Status; import org.restlet.resource.ResourceException; /** * QueryHandler * @param <C> * @param <E> * @author valdo */ @Log4j @ToString(of = "query") public abstract class Handler<C extends CcBase<?>, E extends Exporter<C>> { @Inject private CacheFactory ccf; @Inject protected ResourceFactory rf; @Getter private final Query query; @Getter private final Map<QueryParameter, Object> parameters = new HashMap<>(); @Getter private final String queryString; public Handler(Query query, Form form) throws ResourceException { this.query = query; this.queryString = form.getQueryString(); for (QueryParameter p : query.getParameters()) { String name = p.getName(); String svalue = form.getFirstValue(name, true); switch (p.getType()) { case DATE: parameters.put(p, readParameterValue(Date.class, p, svalue)); break; case NUMBER: parameters.put(p, readParameterValue(BigDecimal.class, p, svalue)); break; case STRING: parameters.put(p, readParameterValue(String.class, p, svalue)); break; case CLOB: case BLOB: throw new ClientErrorException(Status.CLIENT_ERROR_BAD_REQUEST, String.format("LOBs are not supported as parameters: %s", name)); } } if (log.isDebugEnabled()) { for (QueryParameter qp: parameters.keySet()) { log.debug(qp.toString(parameters.get(qp))); } } } private static <T> Object readParameterValue(Class<T> clazz, QueryParameter param, String svalue) throws ResourceException { if (svalue == null) { return null; } else { try { if (param.getArray()) { JSONArray ja = new JSONArray(svalue); List<T> a = new ArrayList<>(); for (int i = 0; i < ja.length(); i++) { a.add(BaseResource.convertValue(clazz, ja.getString(i))); } return a.toArray(); } else { return BaseResource.convertValue(clazz, svalue); } } catch (JSONException ex) { throw new ClientErrorException(Status.CLIENT_ERROR_BAD_REQUEST, ex); } } } public void applyParameters(SQLQuery query) throws SQLException { for (Map.Entry<QueryParameter, Object> e : parameters.entrySet()) { QueryParameter p = e.getKey(); Object value = e.getValue(); String name = p.getSqlName(); if (value != null && p.getArray()) { switch (p.getType()) { case DATE: query.setParameterList(name, (Object[]) value, new DateType()); break; case NUMBER: query.setParameterList(name, (Object[]) value, new BigDecimalType()); break; case STRING: query.setParameterList(name, (Object[]) value, new StringType()); break; case CLOB: case BLOB: throw new ClientErrorException(Status.CLIENT_ERROR_BAD_REQUEST, String.format("LOBs are not supported as parameters: %s", name)); } } else { switch (p.getType()) { case DATE: query.setDate(name, (Date) value); break; case NUMBER: query.setBigDecimal(name, (BigDecimal) value); break; case STRING: query.setString(name, (String) value); break; case CLOB: case BLOB: throw new ClientErrorException(Status.CLIENT_ERROR_BAD_REQUEST, String.format("LOBs are not supported as parameters: %s", name)); } } } } @SuppressWarnings("unchecked") public C getCached() { if (query.isCacheable()) { Cache cache = ccf.get(query); if (cache == null) { if (log.isDebugEnabled()) { log.debug(String.format("Cache for %s not found", this)); } } else { if (log.isDebugEnabled()) { log.debug(String.format("Cache for %s found", this)); } if (cache.isKeyInCache(getId())) { Element el = cache.get(getId()); boolean expired = (el == null || el.isExpired()); if (log.isDebugEnabled()) { log.debug(String.format("Element %d found in %s cache (expired = %s)", getId(), query.getQid(), expired)); } if (el != null && !expired) { return (C) el.getObjectValue(); } } else { if (log.isDebugEnabled()) { log.debug(String.format("Element %d not found in %s cache", getId(), query.getQid())); } } } } return (C) null; } public void setCached(C data) { if (query.isCacheable()) { Cache cache = ccf.get(query); if (cache != null) { cache.put(new Element(getId(), data)); if (query.getHitCount() > 0 && !query.isEternal()) { CacheStats cs = getCacheStats(); ccf.createCacheJob(this, cs.getExpTime()); } if (log.isDebugEnabled()) { log.debug(String.format("Element %s put into %s cache", getId(), query.getQid())); } } } } public CacheStats getCacheStats() { CacheStats cs = new CacheStats(); if (query.isCacheable()) { Cache cache = ccf.get(query); if (cache != null) { if (cache.isKeyInCache(getId())) { Element el = cache.get(getId()); cs.setExpired(el == null || el.isExpired()); if (el != null && !cs.isExpired()) { cs.setLastUpdate(el.getLastUpdateTime()); cs.setExpTime(el.getExpirationTime()); cs.setHitCount(el.getHitCount()); } } } } return cs; } private Integer id = null; public final Integer getId() { if (id == null) { HashCodeBuilder hcb = new HashCodeBuilder(17, 37) .append(this.getClass()) .append(getQuery().getQid().getId()); for (Object part: getIdParts()) { hcb.append(part); } for (Map.Entry<QueryParameter, Object> e : getParameters().entrySet()) { hcb.append(e.getKey().getName()); hcb.append(e.getValue()); } this.id = hcb.toHashCode(); } return id; } protected abstract List getIdParts(); public abstract E createExporter(); }