package io.advantageous.qbit.spring.config;

import io.advantageous.consul.discovery.ConsulServiceDiscoveryBuilder;
import io.advantageous.qbit.eventbus.EventBusCluster;
import io.advantageous.qbit.eventbus.EventBusClusterBuilder;
import io.advantageous.qbit.events.EventBusProxyCreator;
import io.advantageous.qbit.events.EventManager;
import io.advantageous.qbit.events.EventManagerBuilder;
import io.advantageous.qbit.events.impl.EventConnectorHub;
import io.advantageous.qbit.service.ServiceQueue;
import io.advantageous.qbit.service.discovery.ServiceChangedEventChannel;
import io.advantageous.qbit.service.discovery.ServiceDiscovery;
import io.advantageous.qbit.service.discovery.ServiceDiscoveryBuilder;
import io.advantageous.qbit.service.discovery.impl.ServiceDiscoveryImpl;
import io.advantageous.qbit.service.discovery.spi.ServiceDiscoveryFileSystemProvider;
import io.advantageous.qbit.spring.annotation.Clustered;
import io.advantageous.qbit.spring.annotation.QBitPublisher;
import io.advantageous.qbit.spring.properties.ConsulProperties;
import io.advantageous.qbit.spring.properties.EventBusProperties;
import io.advantageous.qbit.spring.properties.ServiceDiscoveryProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import java.io.File;
import java.util.concurrent.TimeUnit;

import static io.advantageous.consul.discovery.ConsulServiceDiscoveryBuilder.consulServiceDiscoveryBuilder;

/**
 * Configuration for QBit clustering.  This includes the remote service bus, remote proxy creator and service discovery.
 *
 * @author geoffc@gmail.com (Geoff Chandler)
 */
@Configuration
@EnableConfigurationProperties({ConsulProperties.class, EventBusProperties.class,
        ServiceDiscoveryProperties.class})
public class ClusterConfiguration {

    private final Logger logger = LoggerFactory.getLogger(ClusterConfiguration.class);

    @Bean
    public EventConnectorHub eventConnectorHub() {
        return new EventConnectorHub();
    }

    @Bean
    @ConditionalOnMissingBean(ServiceDiscovery.class)
    public ServiceDiscovery serviceDiscovery(final ConsulProperties consulProperties,
                                             final @QBitPublisher
                                             ServiceChangedEventChannel servicePoolUpdateEventChannel,
                                             final ServiceDiscoveryProperties discoveryProperties) {

        if (!(consulProperties.getDatacenter() == null || consulProperties.getDatacenter().isEmpty())) {

            /** Depending on consul as the default or only seems like a mistake. */
            final ConsulServiceDiscoveryBuilder consulServiceDiscoveryBuilder = consulServiceDiscoveryBuilder();

            consulServiceDiscoveryBuilder
                    .setConsulHost(consulProperties.getHost())
                    .setConsulPort(consulProperties.getPort())
                    .setDatacenter(consulProperties.getDatacenter())
                    .setServiceChangedEventChannel(servicePoolUpdateEventChannel)
                    .build();

            if (consulProperties.getBackupDir() != null) {
                final File backupDir = new File(consulProperties.getBackupDir());
                if (!backupDir.exists()) {
                    if (!backupDir.mkdirs()) {
                        logger.error(String.format("Backup dir %s does not exist and can't be created", backupDir));
                    }
                }
                consulServiceDiscoveryBuilder.setBackupDir(backupDir);
            }

            final ServiceDiscoveryImpl serviceDiscovery = consulServiceDiscoveryBuilder.build();
            serviceDiscovery.start();
            return serviceDiscovery;
        } else {
            final ServiceDiscoveryBuilder serviceDiscoveryBuilder = ServiceDiscoveryBuilder.serviceDiscoveryBuilder()
                    .setServiceDiscoveryProvider(new ServiceDiscoveryFileSystemProvider(discoveryProperties.getDir(),
                            discoveryProperties.getCheckIntervalMS()));
            return serviceDiscoveryBuilder.build();
        }
    }

    @Bean
    public EventBusCluster eventBusCluster(final @Qualifier("clusteredEventManagerImpl") EventManager eventManager,
                                           final EventConnectorHub eventConnectorHub,
                                           final ServiceDiscovery serviceDiscovery,
                                           final EventBusProperties props) {

        logger.info("Binding event bus to " + props.getPort());
        final EventBusClusterBuilder clusterBuilder = EventBusClusterBuilder.eventBusClusterBuilder();
        clusterBuilder.setServiceDiscovery(serviceDiscovery);
        clusterBuilder.setReplicationPortLocal(props.getPort());
        clusterBuilder.setEventBusName(props.getName());
        clusterBuilder.setEventManager(eventManager);
        clusterBuilder.setEventConnectorHub(eventConnectorHub);
        clusterBuilder.setPeerCheckTimeUnit(TimeUnit.SECONDS);
        clusterBuilder.setPeerCheckTimeInterval(props.getPeriodicCheckInSeconds());
        clusterBuilder.setReplicationServerCheckInInterval(props.getTtl());
        clusterBuilder.setReplicationServerCheckInTimeUnit(TimeUnit.SECONDS);
        final EventBusCluster cluster = clusterBuilder.build();
        try {
            cluster.start();
        } catch (final Exception e) {
            logger.error("Failed to start cluster for event bus.  Make sure you can connect to consul.");
            logger.error("Consul is down.  The event bus predates ServiceDiscovery");
            logger.error("ServiceDiscovery will default to using JSON files for peer endpoint config.");
            logger.error("The stats count is using service discovery, event bus is not yet.");
        }
        return cluster;
    }

    /**
     * The actual service queue for the clustered event bus.
     * All events are sent to this queue first.
     *
     * @param eventBusCluster actual event manager impl no proxy
     * @return the service queue
     */
    @Bean
    public ServiceQueue clusteredEventManagerServiceQueue(final @Qualifier("eventBusCluster")
                                                          EventBusCluster eventBusCluster) {

        if (eventBusCluster == null) {
            return null;
        }
        return eventBusCluster.eventServiceQueue();
    }

    @Bean
    @Clustered
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public EventManager clusteredEventManager(final @Qualifier("clusteredEventManagerServiceQueue")
                                              ServiceQueue eventManagerServiceQueue) {

        return eventManagerServiceQueue.createProxy(EventManager.class);
    }

    /**
     * Wraps factory method call so we can provide another implementation of interface if needed.
     *
     * @param eventConnectorHub the cluster of event connectors
     * @return the event manager
     */
    @Bean
    public EventManager clusteredEventManagerImpl(final EventConnectorHub eventConnectorHub) {
        return EventManagerBuilder.eventManagerBuilder()
                .setEventConnector(eventConnectorHub)
                .setName("CLUSTERED_EVENT_MANAGER").build();
    }

    @Bean
    @QBitPublisher
    public ServiceChangedEventChannel servicePoolUpdateEventChannel(final EventBusProxyCreator eventBusProxyCreator) {
        return eventBusProxyCreator.createProxy(ServiceChangedEventChannel.class);
    }
}


/*
 * $#
 * FOS Weka
 *
 * Copyright (C) 2013 Feedzai SA
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka.utils;

import com.feedzai.fos.api.*;
import com.feedzai.fos.common.validation.NotNull;
import com.feedzai.fos.common.validation.Nullable;
import com.feedzai.fos.impl.weka.exception.Data2ConfigurationMismatch;
import com.feedzai.fos.impl.weka.exception.WekaClassifierException;
import com.feedzai.fos.impl.weka.utils.setter.InstanceSetter;
import weka.classifiers.Classifier;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;

import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Utilities to map from weka specific objects to classification-api objects.
 *

 * These utilities are centralized where for easier maintenance.
 *
 * @author Marco Jorge (marco.jorge@feedzai.com)
 */
public class WekaUtils {

    /**
     * Converts the given instance fields into a fast vector with Attributes.
     *
     * @param classIndex     classifier index
     * @param instanceFields the list of instance fields that will generate the Attributes
     * @return a vector with one Attribute for each instanceFields (in the same order).
     */
    @NotNull
    public static FastVector instanceFields2Attributes(int classIndex, List<? extends Attribute> instanceFields) throws FOSException {
        checkNotNull(instanceFields, "Instance fields cannot be null");

        FastVector result = new FastVector(instanceFields.size());
        classIndex = classIndex == -1 ? instanceFields.size() - 1 : classIndex;

        int idx = 0;
        for (Attribute instanceField : instanceFields) {
            weka.core.Attribute attribute;

            Class<?> type = instanceField.getClass();
            if (type == CategoricalAttribute.class) {
                CategoricalAttribute categorical = (CategoricalAttribute) instanceField;
                List<String> instances = categorical.getCategoricalInstances();

                FastVector categoricalInstances = new FastVector(instances.size());
                for (String categoricalInstance : instances) {
                    categoricalInstances.addElement(categoricalInstance);
                }

                attribute = new weka.core.Attribute(instanceField.getName(), categoricalInstances, idx);

            } else if (type == NumericAttribute.class) {
                attribute = new weka.core.Attribute(instanceField.getName(), idx);
            } else {
                throw new FOSException("Unknown instance class");
            }
            result.addElement(attribute);
            idx++;
        }

        return result;
    }

    /**
     * Converts from the received Object[] to a weka Instance.
     *
     * @param data       the Object[] to convert (the scorable)
     * @param setters    the setters that will copy the data to the instance
     * @param attributes the list of attributes to copy over
     * @return an Instance with all data elements copied over from the Object[]
     * @throws WekaClassifierException when the setters and the data do not have the same size (more data than setters, or more setters than data)
     */

    public static Instance objectArray2Instance(Object[] data, InstanceSetter[] setters, FastVector attributes) throws FOSException {
        checkNotNull(data, "Instance cannot be null");
        checkNotNull(attributes, "Attributes cannot be null");
        checkNotNull(setters, "Setters cannot be null");

        Instance instance = new Instance(attributes.size());

        if (data.length == setters.length) {
            for (int idx = 0; idx < data.length; idx++) {
                if (data[idx] != null) {
                    setters[idx].set(instance, (weka.core.Attribute) attributes.elementAt(idx), data[idx].toString());
                } else {
                    instance.setMissing(idx);
                }
            }
        } else {
            throw new Data2ConfigurationMismatch(String.format("Data is not the same size as configured attributes (expected data size '%s' but was '%s')", setters.length, data.length));
        }

        return instance;
    }

