/**
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.pnc.rest.provider;

import com.google.common.collect.ObjectArrays;
import org.jboss.pnc.model.GenericEntity;
import org.jboss.pnc.rest.provider.collection.CollectionInfo;
import org.jboss.pnc.rest.provider.collection.CollectionInfoCollector;
import org.jboss.pnc.rest.restmodel.GenericRestEntity;
import org.jboss.pnc.rest.validation.ValidationBuilder;
import org.jboss.pnc.rest.validation.exceptions.ValidationException;
import org.jboss.pnc.rest.validation.groups.WhenCreatingNew;
import org.jboss.pnc.rest.validation.groups.WhenDeleting;
import org.jboss.pnc.rest.validation.groups.WhenUpdating;
import org.jboss.pnc.spi.datastore.repositories.PageInfoProducer;
import org.jboss.pnc.spi.datastore.repositories.SortInfoProducer;
import org.jboss.pnc.spi.datastore.repositories.api.PageInfo;
import org.jboss.pnc.spi.datastore.repositories.api.Predicate;
import org.jboss.pnc.spi.datastore.repositories.api.RSQLPredicateProducer;
import org.jboss.pnc.spi.datastore.repositories.api.Repository;
import org.jboss.pnc.spi.datastore.repositories.api.SortInfo;

import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.function.Function;

import static org.jboss.pnc.rest.utils.StreamHelper.nullableStreamOf;

/**
 * Abstract provider with common functionality.
 *
 * @author Sebastian Laskawiec
 */
public abstract class AbstractProvider<DBEntity extends GenericEntity<Integer>, RESTEntity extends GenericRestEntity<Integer>> {

    protected RSQLPredicateProducer rsqlPredicateProducer;

    protected SortInfoProducer sortInfoProducer;

    protected PageInfoProducer pageInfoProducer;

    protected Repository<DBEntity, Integer> repository;

    @Deprecated
    public AbstractProvider() {
    }

    public AbstractProvider(Repository<DBEntity, Integer> repository, RSQLPredicateProducer rsqlPredicateProducer, SortInfoProducer sortInfoProducer,
            PageInfoProducer pageInfoProducer) {
        this.repository = repository;
        this.rsqlPredicateProducer = rsqlPredicateProducer;
        this.sortInfoProducer = sortInfoProducer;
        this.pageInfoProducer = pageInfoProducer;
    }

    public CollectionInfo<RESTEntity> getAll(int pageIndex, int pageSize, String sortingRsql, String query) {
        return queryForCollection(pageIndex, pageSize, sortingRsql, query, null);
    }

    public CollectionInfo<RESTEntity> queryForCollection(int pageIndex, int pageSize, String sortingRsql, String query,
            Predicate<DBEntity>... predicates) {
        Predicate<DBEntity> rsqlPredicate = rsqlPredicateProducer.getPredicate(getDBEntityClass(), query);
        PageInfo pageInfo = pageInfoProducer.getPageInfo(pageIndex, pageSize);
        SortInfo sortInfo = sortInfoProducer.getSortInfo(sortingRsql);

        List<DBEntity> collection;
        int totalPages;
        if(predicates == null) {
            collection = repository.queryWithPredicates(pageInfo, sortInfo, rsqlPredicate);
            totalPages = (repository.count(rsqlPredicate) + pageSize - 1) / pageSize;
        } else {
            collection = repository.queryWithPredicates(pageInfo, sortInfo, ObjectArrays.concat(rsqlPredicate, predicates));
            totalPages = (repository.count(ObjectArrays.concat(rsqlPredicate, predicates)) + pageSize - 1) / pageSize;
        }

        return nullableStreamOf(collection)
                .map(toRESTModel())
                .collect(new CollectionInfoCollector<>(pageIndex, pageSize, totalPages));
    }

    public RESTEntity getSpecific(Integer id) {
        DBEntity dbEntity = repository.queryById(id);
        if (dbEntity != null) {
            return toRESTModel().apply(dbEntity);
        }
        return null;
    }

    public Integer store(RESTEntity restEntity) throws ValidationException {
        validateBeforeSaving(restEntity);
        return repository.save(toDBModel().apply(restEntity)).getId();
    }

    public void update(Integer id, RESTEntity restEntity) throws ValidationException {
        restEntity.setId(id);
        validateBeforeUpdating(id, restEntity);
        repository.save(toDBModel().apply(restEntity));
    }

    public void delete(Integer id) throws ValidationException {
        validateBeforeDeleting(id);
        repository.delete(id);
    }

    protected void validateBeforeUpdating(Integer id, RESTEntity restEntity) throws ValidationException {
        ValidationBuilder.validateObject(restEntity, WhenUpdating.class)
                .validateNotEmptyArgument()
                .validateAnnotations()
                .validateAgainstRepository(repository, id, true);
    }

    protected void validateBeforeSaving(RESTEntity restEntity) throws ValidationException {
        ValidationBuilder.validateObject(restEntity, WhenCreatingNew.class)
                .validateNotEmptyArgument().validateAnnotations();
    }

    protected void validateBeforeDeleting(Integer id) throws ValidationException {
        ValidationBuilder.validateObject(WhenDeleting.class)
                .validateAgainstRepository(repository, id, true)
                .validateAnnotations();
    }

    protected abstract Function<? super DBEntity, ? extends RESTEntity> toRESTModel();

    protected abstract Function<? super RESTEntity, ? extends DBEntity> toDBModel();

    public Class<DBEntity> getDBEntityClass() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<DBEntity>) superclass.getActualTypeArguments()[0];
    }

    public Class<DBEntity> getRESTEntityClass() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<DBEntity>) superclass.getActualTypeArguments()[1];
    }

}

@@ -817,32 +817,85 @@
 .GenericEntity;%0A
+import org.jboss.pnc.rest.endpoint.AbstractEndpoint;%0A
 import org.jboss
@@ -1825,16 +1825,73 @@
 ortInfo;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Aimport
@@ -2279,16 +2279,104 @@
 er%3E%3E %7B%0A%0A
+    private static final Logger log = LoggerFactory.getLogger(AbstractEndpoint.class);%0A%0A
     prot
@@ -4721,32 +4721,95 @@
 ng(restEntity);%0A
+        log.debug(%22Storing entity: %22 + restEntity.toString());%0A
         return r
@@ -5027,32 +5027,96 @@
 d, restEntity);%0A
+        log.debug(%22Updating entity: %22 + restEntity.toString());%0A
         reposito

Add logging for REST operations.