package org.apereo.cas.ticket.registry.support.kryo.serial;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apereo.cas.services.AbstractRegisteredService;
import org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy;
import org.apereo.cas.services.DefaultRegisteredServiceUsernameProvider;
import org.apereo.cas.services.LogoutType;
import org.apereo.cas.services.RefuseRegisteredServiceProxyPolicy;
import org.apereo.cas.services.RegexRegisteredService;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.RegisteredServicePublicKeyImpl;
import org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy;

import java.net.URL;

/**
 * Serializer for {@link RegisteredService} instances.
 * @author Misagh Moayyed
 * @since 4.1.0
 */
public class RegisteredServiceSerializer extends Serializer<RegisteredService> {

    /**
     * In case the url object is null in the service,
     * we need to be able to return a default/mock url.
     * @return mock url
     */
    private static URL getEmptyUrl() {
        try {
            return new URL("https://");
        } catch (final Exception e) {
            throw Throwables.propagate(e);
        }
    }

    @Override
    public void write(final Kryo kryo, final Output output, final RegisteredService service) {
        kryo.writeObject(output, service.getServiceId());
        kryo.writeObject(output, StringUtils.defaultIfEmpty(service.getName(), StringUtils.EMPTY));
        kryo.writeObject(output, StringUtils.defaultIfEmpty(service.getDescription(), StringUtils.EMPTY));
        kryo.writeObject(output, service.getId());
        kryo.writeObject(output, service.getEvaluationOrder());
        kryo.writeObject(output, ObjectUtils.defaultIfNull(service.getLogo(), getEmptyUrl()));
        kryo.writeObject(output, service.getLogoutType());
        kryo.writeObject(output, ObjectUtils.defaultIfNull(service.getLogoutUrl(), getEmptyUrl()));
        kryo.writeObject(output, ImmutableSet.copyOf(service.getRequiredHandlers()));
        kryo.writeObject(output, StringUtils.defaultIfEmpty(service.getTheme(), StringUtils.EMPTY));

        writeObjectByReflection(kryo, output, ObjectUtils.defaultIfNull(service.getPublicKey(),
                new RegisteredServicePublicKeyImpl()));
        writeObjectByReflection(kryo, output, ObjectUtils.defaultIfNull(service.getProxyPolicy(),
                new RefuseRegisteredServiceProxyPolicy()));
        writeObjectByReflection(kryo, output, ObjectUtils.defaultIfNull(service.getAttributeReleasePolicy(),
                new ReturnAllowedAttributeReleasePolicy()));
        writeObjectByReflection(kryo, output, ObjectUtils.defaultIfNull(service.getUsernameAttributeProvider(),
                new DefaultRegisteredServiceUsernameProvider()));
        writeObjectByReflection(kryo, output, ObjectUtils.defaultIfNull(service.getAccessStrategy(),
                new DefaultRegisteredServiceAccessStrategy()));
    }

    @Override
    public RegisteredService read(final Kryo kryo, final Input input, final Class<RegisteredService> type) {
        final AbstractRegisteredService svc = new RegexRegisteredService();
        svc.setServiceId(kryo.readObject(input, String.class));
        svc.setName(kryo.readObject(input, String.class));
        svc.setDescription(kryo.readObject(input, String.class));
        svc.setId(kryo.readObject(input, Long.class));
        svc.setEvaluationOrder(kryo.readObject(input, Integer.class));
        svc.setLogo(kryo.readObject(input, URL.class));
        svc.setLogoutType(kryo.readObject(input, LogoutType.class));
        svc.setLogoutUrl(kryo.readObject(input, URL.class));
        svc.setRequiredHandlers(kryo.readObject(input, ImmutableSet.class));
        svc.setTheme(kryo.readObject(input, String.class));

        svc.setPublicKey(readObjectByReflection(kryo, input));
        svc.setProxyPolicy(readObjectByReflection(kryo, input));
        svc.setAttributeReleasePolicy(readObjectByReflection(kryo, input));
        svc.setUsernameAttributeProvider(readObjectByReflection(kryo, input));
        svc.setAccessStrategy(readObjectByReflection(kryo, input));

        return svc;
    }


    /**
     * Write object by reflection.
     *
     * @param kryo   the kryo
     * @param output the output
     * @param obj    the obj
     */
    private static void writeObjectByReflection(final Kryo kryo, final Output output, final Object obj) {
        final String className = obj.getClass().getCanonicalName();
        kryo.writeObject(output, className);
        kryo.writeObject(output, obj);
    }

    /**
     * Read object by reflection.
     *
     * @param <T>   the type parameter
     * @param kryo  the kryo
     * @param input the input
     * @param type  the type
     * @return the t
     */
    private static <T> T readObjectByReflection(final Kryo kryo, final Input input) {
        try {
            final String className = kryo.readObject(input, String.class);
            final Class<T> clazz = (Class<T>) Class.forName(className);
            return kryo.readObject(input, clazz);
        } catch (final Exception e) {
            throw Throwables.propagate(e);
        }
    }
}

@@ -271,55 +271,8 @@
 es;%0A
-import com.google.common.collect.ImmutableSet;%0A
 impo
@@ -308,24 +308,24 @@
 tringUtils;%0A
+
 import org.a
@@ -926,16 +926,42 @@
 net.URL;
+%0Aimport java.util.HashSet;
 %0A%0A/**%0A *
@@ -2262,27 +2262,21 @@
 ut, 
-ImmutableSet.copyOf
+new HashSet%3C%3E
 (ser
@@ -3996,17 +3996,12 @@
 ut, 
-Immutable
+Hash
 Set.
@@ -4449,17 +4449,16 @@
 %0A    %7D%0A%0A
-%0A
     /**%0A
@@ -4972,32 +4972,32 @@
  kryo  the kryo%0A
+
      * @param in
@@ -5014,37 +5014,8 @@
 put%0A
-     * @param type  the type%0A
     

Replace Guava with Java collections (#2231)