    /**
     * Converts from InstanceFields to InstanceSetters (required for scorable manipulation).
     *
     * @param instanceFields The instance fields for which to create the converters
     * @param type           The type of instance to handle.
     * @return an array of InstanceSetter with one InstanceSetter for each InstanceField.
     */
    @NotNull
    public static InstanceSetter[] instanceFields2ValueSetters(final List<? extends Attribute> instanceFields, final InstanceType type) throws FOSException {
        InstanceSetter[] instanceSetters = new InstanceSetter[instanceFields.size()];

        for (int idx = 0; idx < instanceFields.size(); idx++) {
            final Attribute att = instanceFields.get(idx);
            instanceSetters[idx] = new InstanceSetter() {
                @Override
                public void set(Instance instance, weka.core.Attribute attribute, Object value) throws FOSException {
                    instance.setValue(attribute, att.parseOrMissing(value));
                }
            };
        }
        return instanceSetters;
    }

    public static double[] score(Classifier classifier, Object[] scorable, InstanceSetter[] instanceSetters, Instances instances, FastVector attributes) throws FOSException {
        try {
            Instance instance = WekaUtils.objectArray2Instance(scorable, instanceSetters, attributes);
            instance.setDataset(instances);

            return classifier.distributionForInstance(instance);
        } catch (Exception e) {
            throw new FOSException(e.getMessage(), e);
        }
    }

    /**
     * Closes the given scorer (check for null first).
     *
     * @param wekaThreadSafeScorer the scorer to close.
     */
    public static void closeSilently(@Nullable WekaThreadSafeScorer wekaThreadSafeScorer) {
        if (wekaThreadSafeScorer != null) {
            wekaThreadSafeScorer.close();
        }
    }

}


package br.com.triadworks.dbunit;

import static org.dbunit.operation.DatabaseOperation.CLEAN_INSERT;
import static org.dbunit.operation.DatabaseOperation.DELETE;
import static org.dbunit.operation.DatabaseOperation.DELETE_ALL;
import static org.dbunit.operation.DatabaseOperation.INSERT;
import static org.dbunit.operation.DatabaseOperation.REFRESH;
import static org.dbunit.operation.DatabaseOperation.TRUNCATE_TABLE;
import static org.dbunit.operation.DatabaseOperation.UPDATE;

import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;

import br.com.triadworks.dbunit.connection.DbUnitConnectionCreator;
import br.com.triadworks.dbunit.dataset.DataSetSource;
import br.com.triadworks.dbunit.resolver.DbUnitDataSetResolver;

public class DefaultDbUnitManagerImpl implements DbUnitManager {

	private final DbUnitConnectionCreator connectionCreator;
	private final DbUnitDataSetResolver dataSetResolver;

	public DefaultDbUnitManagerImpl(DbUnitConnectionCreator connectionCreator, DbUnitDataSetResolver dataSetResolver) {
		this.connectionCreator = connectionCreator;
		this.dataSetResolver = dataSetResolver;
	}

	@Override
	public void refresh(DataSetSource dataSetSource) {
		execute(REFRESH, dataSetSource);
	}

	@Override
	public void cleanAndInsert(DataSetSource dataSetSource) {
		execute(CLEAN_INSERT, dataSetSource);
	}

	@Override
	public void insert(DataSetSource dataSetSource) {
		execute(INSERT, dataSetSource);
	}

	@Override
	public void update(DataSetSource dataSetSource) {
		execute(UPDATE, dataSetSource);
	}

	@Override
	public void delete(DataSetSource dataSetSource) {
		execute(DELETE, dataSetSource);
	}

	@Override
	public void deleteAll(DataSetSource dataSetSource) {
		execute(DELETE_ALL, dataSetSource);
	}

	@Override
	public void truncate(DataSetSource dataSetSource) {
		execute(TRUNCATE_TABLE, dataSetSource);
	}

	/**
	 * Executes DbUnit operation on dataset
	 * dataSetSource.
	 *
	 * @param operation
	 *            the DBUnit operation
	 * @param dataSetSource
	 *            the dataset to be used by this operation
	 */
	protected void execute(DatabaseOperation operation, DataSetSource dataSetSource) {
		IDatabaseConnection dbconn = null;
		try {
			dbconn = connectionCreator.create();
			operation.execute(dbconn, findDataSet(dataSetSource));
		} catch (Exception e) {
			throw new IllegalStateException(
					"It's not possible to execute DbUnit operation: "
							+ e.getMessage(), e);
		} finally {
			connectionCreator.close(dbconn);
		}
	}

	protected IDataSet findDataSet(DataSetSource dataSetSource) {
		return dataSetResolver.resolve(dataSetSource);
	}

}


package org.jboss.resteasy.util;

import org.jboss.resteasy.specimpl.MultivaluedMapImpl;

import javax.ws.rs.core.MultivaluedMap;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Bill Burke
 * @version $Revision: 1 $
 */
public class Encode {
    private static final String UTF_8 = "UTF-8";

    private static final Pattern PARAM_REPLACEMENT = Pattern.compile("_resteasy_uri_parameter");

    private static final String[] pathEncoding = new String[128];
    private static final String[] pathSegmentEncoding = new String[128];
    private static final String[] matrixParameterEncoding = new String[128];
    private static final String[] queryNameValueEncoding = new String[128];
    private static final String[] queryStringEncoding = new String[128];

    static {
        /*
         * Encode via RFC 3986.
         * PCHAR is allowed allong with '/'
         *
         * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" sub-delims = "!" /
         * "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" pchar =
         * unreserved / pct-encoded / sub-delims / ":" / "@"
         */
        for (int i = 0; i < 128; i++) {
            if (i >= 'a' && i <= 'z')
                continue;
            if (i >= 'A' && i <= 'Z')
                continue;
            if (i >= '0' && i <= '9')
                continue;
            switch ((char) i) {
            case '-':
            case '.':
            case '_':
            case '~':
            case '!':
            case '$':
            case '&':
            case '\'':
            case '(':
            case ')':
            case '*':
            case '+':
            case ',':
            case '/':
            case ';':
            case '=':
            case ':':
            case '@':
                continue;
            }
            StringBuffer sb = new StringBuffer();
            sb.append((char) i);
            pathEncoding[i] = URLEncoder.encode(sb.toString());
        }
        pathEncoding[' '] = "%20";
        System.arraycopy(pathEncoding, 0, matrixParameterEncoding, 0, pathEncoding.length);
        matrixParameterEncoding[';'] = "%3B";
        matrixParameterEncoding['='] = "%3D";
        matrixParameterEncoding['/'] = "%2F"; // RESTEASY-729
        System.arraycopy(pathEncoding, 0, pathSegmentEncoding, 0, pathEncoding.length);
        pathSegmentEncoding['/'] = "%2F";
        /*
         * Encode via RFC 3986.
         *
         * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" space encoded as
         * '+'
         */
        for (int i = 0; i < 128; i++) {
            if (i >= 'a' && i <= 'z')
                continue;
            if (i >= 'A' && i <= 'Z')
                continue;
            if (i >= '0' && i <= '9')
                continue;
            switch ((char) i) {
            case '-':
            case '.':
            case '_':
            case '~':
            case '?':
                continue;
            case ' ':
                queryNameValueEncoding[i] = "+";
                continue;
            }
            StringBuffer sb = new StringBuffer();
            sb.append((char) i);
            queryNameValueEncoding[i] = URLEncoder.encode(sb.toString());
        }

        /*
         * query = *( pchar / "/" / "?" )
         */
        for (int i = 0; i < 128; i++) {
            if (i >= 'a' && i <= 'z')
                continue;
            if (i >= 'A' && i <= 'Z')
                continue;
            if (i >= '0' && i <= '9')
                continue;
            switch ((char) i) {
            case '-':
            case '.':
            case '_':
            case '~':
            case '!':
            case '$':
            case '&':
            case '\'':
            case '(':
            case ')':
            case '*':
            case '+':
            case ',':
            case ';':
            case '=':
            case ':':
            case '@':
            case '?':
            case '/':
                continue;
            case ' ':
                queryStringEncoding[i] = "%20";
                continue;
            }
            StringBuffer sb = new StringBuffer();
            sb.append((char) i);
            queryStringEncoding[i] = URLEncoder.encode(sb.toString());
        }
    }

    /**
     * Keep encoded values "%..." and template parameters intact.
     */
    public static String encodeQueryString(String value) {
        return encodeValue(value, queryStringEncoding);
    }

    /**
     * Keep encoded values "%...", matrix parameters, template parameters, and
     * '/' characters intact.
     */
    public static String encodePath(String value) {
        return encodeValue(value, pathEncoding);
    }

    /**
     * Keep encoded values "%...", matrix parameters and template parameters
     * intact.
     */
    public static String encodePathSegment(String value) {
        return encodeValue(value, pathSegmentEncoding);
    }

    /**
     * Keep encoded values "%..." and template parameters intact.
     */
    public static String encodeFragment(String value) {
        return encodeValue(value, queryNameValueEncoding);
    }

    /**
     * Keep encoded values "%..." and template parameters intact.
     */
    public static String encodeMatrixParam(String value) {
        return encodeValue(value, matrixParameterEncoding);
    }

    /**
     * Keep encoded values "%..." and template parameters intact.
     */
    public static String encodeQueryParam(String value) {
        return encodeValue(value, queryNameValueEncoding);
    }

