com.msopentech.odatajclient.proxy.api.impl.EntitySetInvocationHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.msopentech.odatajclient.proxy.api.impl.EntitySetInvocationHandler.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.msopentech.odatajclient.proxy.api.impl;

import com.msopentech.odatajclient.engine.communication.request.retrieve.ODataValueRequest;
import com.msopentech.odatajclient.engine.communication.response.ODataRetrieveResponse;
import com.msopentech.odatajclient.engine.data.ODataEntity;
import com.msopentech.odatajclient.engine.data.ODataEntitySet;
import com.msopentech.odatajclient.engine.format.ODataValueFormat;
import com.msopentech.odatajclient.engine.uri.URIBuilder;
import com.msopentech.odatajclient.proxy.api.AbstractEntityCollection;
import com.msopentech.odatajclient.proxy.api.AbstractEntitySet;
import com.msopentech.odatajclient.proxy.api.EntityContainerFactory;
import com.msopentech.odatajclient.proxy.api.annotations.CompoundKey;
import com.msopentech.odatajclient.proxy.api.annotations.CompoundKeyElement;
import com.msopentech.odatajclient.proxy.api.annotations.EntitySet;
import com.msopentech.odatajclient.proxy.api.annotations.EntityType;
import com.msopentech.odatajclient.proxy.api.context.AttachedEntityStatus;
import com.msopentech.odatajclient.proxy.api.context.EntityContext;
import com.msopentech.odatajclient.proxy.api.context.EntityUUID;
import com.msopentech.odatajclient.proxy.api.Query;
import com.msopentech.odatajclient.proxy.utils.ClassUtils;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class EntitySetInvocationHandler<T extends Serializable, KEY extends Serializable, EC extends AbstractEntityCollection<T>>
        extends AbstractInvocationHandler implements AbstractEntitySet<T, KEY, EC> {

    private static final long serialVersionUID = 2629912294765040027L;

    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(EntitySetInvocationHandler.class);

    private final Class<T> typeRef;

    private final Class<EC> collTypeRef;

    private final String entitySetName;

    private final URI uri;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    static EntitySetInvocationHandler getInstance(final Class<?> ref,
            final EntityContainerInvocationHandler containerHandler) {

        return new EntitySetInvocationHandler(ref, containerHandler);
    }

    @SuppressWarnings("unchecked")
    private EntitySetInvocationHandler(final Class<?> ref,
            final EntityContainerInvocationHandler containerHandler) {

        super(containerHandler.getClient(), containerHandler);

        final Annotation annotation = ref.getAnnotation(EntitySet.class);
        if (!(annotation instanceof EntitySet)) {
            throw new IllegalArgumentException(
                    "Return type " + ref.getName() + " is not annotated as @" + EntitySet.class.getSimpleName());
        }

        this.entitySetName = ((EntitySet) annotation).name();

        final Type[] abstractEntitySetParams = ((ParameterizedType) ref.getGenericInterfaces()[0])
                .getActualTypeArguments();

        this.typeRef = (Class<T>) abstractEntitySetParams[0];
        if (typeRef.getAnnotation(EntityType.class) == null) {
            throw new IllegalArgumentException("Invalid entity '" + typeRef.getSimpleName() + "'");
        }
        this.collTypeRef = (Class<EC>) abstractEntitySetParams[2];

        final URIBuilder uriBuilder = client.getURIBuilder(containerHandler.getFactory().getServiceRoot());

        if (!containerHandler.isDefaultEntityContainer()) {
            uriBuilder.appendStructuralSegment(containerHandler.getEntityContainerName())
                    .appendStructuralSegment(".");
        }

        uriBuilder.appendEntitySetSegment(entitySetName);
        this.uri = uriBuilder.build();
    }

    Class<T> getTypeRef() {
        return typeRef;
    }

    Class<EC> getCollTypeRef() {
        return collTypeRef;
    }

    String getEntitySetName() {
        return entitySetName;
    }

    URI getUri() {
        return uri;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        if (isSelfMethod(method, args)) {
            return invokeSelfMethod(method, args);
        } else if (method.getName().startsWith("new") && ArrayUtils.isEmpty(args)) {
            if (method.getName().endsWith("Collection")) {
                return newEntityCollection(method.getReturnType());
            } else {
                return newEntity(method.getReturnType());
            }
        } else {
            throw new UnsupportedOperationException("Method not found: " + method);
        }
    }

    @SuppressWarnings("unchecked")
    private <NE> NE newEntity(final Class<NE> reference) {
        final ODataEntity entity = client.getObjectFactory()
                .newEntity(containerHandler.getSchemaName() + "." + ClassUtils.getEntityTypeName(reference));

        final EntityTypeInvocationHandler handler = EntityTypeInvocationHandler.getInstance(entity,
                containerHandler.getEntityContainerName(), entitySetName, reference, containerHandler);
        EntityContainerFactory.getContext().entityContext().attachNew(handler);

        return (NE) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { reference }, handler);
    }

    @SuppressWarnings("unchecked")
    private <NEC> NEC newEntityCollection(final Class<NEC> reference) {
        return (NEC) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { reference }, new EntityCollectionInvocationHandler<T>(containerHandler,
                        new ArrayList<T>(), typeRef, containerHandler.getEntityContainerName()));
    }

    @Override
    public Long count() {
        final ODataValueRequest req = client.getRetrieveRequestFactory()
                .getValueRequest(client.getURIBuilder(this.uri.toASCIIString()).appendCountSegment().build());
        req.setFormat(ODataValueFormat.TEXT);
        return Long.valueOf(req.execute().getBody().asPrimitive().toString());
    }

    @Override
    public Boolean exists(final KEY key) throws IllegalArgumentException {
        boolean result = false;

        try {
            result = get(key) != null;
        } catch (Exception e) {
            LOG.error("Could not check existence of {}({})", this.entitySetName, key, e);
        }

        return result;
    }

    private LinkedHashMap<String, Object> getCompoundKey(final Object key) {
        final Set<CompoundKeyElementWrapper> elements = new TreeSet<CompoundKeyElementWrapper>();

        for (Method method : key.getClass().getMethods()) {
            final Annotation annotation = method.getAnnotation(CompoundKeyElement.class);
            if (annotation instanceof CompoundKeyElement) {
                elements.add(new CompoundKeyElementWrapper(((CompoundKeyElement) annotation).name(), method,
                        ((CompoundKeyElement) annotation).position()));
            }
        }

        final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();

        for (CompoundKeyElementWrapper element : elements) {
            try {
                map.put(element.getName(), element.getMethod().invoke(key));
            } catch (Exception e) {
                LOG.warn("Error retrieving compound key element '{}' value", element.getName(), e);
            }
        }

        return map;
    }

    @Override
    public T get(KEY key) throws IllegalArgumentException {
        return get(key, typeRef);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <S extends T> S get(final KEY key, final Class<S> typeRef) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("Null key");
        }

        final EntityUUID uuid = new EntityUUID(ClassUtils.getNamespace(typeRef),
                containerHandler.getEntityContainerName(), entitySetName,
                ClassUtils.getNamespace(typeRef) + "." + ClassUtils.getEntityTypeName(typeRef), key);

        LOG.debug("Ask for '{}({})'", typeRef.getSimpleName(), key);

        EntityTypeInvocationHandler handler = EntityContainerFactory.getContext().entityContext().getEntity(uuid);

        if (handler == null) {
            // not yet attached: search against the service
            try {
                LOG.debug("Search for '{}({})' into the service", typeRef.getSimpleName(), key);
                final URIBuilder uriBuilder = client.getURIBuilder(this.uri.toASCIIString());

                if (key.getClass().getAnnotation(CompoundKey.class) == null) {
                    LOG.debug("Append key segment '{}'", key);
                    uriBuilder.appendKeySegment(key);
                } else {
                    LOG.debug("Append compound key segment '{}'", key);
                    uriBuilder.appendKeySegment(getCompoundKey(key));
                }

                LOG.debug("Execute query '{}'", uriBuilder.toString());

                final ODataRetrieveResponse<ODataEntity> res = client.getRetrieveRequestFactory()
                        .getEntityRequest(uriBuilder.build()).execute();

                handler = EntityTypeInvocationHandler.getInstance(res.getBody(), this, typeRef);
                handler.setETag(res.getEtag());
            } catch (Exception e) {
                LOG.info("Entity '" + uuid + "' not found", e);
            }
        } else if (isDeleted(handler)) {
            // object deleted
            LOG.debug("Object '{}({})' has been delete", typeRef.getSimpleName(), uuid);
            handler = null;
        }

        return handler == null ? null
                : (S) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                        new Class<?>[] { typeRef }, handler);
    }

    @SuppressWarnings("unchecked")
    public <S extends T> Map.Entry<List<S>, URI> fetchPartialEntitySet(final URI uri, final Class<S> typeRef) {
        final ODataRetrieveResponse<ODataEntitySet> res = client.getRetrieveRequestFactory()
                .getEntitySetRequest(uri).execute();

        final ODataEntitySet entitySet = res.getBody();

        final List<S> items = new ArrayList<S>(entitySet.getEntities().size());
        for (ODataEntity entity : entitySet.getEntities()) {
            final EntityTypeInvocationHandler handler = EntityTypeInvocationHandler.getInstance(entity, this,
                    typeRef);

            final EntityTypeInvocationHandler handlerInTheContext = EntityContainerFactory.getContext()
                    .entityContext().getEntity(handler.getUUID());

            items.add((S) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[] { typeRef }, handlerInTheContext == null ? handler : handlerInTheContext));
        }

        return new AbstractMap.SimpleEntry<List<S>, URI>(items, entitySet.getNext());
    }

    @SuppressWarnings("unchecked")
    public <S extends T, SEC extends AbstractEntityCollection<S>> SEC fetchWholeEntitySet(final URI entitySetURI,
            final Class<S> typeRef, final Class<SEC> collTypeRef) {

        final List<S> items = new ArrayList<S>();

        URI nextURI = entitySetURI;
        while (nextURI != null) {
            final Map.Entry<List<S>, URI> entitySet = fetchPartialEntitySet(nextURI, typeRef);
            nextURI = entitySet.getValue();
            items.addAll(entitySet.getKey());
        }

        return (SEC) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { collTypeRef }, new EntityCollectionInvocationHandler<S>(containerHandler, items,
                        typeRef, containerHandler.getEntityContainerName(), entitySetURI));
    }

    @Override
    public EC getAll() {
        return getAll(collTypeRef);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <S extends T, SEC extends AbstractEntityCollection<S>> SEC getAll(final Class<SEC> collTypeRef) {
        final Class<S> typeRef = (Class<S>) ClassUtils.extractTypeArg(collTypeRef);

        final URI entitySetURI = client.getURIBuilder(this.uri.toASCIIString()).appendStructuralSegment(
                ClassUtils.getNamespace(typeRef) + "." + ClassUtils.getEntityTypeName(typeRef)).build();

        return fetchWholeEntitySet(entitySetURI, typeRef, collTypeRef);
    }

    @Override
    public Query<T, EC> createQuery() {
        return new QueryImpl<T, EC>(this.client, this.collTypeRef, this.uri, this);
    }

    @Override
    public <S extends T, SEC extends AbstractEntityCollection<S>> Query<S, SEC> createQuery(
            final Class<SEC> reference) {

        return new QueryImpl<S, SEC>(this.client, reference, this.uri, this);
    }

    @Override
    public void delete(final KEY key) throws IllegalArgumentException {
        final EntityContext entityContext = EntityContainerFactory.getContext().entityContext();

        EntityTypeInvocationHandler entity = entityContext.getEntity(new EntityUUID(
                ClassUtils.getNamespace(typeRef), containerHandler.getEntityContainerName(), entitySetName,
                ClassUtils.getNamespace(typeRef) + "." + ClassUtils.getEntityTypeName(typeRef), key));

        if (entity == null) {
            // search for entity
            final T searched = get(key);
            entity = (EntityTypeInvocationHandler) Proxy.getInvocationHandler(searched);
            entityContext.attach(entity, AttachedEntityStatus.DELETED);
        } else {
            entityContext.setStatus(entity, AttachedEntityStatus.DELETED);
        }
    }

    @Override
    public <S extends T> void delete(final Iterable<S> entities) {
        final EntityContext entityContext = EntityContainerFactory.getContext().entityContext();

        for (T en : entities) {
            final EntityTypeInvocationHandler entity = (EntityTypeInvocationHandler) Proxy.getInvocationHandler(en);
            if (entityContext.isAttached(entity)) {
                entityContext.setStatus(entity, AttachedEntityStatus.DELETED);
            } else {
                entityContext.attach(entity, AttachedEntityStatus.DELETED);
            }
        }
    }

    private boolean isDeleted(final EntityTypeInvocationHandler handler) {
        return EntityContainerFactory.getContext().entityContext()
                .getStatus(handler) == AttachedEntityStatus.DELETED;
    }

    @Override
    public EntitySetIterator<T, KEY, EC> iterator() {
        return new EntitySetIterator<T, KEY, EC>(client.getURIBuilder(this.uri.toASCIIString())
                .appendStructuralSegment(
                        ClassUtils.getNamespace(typeRef) + "." + ClassUtils.getEntityTypeName(typeRef))
                .build(), this);
    }
}