    // private static final Pattern nonCodes =
    // Pattern.compile("%([^a-fA-F0-9]|$)");
    private static final Pattern nonCodes = Pattern.compile("%([^a-fA-F0-9]|[a-fA-F0-9]$|$|[a-fA-F0-9][^a-fA-F0-9])");
    private static final Pattern encodedChars = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])");
    private static final Pattern encodedCharsMulti = Pattern.compile("((%[a-fA-F0-9][a-fA-F0-9])+)");

    public static String decodePath(String path) {
        Matcher matcher = encodedCharsMulti.matcher(path);
        StringBuffer buf = new StringBuffer();
        CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();
        while (matcher.find()) {
            decoder.reset();
            String decoded = decodeBytes(matcher.group(1), decoder);
            decoded = decoded.replace("\\", "\\\\");
            decoded = decoded.replace("$", "\\$");
            matcher.appendReplacement(buf, decoded);
        }
        matcher.appendTail(buf);
        return buf.toString();
    }

    private static String decodeBytes(String enc, CharsetDecoder decoder) {
        Matcher matcher = encodedChars.matcher(enc);
        StringBuffer buf = new StringBuffer();
        ByteBuffer bytes = ByteBuffer.allocate(enc.length() / 3);
        while (matcher.find()) {
            int b = Integer.parseInt(matcher.group(1), 16);
            bytes.put((byte) b);
        }
        bytes.flip();
        try {
            return decoder.decode(bytes).toString();
        } catch (CharacterCodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Encode '%' if it is not an encoding sequence
     *
     * @param string
     * @return
     */
    public static String encodeNonCodes(String string) {
        Matcher matcher = nonCodes.matcher(string);
        StringBuffer buf = new StringBuffer();

        // FYI: we do not use the no-arg matcher.find()
        // coupled with matcher.appendReplacement()
        // because the matched text may contain
        // a second % and we must make sure we
        // encode it (if necessary).
        int idx = 0;
        while (matcher.find(idx)) {
            int start = matcher.start();
            buf.append(string.substring(idx, start));
            buf.append("%25");
            idx = start + 1;
        }
        buf.append(string.substring(idx));
        return buf.toString();
    }

    public static boolean savePathParams(String segment, StringBuffer newSegment, List<String> params) {
        boolean foundParam = false;
        // Regular expressions can have '{' and '}' characters. Replace them to
        // do match
        segment = PathHelper.replaceEnclosedCurlyBraces(segment);
        Matcher matcher = PathHelper.URI_TEMPLATE_PATTERN.matcher(segment);
        while (matcher.find()) {
            foundParam = true;
            String group = matcher.group();
            // Regular expressions can have '{' and '}' characters. Recover
            // earlier replacement
            params.add(PathHelper.recoverEnclosedCurlyBraces(group));
            matcher.appendReplacement(newSegment, "_resteasy_uri_parameter");
        }
        matcher.appendTail(newSegment);
        return foundParam;
    }

    /**
     * Keep encoded values "%..." and template parameters intact i.e. "{x}"
     *
     * @param segment
     * @param encoding
     * @return
     */
    public static String encodeValue(String segment, String[] encoding) {
        ArrayList<String> params = new ArrayList<String>();
        boolean foundParam = false;
        StringBuffer newSegment = new StringBuffer();
        if (savePathParams(segment, newSegment, params)) {
            foundParam = true;
            segment = newSegment.toString();
        }
        String result = encodeFromArray(segment, encoding, false);
        result = encodeNonCodes(result);
        segment = result;
        if (foundParam) {
            segment = pathParamReplacement(segment, params);
        }
        return segment;
    }

    /**
     * Encode via RFC 3986. PCHAR
     * is allowed allong with '/'
     *

     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" sub-delims = "!" / "$"
     * / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" pchar = unreserved
     * / pct-encoded / sub-delims / ":" / "@"
     */
    public static String encodePathAsIs(String segment) {
        return encodeFromArray(segment, pathEncoding, true);
    }

    /**
     * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
     *
     * @param segment
     * @return
     */
    public static String encodePathSaveEncodings(String segment) {
        String result = encodeFromArray(segment, pathEncoding, false);
        result = encodeNonCodes(result);
        return result;
    }

    /**
     * Encode via RFC 3986. PCHAR
     * is allowed allong with '/'
     *

     * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" sub-delims = "!" / "$"
     * / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" pchar = unreserved
     * / pct-encoded / sub-delims / ":" / "@"
     */
    public static String encodePathSegmentAsIs(String segment) {
        return encodeFromArray(segment, pathSegmentEncoding, true);
    }

    /**
     * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
     *
     * @param segment
     * @return
     */
    public static String encodePathSegmentSaveEncodings(String segment) {
        String result = encodeFromArray(segment, pathSegmentEncoding, false);
        result = encodeNonCodes(result);
        return result;
    }

    /**
     * Encodes everything of a query parameter name or value.
     *
     * @param nameOrValue
     * @return
     */
    public static String encodeQueryParamAsIs(String nameOrValue) {
        return encodeFromArray(nameOrValue, queryNameValueEncoding, true);
    }

    /**
     * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p"
     *
     * @param segment
     * @return
     */
    public static String encodeQueryParamSaveEncodings(String segment) {
        String result = encodeFromArray(segment, queryNameValueEncoding, false);
        result = encodeNonCodes(result);
        return result;
    }

    public static String encodeFragmentAsIs(String nameOrValue) {
        return encodeFromArray(nameOrValue, queryNameValueEncoding, true);
    }

    protected static String encodeFromArray(String segment, String[] encodingMap, boolean encodePercent) {
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < segment.length(); i++) {
            if (!encodePercent && segment.charAt(i) == '%') {
                result.append(segment.charAt(i));
                continue;
            }
            int idx = segment.charAt(i);
            String encoding = encode(idx, encodingMap);
            if (encoding == null) {
                result.append(segment.charAt(i));
            } else {
                result.append(encoding);
            }
        }
        return result.toString();
    }

    /**
     * @param zhar
     *            integer representation of character
     * @param encodingMap
     *            encoding map
     * @return URL encoded character
     */
    private static String encode(int zhar, String[] encodingMap) {
        String encoded;
        if (zhar < encodingMap.length) {
            encoded = encodingMap[zhar];
        } else {
            try {
                encoded = URLEncoder.encode(Character.toString((char) zhar), UTF_8);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return encoded;
    }

    public static String pathParamReplacement(String segment, List<String> params) {
        StringBuffer newSegment = new StringBuffer();
        Matcher matcher = PARAM_REPLACEMENT.matcher(segment);
        int i = 0;
        while (matcher.find()) {
            String replacement = params.get(i++);
            // double encode slashes, so that slashes stay where they are
            replacement = replacement.replace("\\", "\\\\");
            replacement = replacement.replace("$", "\\$");
            matcher.appendReplacement(newSegment, replacement);
        }
        matcher.appendTail(newSegment);
        segment = newSegment.toString();
        return segment;
    }

    /**
     * decode an encoded map
     *
     * @param map
     * @return
     */
    public static MultivaluedMap<String, String> decode(MultivaluedMap<String, String> map) {
        MultivaluedMapImpl<String, String> decoded = new MultivaluedMapImpl<String, String>();
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            List<String> values = entry.getValue();
            for (String value : values) {
                try {
                    decoded.add(URLDecoder.decode(entry.getKey(), UTF_8), URLDecoder.decode(value, UTF_8));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return decoded;
    }

    public static MultivaluedMap<String, String> encode(MultivaluedMap<String, String> map) {
        MultivaluedMapImpl<String, String> decoded = new MultivaluedMapImpl<String, String>();
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            List<String> values = entry.getValue();
            for (String value : values) {
                try {
                    decoded.add(URLEncoder.encode(entry.getKey(), UTF_8), URLEncoder.encode(value, UTF_8));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return decoded;
    }

    public static String decode(String string) {
        try {
            return URLDecoder.decode(string, UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

}


/*
 * $#
 * FOS Weka
 *
 * Copyright (C) 2013 Feedzai SA
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package hr.irb.fastRandomForest;

import com.feedzai.fos.impl.weka.exception.PMMLConversionException;
import com.feedzai.fos.impl.weka.utils.pmml.PMMLProducer;
import org.dmg.pmml.*;
import org.jpmml.model.JAXBUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weka.classifiers.Classifier;
import weka.classifiers.RandomForestUtils;
import weka.core.Attribute;
import weka.core.Instances;

import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import static com.feedzai.fos.impl.weka.utils.pmml.PMMLConversionCommons.*;

/**
 * A producer that converts a {@link hr.irb.fastRandomForest.FastRandomForest} instance to PMML.
 *
 * @author Ricardo Ferreira (ricardo.ferreira@feedzai.com)
 * @since 1.0.4
 */
public class FastRandomForestPMMLProducer implements PMMLProducer<FastRandomForest> {

    /**
     * The logger.
     */
    private final static Logger logger = LoggerFactory.getLogger(FastRandomForestPMMLProducer.class);

    /**
     * The algorithm name.
     */
    private static final String ALGORITHM_NAME = "weka:"+FastRandomForest.class.getName();

    /**
     * The model name.
     */
    private static final String MODEL_NAME = ALGORITHM_NAME+"_Model";

    @Override
    public void produce(FastRandomForest randomForestClassifier, File targetFile) throws PMMLConversionException {
        PMML pmml = produce(randomForestClassifier);
        try (FileOutputStream fis = new FileOutputStream(targetFile)){
            JAXBUtil.marshalPMML(pmml, new StreamResult(fis));
        } catch (Exception e) {
            throw new PMMLConversionException("Failed to marshal the PMML to the given file.", e);
        }
    }

    @Override
    public PMML produce(FastRandomForest randomForestClassifier) {
        // Get the Instances in the forest.
        Instances data = randomForestClassifier.m_Info;

        Header header = buildPMMLHeader("FastRandomForest as PMML.");

        PMML pmml = new PMML(header, new DataDictionary(), "4.2");

        // Builds the PMML DataDictionary and MiningSchema elements.
        DataDictionary dataDictionary = new DataDictionary();
        MiningSchema miningSchema = new MiningSchema();

        for (int i = 0; i < data.numAttributes(); i++) {
            Attribute attribute = data.attribute(i);

            DataType fieldType;
            if (attribute.isNumeric()) {
                fieldType = DataType.DOUBLE;
            } else {
                fieldType = DataType.STRING;
            }

            DataField dataField = new DataField(new FieldName(attribute.name()), attribute.isNominal() ? OpType.CATEGORICAL : OpType.CONTINUOUS, fieldType);
            if (attribute.isNominal()) {
                Enumeration enumeration = attribute.enumerateValues();
                while (enumeration.hasMoreElements()) {
                    dataField.withValues(new Value(String.valueOf(enumeration.nextElement())));
                }
            }

            dataDictionary.withDataFields(dataField);

            MiningField miningField = new MiningField(new FieldName(attribute.name()));

            if (data.classIndex() == i) {
                miningField.withUsageType(FieldUsageType.PREDICTED);
            } else {
                miningField.withUsageType(FieldUsageType.ACTIVE);
            }
            miningSchema.withMiningFields(miningField);
        }

        pmml.withDataDictionary(dataDictionary);

        MiningModel miningModel = new MiningModel(miningSchema, MiningFunctionType.CLASSIFICATION);
        miningModel.withModelName(MODEL_NAME);

        pmml.withModels(miningModel);

        Segmentation segmentation = new Segmentation(MultipleModelMethodType.MAJORITY_VOTE);
        miningModel.withSegmentation(segmentation);

        int segmentId = 1;
        for (Classifier classifier : RandomForestUtils.getBaggingClassifiers(randomForestClassifier.m_bagger)) {
            Segment segment = buildSegment(miningSchema, segmentId++, (FastRandomTree) classifier);
            segmentation.withSegments(segment);
        }

        return pmml;
    }

    /**
     * Builds a {@link org.dmg.pmml.Segment PMML Segment} that contains the {@link org.dmg.pmml.TreeModel PMML TreeModel}
     * representing the given {@link weka.classifiers.trees.RandomTree Weka RandomTree}.
     *
     * @param miningSchema The {@link org.dmg.pmml.MiningSchema PMML MiningSchema} that lists fields as used in the model.
     * @param segmentId    The Id to given to the {@link org.dmg.pmml.Segment PMML Segment element}.
     * @param randomTree   The {@link weka.classifiers.trees.RandomTree Weka RandomTree} to be converted to a {@link org.dmg.pmml.TreeModel PMML TreeModel}.
     * @return The created {@link org.dmg.pmml.Segment PMML Segment}.
     */
    private static Segment buildSegment(MiningSchema miningSchema, int segmentId, FastRandomTree randomTree) {
        int rootNodeId = 1;

        Node rootNode = new Node().withId(String.valueOf(rootNodeId)).withPredicate(new True());
        TreeModel treeModel = new TreeModel(miningSchema, rootNode, MiningFunctionType.CLASSIFICATION).withAlgorithmName(ALGORITHM_NAME).withModelName(MODEL_NAME).withSplitCharacteristic(TreeModel.SplitCharacteristic.BINARY_SPLIT);

        buildTreeNode(randomTree, rootNodeId, rootNode);

        Segment segment = new Segment();
        segment.withId(String.valueOf(segmentId));
        segment.withModel(treeModel);

        return segment;
    }

    /**
     * Builds a new {@link org.dmg.pmml.Node PMML Node} from the given {@link weka.classifiers.trees.RandomTree.Tree Weka Tree Node}.
     *
     * @param node           The Id to give to the generted {@link org.dmg.pmml.Node PMML Node}.
     * @param nodeId         The Id to give to the generted {@link org.dmg.pmml.Node PMML Node}.
     * @param parentPMMLNode The parent {@link org.dmg.pmml.Node PMML Node}.
     * @return The incremented Id given to recursively created {@link org.dmg.pmml.Node PMML Nodes}.
     */
    private static int buildTreeNode(FastRandomTree node, int nodeId, Node parentPMMLNode) {
        Instances instance = node.m_MotherForest.m_Info;

        addScoreDistribution(parentPMMLNode, node.m_ClassProbs, instance);

        if (node.m_Attribute == -1) {
            // Leaf: Add the node's score.
            parentPMMLNode.withScore(leafScoreFromDistribution(node.m_ClassProbs, instance));
            return nodeId;
        }

        Attribute attribute = instance.attribute(node.m_Attribute);

        if (attribute.isNominal()) {
            return buildNominalNode(attribute, node, nodeId, parentPMMLNode);
        } else if (attribute.isNumeric()) {
            return buildNumericNode(attribute, node, nodeId, parentPMMLNode);
        } else {
            throw new RuntimeException("Unsupported attribute type for: " + attribute);
        }
    }

    /**
     * Builds the {@link org.dmg.pmml.Node PMML Node} for a nominal attribute.
     *

     * In PMML these nodes are represented with multiple children, one for each of the attribute's values.
     *

     * For example, consider a nominal attribute, named "nominalAttribute", with values "cold", "hot" and "warm". In PMML this translates to:
     *

     *     {@code
     *       <Node id="2" score="1">
     *         <SimplePredicate field="nominalAttribute" operator="equal" value="cold"/>
     *       </Node>
     *       <Node id="3" score="0">
     *         <SimplePredicate field="nominalAttribute" operator="equal" value="hot"/>
     *       </Node>
     *       <Node id="4" score="1.5">
     *         <SimplePredicate field="nominalAttribute" operator="equal" value="warm"/>
     *       </Node>
     *     }
     *

     *
     * @param attribute      The {@link weka.core.Attribute} to which the node to build refers to.
     * @param node           The {@link weka.classifiers.trees.RandomTree.Tree Weka RandomTree node} we are converting to PMML.
     * @param nodeId         The Id to give to the generted {@link org.dmg.pmml.Node PMML Node}.
     * @param parentPMMLNode The parent {@link org.dmg.pmml.Node PMML Node}.
     * @return The incremented Id given to recursively created {@link org.dmg.pmml.Node PMML Nodes}.
     */
    private static int buildNominalNode(Attribute attribute, FastRandomTree node, int nodeId, Node parentPMMLNode) {
        List<Object> values = new ArrayList<>();
        Enumeration enumeration = attribute.enumerateValues();
        while (enumeration.hasMoreElements()) {
            values.add(enumeration.nextElement());
        }

        assert values.size() == node.m_Successors.length : "Number of successors expected to be the same as the number of attribute values";

        List<Node> children = new ArrayList<>();

        for (int i = 0; i < values.size(); i++) {
            Object value = values.get(i);

            SimplePredicate predicate = new SimplePredicate(new FieldName(attribute.name()), SimplePredicate.Operator.EQUAL).withValue(String.valueOf(value));
            Node child = new Node().withId(String.valueOf(++nodeId)).withPredicate(predicate);

            nodeId = buildTreeNode(node.m_Successors[i], nodeId, child);

            // Training proportion extension.
            child.withExtensions(new Extension().withName(TRAINING_PROPORTION_ELEMENT).withValue(String.valueOf(node.m_Prop[i])));

            children.add(child);
        }

        parentPMMLNode.withNodes(children);

        return nodeId;
    }

    /**
     * Builds the {@link org.dmg.pmml.Node PMML Node} for a numeric attribute.
     *

     * In PMML these nodes are represented having two children, each with a predicate that checks the node's split point.
     *

     * For example, consider a numeric attribute, named "numericAttribute", with a split point of 2.5 and two leaf nodes. In PMML this translates to:
     *

     *     {@code
     *       <Node id="2" score="1">
     *         <SimplePredicate field="numericAttribute" operator="lessThan" value="2.5"/>
     *       </Node>
     *       <Node id="3" score="0">
     *         <SimplePredicate field="numericAttribute" operator="greaterOrEqual" value="2.5"/>
     *       </Node>
     *     }
     *

     *
     * @param attribute      The {@link weka.core.Attribute} to which the node to build refers to.
     * @param node           The {@link weka.classifiers.trees.RandomTree.Tree Weka RandomTree node} we are converting to PMML.
     * @param nodeId         The Id to give to the generted {@link org.dmg.pmml.Node PMML Node}.
     * @param parentPMMLNode The parent {@link org.dmg.pmml.Node PMML Node}.
     * @return The incremented Id given to recursively created {@link org.dmg.pmml.Node PMML Nodes}.
     */
    private static int buildNumericNode(Attribute attribute, FastRandomTree node, int nodeId, Node parentPMMLNode) {
        SimplePredicate predicateLo = new SimplePredicate(new FieldName(attribute.name()), SimplePredicate.Operator.LESS_THAN).withValue(String.valueOf(node.m_SplitPoint));
        SimplePredicate predicateHi = new SimplePredicate(new FieldName(attribute.name()), SimplePredicate.Operator.GREATER_OR_EQUAL).withValue(String.valueOf(node.m_SplitPoint));

        Node nodeLo = new Node().withId(String.valueOf(++nodeId));
        nodeLo.withPredicate(predicateLo);

        nodeId = buildTreeNode(node.m_Successors[0], nodeId, nodeLo);

        Node nodeHi = new Node().withId(String.valueOf(++nodeId));
        nodeHi.withPredicate(predicateHi);

        nodeId = buildTreeNode(node.m_Successors[1], nodeId, nodeHi);

        // Training proportion extension.
        nodeLo.withExtensions(new Extension().withName(TRAINING_PROPORTION_ELEMENT).withValue(String.valueOf(node.m_Prop[0])));
        nodeHi.withExtensions(new Extension().withName(TRAINING_PROPORTION_ELEMENT).withValue(String.valueOf(node.m_Prop[1])));

        parentPMMLNode.withNodes(nodeLo, nodeHi);

        return nodeId;
    }
}


package org.projectodd.jrapidoc.introspector;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.jboss.resteasy.spi.metadata.MethodParameter;
import org.jboss.resteasy.spi.metadata.Parameter;
import org.jboss.resteasy.spi.metadata.ResourceBuilder;
import org.jboss.resteasy.spi.metadata.ResourceClass;
import org.jboss.resteasy.spi.metadata.ResourceLocator;
import org.jboss.resteasy.spi.metadata.ResourceMethod;
import org.jboss.resteasy.spi.metadata.ReturnOption;
import org.projectodd.jrapidoc.RestUtil;
import org.projectodd.jrapidoc.logger.Logger;
import org.projectodd.jrapidoc.model.Method;
import org.projectodd.jrapidoc.model.ModelUtil;
import org.projectodd.jrapidoc.model.Return;
import org.projectodd.jrapidoc.model.Service;
import org.projectodd.jrapidoc.model.ServiceGroup;
import org.projectodd.jrapidoc.model.TransportType;
import org.projectodd.jrapidoc.model.object.type.Type;
import org.projectodd.jrapidoc.model.param.CookieParam;
import org.projectodd.jrapidoc.model.param.FormParam;
import org.projectodd.jrapidoc.model.param.HeaderParam;
import org.projectodd.jrapidoc.model.param.MatrixParam;
import org.projectodd.jrapidoc.model.param.Param;
import org.projectodd.jrapidoc.model.param.PathParam;
import org.projectodd.jrapidoc.model.param.QueryParam;
import org.projectodd.jrapidoc.model.type.provider.TypeProvider;

public class ResourceClassProcessor {

    TypeProvider typeProvider;

    public ResourceClassProcessor(TypeProvider typeProvider) {
        this.typeProvider = typeProvider;
    }

    /**
     * Main method, creates API model from metadata
     *
     * @param resourceClasses
     * @return
     */
    public ServiceGroup createServiceGroup(Set<ResourceClass> resourceClasses, ServiceGroup.ServiceGroupBuilder serviceGroupBuilder) {
        for (ResourceClass resourceClass : resourceClasses) {
            Logger.info("{0} processing started", resourceClass.getClazz().getCanonicalName());
            Service service = createResource(resourceClass);
            serviceGroupBuilder.service(service);
            Logger.info("{0} processing finished", resourceClass.getClazz().getCanonicalName());
        }
        return serviceGroupBuilder.build();
    }

    void addConsumesParam(Method.MethodBuilder methodBuilder, ResourceClass resourceClass, ResourceMethod resourceMethod) {
        if (resourceMethod.getConsumes() != null) {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(resourceMethod.getConsumes()).setName(HeaderParam.ACCEPT).build();
            methodBuilder.param(param.getType(), param);
        }
        if (resourceClass.getConsumes() != null) {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(resourceClass.getConsumes()).setName(HeaderParam.ACCEPT).build();
            methodBuilder.param(param.getType(), param);
        } else {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(new String[] { "*/*" }).setName(HeaderParam.ACCEPT).build();
            methodBuilder.param(param.getType(), param);
        }
    }

    void addProducesParam(Method.MethodBuilder methodBuilder, ResourceClass resourceClass, ResourceMethod resourceMethod) {
        if (resourceMethod.getProduces() != null) {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(resourceMethod.getProduces()).setName(HeaderParam.CONTENT_TYPE).build();
            methodBuilder.param(param.getType(), param);
        }
        if (resourceClass.getProduces() != null) {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(resourceClass.getProduces()).setName(HeaderParam.CONTENT_TYPE).build();
            methodBuilder.param(param.getType(), param);
        } else {
            Param param = new HeaderParam.HeaderParamBuilder().setOptions(new String[] { "*/*" }).setName(HeaderParam.CONTENT_TYPE).build();
            methodBuilder.param(param.getType(), param);
        }
    }

    Service createResource(ResourceClass resourceClass) {
        Service.ResourceBuilder resourceBuilder = new Service.ResourceBuilder();
        addPath(resourceClass, resourceBuilder);
        addPathExample(resourceClass, resourceBuilder);
        resourceBuilder.description(resourceClass.getDescription());
        addMethods(resourceClass, resourceBuilder);
        addLocatorMethods(resourceClass, resourceBuilder);
        return resourceBuilder.build();
    }

    void addPathExample(ResourceClass resourceClass, Service.ResourceBuilder resourceBuilder) {
        if (StringUtils.isEmpty(resourceClass.getPathExample())) {
            resourceBuilder.pathExample(null);
        } else {
            String pathExample = RestUtil.getPathInModelFormat(resourceClass.getPathExample());
            resourceBuilder.pathExample(pathExample);
        }

    }

    void addPath(ResourceClass resourceClass, Service.ResourceBuilder resourceBuilder) {
        String path = RestUtil.getPathInModelFormat(resourceClass.getPath());
        resourceBuilder.path(path);
    }

    void addLocatorMethods(ResourceClass resourceClass, Service.ResourceBuilder resourceBuilder) {
        for (ResourceLocator resourceLocator : resourceClass.getResourceLocators()) {
            try {
                Logger.info("{0} subresource locator processing started", resourceLocator.getReturnType().getCanonicalName());
                ResourceClass newResourceClass = ResourceBuilder.locatorFromAnnotations(resourceLocator.getReturnType());
                newResourceClass.setPath(resourceLocator.getFullpath());
                newResourceClass.setConstructor(resourceClass.getConstructor());
                ServiceGroup.ServiceGroupBuilder serviceGroupBuilder = new ServiceGroup.ServiceGroupBuilder();
                ServiceGroup locatorSubModel = createServiceGroup(
                        new HashSet<ResourceClass>(Arrays.asList(new ResourceClass[] { newResourceClass })), serviceGroupBuilder);
                for (Service service : locatorSubModel.getServices().values()) {
                    for (Method method : service.getMethods().values()) {
                        resourceBuilder.method(method);
                    }
                }
            } catch (Exception e) {
                Logger.error(e, "Problem during preintrospection of locator class {0}, skipping this class", resourceLocator.getReturnType()
                        .getCanonicalName());
            } finally {
                Logger.info("{0} subresource locator processing finished", resourceLocator.getReturnType().getCanonicalName());
            }
        }
    }

    void addMethods(ResourceClass resourceClass, Service.ResourceBuilder resourceBuilder) {
        for (ResourceMethod resourceMethod : resourceClass.getResourceMethods()) {
            Method method = createMethod(resourceMethod, resourceClass);
            resourceBuilder.method(method);
            for (String httpMethod : resourceMethod.getHttpMethods()) {
                method = method.clone(httpMethod);
                resourceBuilder.method(method);
            }
        }
    }

    Method createMethod(ResourceMethod resourceMethod, ResourceClass resourceClass) {
        Logger.debug("{0} method processing started", resourceMethod.getMethod().toString());
        resourceMethod.messageBodyCheck();
        Method.MethodBuilder methodBuilder = new Method.MethodBuilder();
        methodBuilder.isAsynchronous(resourceMethod.isAsynchronous());
        methodBuilder.description(resourceMethod.getDescription());
        addClassParams(methodBuilder, resourceClass);
        addConsumesParam(methodBuilder, resourceClass, resourceMethod);
        addProducesParam(methodBuilder, resourceClass, resourceMethod);
        addMethodParams(methodBuilder, resourceMethod);
        addPaths(methodBuilder, resourceClass, resourceMethod);
        methodBuilder.parameter(createParameterType(resourceMethod.getParams()));
        removeThisMethodFromResource(resourceMethod, methodBuilder);
        methodBuilder.returnOptions(createReturnOptions(resourceMethod));
        Method method = methodBuilder.build();
        Logger.debug("{0} method processing finished", resourceMethod.getMethod().toString());
        return method;
    }

    void removeThisMethodFromResource(ResourceMethod resourceMethod, Method.MethodBuilder methodBuilder) {
        for (String httpMethod : resourceMethod.getHttpMethods()) {
            methodBuilder.httpMethodType(httpMethod);
            resourceMethod.getHttpMethods().remove(httpMethod);
            break;
        }
    }

    List<Return> createReturnOptions(ResourceMethod resourceMethod) {
        List<Return> returnObjects = new ArrayList<Return>();
        for (ReturnOption returnOption : resourceMethod.getReturnOptions()) {
            returnObjects.add(createReturnOption(returnOption));
        }
        return returnObjects;
    }

    Return createReturnOption(ReturnOption returnOption) {
        List<TransportType> returnTypes = new ArrayList<TransportType>();
        if (returnOption.getReturnClass() != null && !returnOption.getReturnClass().equals(Void.class)) {
            Type returnType = typeProvider.createType(returnOption.getParameterized());
            returnTypes.add(new TransportType.TransportTypeBuilder().type(returnType)
                    .description(StringUtils.isEmpty(returnOption.getTypeDescription()) ? null : returnOption.getTypeDescription()).build());
        }
        List<HeaderParam> headerParams = createReturnHeaders(returnOption.getHeaders());
        List<CookieParam> cookieParams = createReturnCookies(returnOption.getCookies());
        return new Return.ReturnBuilder().httpStatus(returnOption.getStatus()).headerParams(headerParams).cookieParams(cookieParams)
                .returnTypes(returnTypes).description(returnOption.getDescription()).build();
    }

    List<HeaderParam> createReturnHeaders(List<String> headersString) {
        List<HeaderParam> items = new ArrayList<HeaderParam>();
        for (String header : headersString) {
            items.add(new HeaderParam.HeaderParamBuilder().setName(header).setTypeRef(ModelUtil.getSimpleTypeSignature(String.class, null)).build());
        }
        return items;
    }

    List<CookieParam> createReturnCookies(List<String> cookiesString) {
        List<CookieParam> items = new ArrayList<CookieParam>();
        for (String header : cookiesString) {
            items.add(new CookieParam.CookieParamBuilder().setName(header).setTypeRef(ModelUtil.getSimpleTypeSignature(String.class, null)).build());
        }
        return items;
    }

    void addPaths(Method.MethodBuilder methodBuilder, ResourceClass resourceClass, ResourceMethod resourceMethod) {
        String methodPath = RestUtil.getPathInModelFormat(RestUtil.trimSlash(resourceClass.getPath()) + "/"
                + RestUtil.trimSlash(resourceMethod.getPath()));
        methodBuilder.path(methodPath);
        String methodPathExample;
        if (StringUtils.isEmpty(resourceClass.getPathExample())) {
            methodPathExample = RestUtil.trimSlash(resourceClass.getPath());
        } else {
            methodPathExample = RestUtil.trimSlash(resourceClass.getPathExample());
        }
        methodPathExample += "/";
        if (StringUtils.isEmpty(resourceMethod.getPathExample())) {
            methodPathExample += RestUtil.trimSlash(resourceMethod.getPath());
        } else {
            methodPathExample += RestUtil.trimSlash(resourceMethod.getPathExample());
        }
        methodPathExample = RestUtil.getPathInModelFormat(methodPathExample);
        if (methodPathExample.equals(methodPath)) {
            methodBuilder.pathExample(null);
        } else {
            methodBuilder.pathExample(methodPathExample);
        }
    }

    void addMethodParams(Method.MethodBuilder methodBuilder, ResourceMethod resourceMethod) {
        for (MethodParameter methodParameter : resourceMethod.getParams()) {
            if (RestUtil.isHttpParam(methodParameter)) {
                Param param = createParam(methodParameter);
                methodBuilder.param(param.getType(), param);
            }
        }
    }

    void addClassParams(Method.MethodBuilder methodBuilder, ResourceClass resourceClass) {
        List<Parameter> classParams = new ArrayList<Parameter>();
        Collections.addAll(classParams, resourceClass.getFields());
        Collections.addAll(classParams, resourceClass.getConstructor().getParams());
        Collections.addAll(classParams, resourceClass.getSetters());
        for (Parameter parameter : classParams) {
            if (RestUtil.isHttpParam(parameter)) {
                Param param = createParam(parameter);
                methodBuilder.param(param.getType(), param);
            }
        }
    }

    Param createParam(Parameter parameter) {
        Param.ParamBuilder paramBuilder = null;
        if (parameter.getParamType().name().equals(Param.Type.QUERY_PARAM.name())) {
            paramBuilder = new QueryParam.QueryParamBuilder();
        } else if (parameter.getParamType().name().equals(Parameter.ParamType.HEADER_PARAM.name())) {
            paramBuilder = new HeaderParam.HeaderParamBuilder();
        } else if (parameter.getParamType().name().equals(Param.Type.PATH_PARAM.name())) {
            paramBuilder = new PathParam.PathParamBuilder();
        } else if (parameter.getParamType().name().equals(Param.Type.MATRIX_PARAM.name())) {
            paramBuilder = new MatrixParam.MatrixParamBuilder();
        } else if (parameter.getParamType().name().equals(Param.Type.COOKIE_PARAM.name())) {
            paramBuilder = new CookieParam.CookieParamBuilder();
        } else if (parameter.getParamType().name().equals(Param.Type.FORM_PARAM.name())) {
            paramBuilder = new FormParam.FormParamBuilder();
        }
        paramBuilder.setName(parameter.getParamName());
        Type type = createParameterType(parameter);
        paramBuilder.setTypeRef(type.getTypeRef());
        paramBuilder.setDescription(parameter.getDescription());
        paramBuilder.setRequired(parameter.getIsRequired());
        return paramBuilder.build();
    }

    private Type createParameterType(Parameter parameter) {
        if (ModelUtil.isNumericType(parameter.getType())) {
            return typeProvider.createType(Integer.class);
        }
        if (ModelUtil.isBooleanType(parameter.getType())) {
            return typeProvider.createType(Boolean.class);
        }
        return typeProvider.createType(String.class);
    }

    private TransportType createParameterType(MethodParameter[] parameters) {
        for (MethodParameter methodParameter : parameters) {
            if (methodParameter.getParamType().equals(MethodParameter.ParamType.MESSAGE_BODY)) {
                return new TransportType.TransportTypeBuilder().type(typeProvider.createType(methodParameter.getGenericType()))
                        .description(methodParameter.getDescription()).build();
            }
        }
        return null;
    }
}


package io.advantageous.qbit.example.jms;

import io.advantageous.qbit.admin.ManagedServiceBuilder;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.annotation.http.GET;
import io.advantageous.qbit.annotation.http.PUT;
import io.advantageous.qbit.jms.JmsException;
import io.advantageous.qbit.queue.Queue;
import io.advantageous.qbit.queue.SendQueue;
import io.advantageous.qbit.reactive.Reactor;
import io.advantageous.qbit.reactive.ReactorBuilder;
import io.advantageous.qbit.service.BaseService;
import io.advantageous.qbit.service.stats.StatsCollector;
import io.advantageous.qbit.time.Duration;
import io.advantageous.qbit.util.Timer;

import java.util.Optional;

@RequestMapping("/jms")
public class JmsSendService extends BaseService{

    private Optional<Queue<Todo>> queue = Optional.empty();

    private Optional<SendQueue<Todo>> sendQueue = Optional.empty();

    public JmsSendService(final Reactor reactor, final Timer timer, final StatsCollector statsCollector) {
        super(reactor, timer, statsCollector);
        reactor.addRepeatingTask(Duration.ONE_SECOND, () -> {
        });
    }

    /**
     * curl -X PUT -H "Content-Type: application/json" -d '{"name":"deploy app", "done":true}' http://localhost:8080/jms/todo/
     * @param todo todo object
     * @return true or throws an exception
     */
    @PUT(value = "/todo/", code = 202)
    public boolean send(final Todo todo) {
        if (!sendQueue.isPresent()) {
            initSendQueue();
        }
        try {
            sendQueue.ifPresent(todoSendQueue -> todoSendQueue.send(todo));
        } catch (JmsException ex) {
            queue = Optional.empty();
            sendQueue = Optional.empty();
            initSendQueue();
            sendQueue.ifPresent(todoSendQueue -> todoSendQueue.send(todo));
        }
        return true;
    }

    private void initSendQueue() {

        if (!queue.isPresent()) {
             queue = Optional.of(JmsUtil.createQueue());
        }

        queue.ifPresent(todoQueue -> {

            sendQueue = Optional.of(todoQueue.sendQueue());
        });

    }

    @GET("/ping")
    public boolean ping() {
        return true;
    }

    public static void main(final String... args) {

        final ManagedServiceBuilder managedServiceBuilder = ManagedServiceBuilder.managedServiceBuilder().setRootURI("/");

        managedServiceBuilder.addEndpointService(new JmsSendService(
                ReactorBuilder.reactorBuilder().build(),
                Timer.timer(), managedServiceBuilder.getStatServiceBuilder().buildStatsCollector()));

        managedServiceBuilder.getEndpointServerBuilder().build().startServer();
    }

}


/*
 * $#
 * FOS Weka
 *
 * Copyright (C) 2013 Feedzai SA
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka;

import au.com.bytecode.opencsv.CSVReader;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.feedzai.fos.api.Attribute;
import com.feedzai.fos.api.FOSException;
import com.feedzai.fos.api.InstanceType;
import com.feedzai.fos.api.KryoScoringEndpoint;
import com.feedzai.fos.api.Manager;
import com.feedzai.fos.api.Model;
import com.feedzai.fos.api.ModelBinary;
import com.feedzai.fos.api.ModelConfig;
import com.feedzai.fos.api.ModelDescriptor;
import com.feedzai.fos.api.ModelPMML;
import com.feedzai.fos.common.validation.NotBlank;
import com.feedzai.fos.common.validation.NotNull;
import com.feedzai.fos.impl.weka.config.WekaManagerConfig;
import com.feedzai.fos.impl.weka.config.WekaModelConfig;
import com.feedzai.fos.impl.weka.utils.WekaUtils;
import com.feedzai.fos.impl.weka.utils.pmml.PMMLProducers;
import com.feedzai.fos.impl.weka.utils.setter.InstanceSetter;
import com.google.common.base.Optional;
import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weka.classifiers.Classifier;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.feedzai.fos.api.util.ManagerUtils.createModelFile;
import static com.feedzai.fos.api.util.ManagerUtils.getUuid;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * This class implements a manager that is able to train and score
 * using Weka classifiers.
 * <p/>
 * Aditionally, it also implements a Kryo endpoint for scoring to be used along
 * KryoScorer.
 *
 * @author Marco Jorge (marco.jorge@feedzai.com)
 * @author Miguel Duarte (miguel.duarte@feedzai.com)
 */
public class WekaManager implements Manager {
    private final static Logger logger = LoggerFactory.getLogger(WekaManager.class);
    private Thread acceptThread;
    private ServerSocket serverSocket;
    ObjectMapper mapper = new ObjectMapper();

    private Map<UUID, WekaModelConfig> modelConfigs = new HashMap<>();
    private WekaManagerConfig wekaManagerConfig;
    private WekaScorer wekaScorer;
    private KryoScoringEndpoint scorerHandler;

    private volatile boolean acceptThreadRunning = false;

    /**
     * Save dirty configurations to disk.
     * <p/> If saving configuration was not possible, a log is produced but no exception is thrown.
     */
    private synchronized void saveConfiguration() {
        for (WekaModelConfig wekaModelConfig : modelConfigs.values()) {
            if (wekaModelConfig.isDirty() && wekaModelConfig.getModelConfig().isStoreModel()) {
                try {
                    String modelConfigJson = mapper.writeValueAsString(wekaModelConfig.getModelConfig());

                    // create a new file because this model has never been written
                    if (wekaModelConfig.getHeader() == null) {
                        File file = File.createTempFile(wekaModelConfig.getId().toString(), "." + WekaManagerConfig.HEADER_EXTENSION, wekaManagerConfig.getHeaderLocation());
                        wekaModelConfig.setHeader(file);
                    }

                    FileUtils.write((wekaModelConfig).getHeader(), modelConfigJson);
                    wekaModelConfig.setDirty(false /* contents have been updated so the model is no longer dirty*/);
                } catch (IOException e) {
                    logger.error("Could not store configuration for model '{}' (will continue to save others)", wekaModelConfig.getId(), e);
                }
            }
        }
    }

    /**
     * Create a new manager from the given configuration.
     * <p/> Will lookup any headers files and to to instantiate the model.
     * <p/> If a model fails, a log is produced but loading other models will continue (no exception is thrown).
     *
     * @param wekaManagerConfig the manager configuration
     */
    public WekaManager(WekaManagerConfig wekaManagerConfig) {
        checkNotNull(wekaManagerConfig, "Manager config cannot be null");

        this.wekaManagerConfig = wekaManagerConfig;

        Collection<File> headers = FileUtils.listFiles(wekaManagerConfig.getHeaderLocation(), new String[]{WekaManagerConfig.HEADER_EXTENSION}, true);
        for (File header : headers) {
            logger.trace("Reading model file '{}'", header);

            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(header);
                String modelConfigJson = IOUtils.toString(fileInputStream);

                ModelConfig modelConfig = mapper.readValue(modelConfigJson, ModelConfig.class);
                WekaModelConfig wekaModelConfig = new WekaModelConfig(modelConfig, wekaManagerConfig);
                wekaModelConfig.setHeader(header);
                wekaModelConfig.setDirty(false /* not changed so far */);

                if (modelConfigs.containsKey(wekaModelConfig.getId())) {
                    logger.error("Model with ID '{}' is duplicated in the configuration (the configuration from '{}' is discarded)", wekaModelConfig.getId(), header.getAbsolutePath());
                } else {
                    modelConfigs.put(wekaModelConfig.getId(), wekaModelConfig);
                }
            } catch (Exception e) {
                logger.error("Could not load from '{}' (continuing to load others)", header, e);
            } finally {
                IOUtils.closeQuietly(fileInputStream);
            }
        }

        this.wekaScorer = new WekaScorer(modelConfigs, wekaManagerConfig);

        try {
            int port = wekaManagerConfig.getScoringPort();
            this.serverSocket = new ServerSocket(port);
            serverSocket.setReuseAddress(true);
            final int max_threads = wekaManagerConfig.getMaxSimultaneousScoringThreads();
            Runnable acceptRunnable = new Runnable() {
                ExecutorService executor = Executors.newFixedThreadPool(max_threads);

                @Override
                public void run() {
                    acceptThreadRunning = true;
                    try {
                        while (acceptThreadRunning &&
                                Thread.currentThread().isInterrupted() == false) {
                            Socket client = serverSocket.accept();
                            client.setTcpNoDelay(true);
                            scorerHandler = new KryoScoringEndpoint(client, wekaScorer);
                            executor.submit(scorerHandler);
                        }
                    } catch (IOException e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            };
            acceptThread = new Thread(acceptRunnable);
            acceptThread.start();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    @Override
    public synchronized UUID addModel(ModelConfig config, Model model) throws FOSException {
        File modelFile;

        try {
            modelFile = createModelFile(wekaManagerConfig.getHeaderLocation(), getUuid(config), model);
        } catch (IOException e) {
            throw new FOSException("Unable to create model file", e);
        }

        return addModel(config, getModelDescriptor(model, modelFile));
    }

    @Override
    public synchronized UUID addModel(ModelConfig config, @NotBlank ModelDescriptor descriptor) throws FOSException {
        UUID uuid = getUuid(config);

        WekaModelConfig wekaModelConfig = new WekaModelConfig(config, wekaManagerConfig);
        wekaModelConfig.setId(uuid);
        wekaModelConfig.setModelDescriptor(descriptor);

        modelConfigs.put(uuid, wekaModelConfig);
        wekaScorer.addOrUpdate(wekaModelConfig);

        saveConfiguration();
        logger.debug("Model {} added", uuid);
        return uuid;
    }

    @Override
    public synchronized void removeModel(UUID modelId) throws FOSException {
        WekaModelConfig wekaModelConfig = modelConfigs.remove(modelId);
        if (wekaModelConfig == null) {
            logger.warn("Could not remove model with id {} because it does not exists", modelId);
            return;
        }
        wekaScorer.removeModel(modelId);

        if (wekaModelConfig.getModelConfig().isStoreModel()) {

            // delete the header & model file (or else it will be picked up on the next restart)
            wekaModelConfig.getHeader().delete();
            // only delete if is in our header location
            if (!wekaManagerConfig.getHeaderLocation().toURI().relativize(wekaModelConfig.getModel().toURI()).isAbsolute()) {
                wekaModelConfig.getModel().delete();
            }
        }
        logger.debug("Model {} removed", modelId);
    }

    @Override
    public synchronized void reconfigureModel(UUID modelId, ModelConfig modelConfig) throws FOSException {
        WekaModelConfig wekaModelConfig = this.modelConfigs.get(modelId);
        wekaModelConfig.update(modelConfig);

        wekaScorer.addOrUpdate(wekaModelConfig);
        saveConfiguration();
        logger.debug("Model {} reconfigured", modelId);
    }

    @Override
    public synchronized void reconfigureModel(UUID modelId, ModelConfig modelConfig, Model model) throws FOSException {
        try {
            File modelFile = createModelFile(wekaManagerConfig.getHeaderLocation(), modelId, model);

            WekaModelConfig wekaModelConfig = this.modelConfigs.get(modelId);
            wekaModelConfig.update(modelConfig);
            ModelDescriptor descriptor = getModelDescriptor(model, modelFile);
            wekaModelConfig.setModelDescriptor(descriptor);

            wekaScorer.addOrUpdate(wekaModelConfig);
            saveConfiguration();
            logger.debug("Model {} reconfigured", modelId);
        } catch (IOException e) {
            throw new FOSException(e);
        }
    }

    @Override
    public synchronized void reconfigureModel(UUID modelId, ModelConfig modelConfig, @NotBlank ModelDescriptor descriptor) throws FOSException {
        File file = new File(descriptor.getModelFilePath());

        WekaModelConfig wekaModelConfig = this.modelConfigs.get(modelId);
        wekaModelConfig.update(modelConfig);
        wekaModelConfig.setModelDescriptor(descriptor);

        wekaScorer.addOrUpdate(wekaModelConfig);
        saveConfiguration();
    }

    @Override
    @NotNull
    public synchronized Map<UUID, ModelConfig> listModels() {
        Map<UUID, ModelConfig> result = new HashMap<>(modelConfigs.size());
        for (Map.Entry<UUID, WekaModelConfig> entry : modelConfigs.entrySet()) {
            result.put(entry.getKey(), entry.getValue().getModelConfig());
        }

        return result;
    }

    @Override
    @NotNull
    public WekaScorer getScorer() {
        return wekaScorer;
    }

    @Override
    public synchronized UUID trainAndAdd(ModelConfig config, List<Object[]> instances) throws FOSException {
        Model trainedModel = train(config, instances);
        return addModel(config, trainedModel);
    }

    @Override
    public synchronized UUID trainAndAddFile(ModelConfig config, String path) throws FOSException {
        Model trainedModel = trainFile(config, path);
        return addModel(config, trainedModel);
    }

    @Override
    public Model train(ModelConfig config, List<Object[]> instances) throws FOSException {
        checkNotNull(instances, "Instances must be supplied");
        checkNotNull(config, "Config must be supplied");
        long time = System.currentTimeMillis();
        WekaModelConfig wekaModelConfig = new WekaModelConfig(config, wekaManagerConfig);
        Classifier classifier = WekaClassifierFactory.create(config);
        FastVector attributes = WekaUtils.instanceFields2Attributes(wekaModelConfig.getClassIndex(), config.getAttributes());
        InstanceSetter[] instanceSetters = WekaUtils.instanceFields2ValueSetters(config.getAttributes(), InstanceType.TRAINING);

        Instances wekaInstances = new Instances(config.getProperty(WekaModelConfig.CLASSIFIER_IMPL), attributes, instances.size());

        for (Object[] objects : instances) {
            wekaInstances.add(WekaUtils.objectArray2Instance(objects, instanceSetters, attributes));
        }

        trainClassifier(wekaModelConfig.getClassIndex(), classifier, wekaInstances);

        final byte[] bytes = SerializationUtils.serialize(classifier);

        logger.debug("Trained model with {} instances in {}ms", instances.size(), (System.currentTimeMillis() - time));

        return new ModelBinary(bytes);
    }

    @Override
    public double[] featureImportance(UUID uuid, Optional<List<Object[]>> instances, long seed) throws FOSException {
        throw new FOSException("FOS Weka implementation does not support feature importance");
    }

    @Override
    public Model trainFile(ModelConfig config, String path) throws FOSException {
        checkNotNull(path, "Config must be supplied");
        checkNotNull(path, "Path must be supplied");

        long time = System.currentTimeMillis();
        WekaModelConfig wekaModelConfig = new WekaModelConfig(config, wekaManagerConfig);
        Classifier classifier = WekaClassifierFactory.create(config);
        List<Attribute> attributeList = config.getAttributes();
        FastVector attributes = WekaUtils.instanceFields2Attributes(wekaModelConfig.getClassIndex(), config.getAttributes());
        InstanceSetter[] instanceSetters = WekaUtils.instanceFields2ValueSetters(config.getAttributes(), InstanceType.TRAINING);

        List<Instance> instances = new ArrayList();

        String[] line;
        try {
            FileReader fileReader = new FileReader(path);
            CSVReader csvReader = new CSVReader(fileReader);
            while ((line = csvReader.readNext()) != null) {
                // parsing is done by InstanceSetter's
                instances.add(WekaUtils.objectArray2Instance(line, instanceSetters, attributes));
            }

        } catch (Exception e) {
            throw new FOSException(e.getMessage(), e);
        }

        Instances wekaInstances = new Instances(config.getProperty(WekaModelConfig.CLASSIFIER_IMPL), attributes, instances.size());

        for (Instance instance : instances) {
            wekaInstances.add(instance);
        }

        trainClassifier(wekaModelConfig.getClassIndex(), classifier, wekaInstances);

        final byte[] bytes = SerializationUtils.serialize(classifier);
        logger.debug("Trained model with {} instances in {}ms", instances.size(), (System.currentTimeMillis() - time));

        return new ModelBinary( bytes);
    }

    /**
     * Will save the configuration to file.
     *
     * @throws FOSException when there are IO problems writing the configuration to file
     */
    @Override
    public synchronized void close() throws FOSException {
        acceptThreadRunning = false;
        if (scorerHandler != null) {
            scorerHandler.close();
        }

        IOUtils.closeQuietly(serverSocket);
        saveConfiguration();
    }

    /**
     * Returns a new {@link com.feedzai.fos.api.ModelDescriptor} for the given {@code model} and {@code file}.
     *
     * @param model     The {@link Model} with the classifier.
     * @param modelFile The file where the model will be saved.
     * @return          A new {@link com.feedzai.fos.api.ModelDescriptor}
     * @throws FOSException If the given {@code model} is of an unknown instance.
     */
    private ModelDescriptor getModelDescriptor(Model model, File modelFile) throws FOSException {
        if (model instanceof ModelBinary) {
            return new ModelDescriptor(ModelDescriptor.Format.BINARY, modelFile.getAbsolutePath());
        } else if (model instanceof ModelPMML) {
            return new ModelDescriptor(ModelDescriptor.Format.PMML, modelFile.getAbsolutePath());
        } else {
            throw new FOSException("Unsupported Model type '" + model.getClass().getSimpleName() + "'.");
        }
    }

    /**
     * Trains the given {@code classifier} using the given {@link com.feedzai.fos.impl.weka.config.WekaModelConfig modelConfig}
     * and {@link weka.core.Instances wekaInstances}.
     *
     * @param classIndex    The index of the class.
     * @param classifier    The classifier to be trained.
     * @param wekaInstances The training instances.
     * @throws FOSException If it fails to train the classifier.
     */
    private void trainClassifier(int classIndex, Classifier classifier, Instances wekaInstances) throws FOSException {
        wekaInstances.setClassIndex(classIndex == -1 ? wekaInstances.numAttributes() - 1 : classIndex);

        try {
            classifier.buildClassifier(wekaInstances);
        } catch (Exception e) {
            throw new FOSException(e.getMessage(), e);
        }
    }

    @Override
    public void save(UUID uuid, String savepath) throws FOSException {
        try {
            File source = modelConfigs.get(uuid).getModel();
            File destination = new File(savepath);
            Files.copy(source, destination);
        } catch (Exception e) {
            throw new FOSException("Unable to save model " + uuid + " to " + savepath, e);
        }
    }

    @Override
    public void saveAsPMML(UUID uuid, String saveFilePath, boolean compress) throws FOSException {
        Classifier classifier = wekaScorer.getClassifier(uuid);

        File target = new File(saveFilePath);

        PMMLProducers.produce(classifier, target, compress);
    }
}


/*
 * $#
 * FOS Weka
 *
 * Copyright (C) 2013 Feedzai SA
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka.utils.pool;

import com.feedzai.fos.api.FOSException;
import com.feedzai.fos.api.ModelDescriptor;
import com.feedzai.fos.common.validation.NotNull;
import com.feedzai.fos.impl.weka.exception.WekaClassifierException;
import com.feedzai.fos.impl.weka.utils.Cloner;
import com.feedzai.fos.impl.weka.utils.pmml.PMMLConsumers;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.pool.BasePoolableObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import weka.classifiers.Classifier;

import java.io.*;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * The Classifier Factory.
 * This factory is used to backup the {@link org.apache.commons.pool.ObjectPool} object life cycle.
 *
 * @author Rafael Marmelo
 */
public class ClassifierFactory extends BasePoolableObjectFactory<Classifier> {
    private static final Logger logger = LoggerFactory.getLogger(ClassifierFactory.class);

    /**
     * The {@link ModelDescriptor} representing the classifier.
     */
    private ModelDescriptor modelDescriptor;

    /**
     * Create a new classifier factory using the given cloner.
     *
     * @param modelDescriptor The {@link ModelDescriptor} representing the classifier.
     */
    public ClassifierFactory(ModelDescriptor modelDescriptor) {
        checkNotNull(modelDescriptor, "Model descriptor cannot be null");
        checkNotNull(modelDescriptor.getModelFilePath(), "The path cannot be null");

        File modelFile = new File(modelDescriptor.getModelFilePath());

        checkArgument(modelFile.exists(), "The model file must exist");
        checkArgument(modelFile.canRead(), "The model file must be readable.");

        this.modelDescriptor = modelDescriptor;
    }

    /**
     * Make on object of the factory type.
     * <p>
     * Reads the {@link Classifier} from file.
     *
     * @return a new classifier instance
     * @throws WekaClassifierException when the object could not be read from file.
     */
    @Override
    @NotNull
    public Classifier makeObject() throws WekaClassifierException {
        logger.debug("Creating classifier");

        File file = new File(modelDescriptor.getModelFilePath());

        checkArgument(file.exists(), "Source file '"+ file.getAbsolutePath() + "' must exist");

        long time = System.currentTimeMillis();

        switch (modelDescriptor.getFormat()) {
            case BINARY:
                try (FileInputStream fileInputStream = new FileInputStream(file);
                     BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 1024 * 1024);
                     ObjectInputStream objectInputStream = new ObjectInputStream(bufferedInputStream)) {
                    Classifier classifier = (Classifier) objectInputStream.readObject();
                    logger.debug("Loaded binary model {} in {} ms.", file.getPath(), (System.currentTimeMillis() - time));
                    return classifier;
                } catch (IOException | ClassNotFoundException e) {
                    throw new WekaClassifierException(e);
                }
            case PMML:
                try {
                    Classifier classifier = PMMLConsumers.consume(file);
                    logger.debug("Loaded PMML model {} in {} ms.", file.getPath(), (System.currentTimeMillis() - time));
                    return classifier;
                } catch (FOSException e) {
                    throw new WekaClassifierException("Failed to consume PMML file " + file.getAbsolutePath() + ".", e);
                }
            default:
                throw new WekaClassifierException("Unknown model type " + modelDescriptor.getFormat());
        }
    }
}


package io.advantageous.qbit.eventbus;

import io.advantageous.qbit.QBit;
import io.advantageous.qbit.annotation.QueueCallback;
import io.advantageous.qbit.annotation.QueueCallbackType;
import io.advantageous.qbit.events.EventManager;
import io.advantageous.qbit.events.impl.SimpleEventConnector;
import io.advantageous.qbit.events.spi.EventConnector;
import io.advantageous.qbit.events.spi.EventTransferObject;
import io.advantageous.qbit.util.MultiMap;

/**
 * @author Rick Hightower
 */
public class EventRemoteReplicatorService implements EventConnector {

    private final EventConnector eventConnector;

    public EventRemoteReplicatorService(final EventManager eventManager) {
        this.eventConnector = new SimpleEventConnector(eventManager);
    }

    public EventRemoteReplicatorService(final EventConnector eventConnector) {
        this.eventConnector = eventConnector;
    }

    public EventRemoteReplicatorService() {
        this.eventConnector = new SimpleEventConnector(QBit.factory().systemEventManager());
    }

    /**
     * This message receives an event from a remote call.
     */
    @Override
    public void forwardEvent(final EventTransferObject<Object> event) {

        eventConnector.forwardEvent(new EventTransferObject<Object>() {
            @Override
            public String channel() {
                return event.channel();
            }

            @Override
            public long id() {
                return event.id();
            }

            @Override
            public Object body() {
                return event.body();
            }

            @Override
            public boolean isSingleton() {
                return true;
            }

            @Override
            public MultiMap<String, String> params() {
                return event.params();
            }

            @Override
            public MultiMap<String, String> headers() {
                return event.headers();
            }

            /* Save a map lookup or building a header map. */
            @Override
            public boolean wasReplicated() {
                return true;
            }

            @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
            @Override
            public boolean equals(Object obj) {
                //noinspection EqualsBetweenInconvertibleTypes
                return event.equals(obj);
            }

            @Override
            public int hashCode() {
                return event.hashCode();
            }
        });
    }

    @QueueCallback({QueueCallbackType.EMPTY, QueueCallbackType.LIMIT})
    void flushConnector() {
        eventConnector.flush();
    }
}


package org.jboss.resteasy.util;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @author Bill Burke
 * @version $Revision: 1 $
 */
@SuppressWarnings("unchecked")
public final class FindAnnotation {

    /**
    *
    */
    private static final Class<? extends Annotation>[] JAXRS_ANNOTATIONS = (Class<? extends Annotation>[]) new Class[] { QueryParam.class,
            HeaderParam.class, CookieParam.class, PathParam.class, MatrixParam.class, Context.class };

    private static final Class[] findJaxRSAnnotations_TYPE = new Class[] {};

    private FindAnnotation() {
    }

    /**
     *
     * @param <T>
     * @param searchList
     * @param annotation
     * @return
     */
    public static <T> T findAnnotation(Annotation[] searchList, Class<T> annotation) {
        if (searchList == null)
            return null;
        for (Annotation ann : searchList) {
            if (ann.annotationType().equals(annotation)) {
                return (T) ann;
            }
        }
        return null;
    }

    public static Class<? extends Annotation>[] findJaxRSAnnotations(Annotation[] searchList) {

        LinkedList<Class<? extends Annotation>> result = new LinkedList<Class<? extends Annotation>>();

        for (Class<? extends Annotation> clazz : JAXRS_ANNOTATIONS) {

            if (findAnnotation(searchList, clazz) != null)
                result.add(clazz);

        }

        return result.toArray(findJaxRSAnnotations_TYPE);

    }

    /**
     * Returns an array of annotations the specified method of a resource class.
     *
     * @param method
     * @return
     */
    public static Annotation[] getResourcesAnnotations(Method method) {
        Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>();
        for (Annotation annotation : method.getDeclaringClass().getAnnotations()) {
            annotations.put(annotation.getClass(), annotation);
        }
        for (Annotation annotation : method.getAnnotations()) {
            annotations.put(annotation.getClass(), annotation);
        }
        return annotations.values().toArray(new Annotation[annotations.size()]);
    }

    /**
     * Look for an annotation in a list of annotations. If not there, see if it
     * is on the type provided
     *
     * @param type
     * @param annotations
     * @param annnotation
     * @return
     */
    public static <T extends Annotation> T findAnnotation(Class<?> type, Annotation[] annotations, Class<T> annotation) {
        T config = FindAnnotation.findAnnotation(annotations, annotation);
        if (config == null) {
            config = type.getAnnotation(annotation);
        }
        return config;
    }

}