ClassNotFoundException Error when publish position to Rabbitmq

Berna Maxim2 years ago

Hi Anton,

I want to use Rabbitmq to publish position from Traccar.

I know that there is a forward url feature in Traccar, but the protocol is HTTP, while what I need is AMQP.

This my code:

build.gradle

...
implementation "com.rabbitmq:amqp-client:5.15.0"
...

config/keys.java

...
public static final ConfigKey<String> RABBIT_URL = new StringConfigKey(
            "rabbit.url",
            List.of(KeyType.CONFIG));
...

database/RabbitManager.java

package org.traccar.database;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.Device;
import org.traccar.model.Position;
import org.traccar.session.cache.CacheManager;

import javax.inject.Inject;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class RabbitManager {
    private final Config config;

    private ConnectionFactory factory;
    private Connection connection;
    private Channel channel;

    private String url;

    private static final String QUEUE_NAME = "traccar";

    private static final String KEY_POSITION = "position";
    private static final String KEY_DEVICE = "device";

    @Inject
    private ObjectMapper objectMapper;
    @Inject
    private CacheManager cacheManager;

    private static final Logger LOGGER = LoggerFactory.getLogger(RabbitManager.class);

    public RabbitManager(Config config) {
        this.config = config;

        initConnection();
    }

    public void initConnection() {
        factory = new ConnectionFactory();
        url = config.getString(Keys.RABBIT_URL);

        try {
            factory.setUri(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        try {
            connection = factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        try {
            channel = connection.createChannel();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void publish(Position position) {
        try {
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Map<String, Object> data = new HashMap<>();
        Device device = cacheManager.getObject(Device.class, position.getDeviceId());

        data.put(KEY_POSITION, position);

        if (device != null) {
            data.put(KEY_DEVICE, device);
        }

        String json = null;
        try {
            json = objectMapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        try {
            channel.basicPublish("", QUEUE_NAME, new AMQP.BasicProperties.Builder()
                    .deliveryMode(2).build(), json.getBytes("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        LOGGER.debug(json);
    }
}

RabbitDataHandler.java

package org.traccar;

import io.netty.channel.ChannelHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.database.RabbitManager;
import org.traccar.model.Position;

import javax.inject.Inject;

@ChannelHandler.Sharable
public class RabbitDataHandler extends BaseDataHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RabbitDataHandler.class);

    @Inject
    private RabbitManager rabbitManager;

    @Override
    protected Position handlePosition(Position position) {

        try {
            rabbitManager.publish(position);
        } catch (Exception error) {
            LOGGER.error("error forward position to Rabbit", error);
        }

        return position;
    }
}

BasePipelineFactory.java

...
DriverEventHandler.class,
MainEventHandler.class,
RabbitDataHandler.class);
...

MainModule.java

...
@Singleton
    @Provides
    public static RabbitManager provideRabbitManager(Config config) {
        if (config.hasKey(Keys.RABBIT_URL)) {
            return new RabbitManager(config);
        }
        return null;
    }
...

I was build from the latest commit on Traccar Master (v5.3)

Edit: /opt/traccar/conf/traccar.xml

<entry key='rabbit.url'>amqp://[USERNAME]:[PASSWORD]@[IPADDRESS]:5672</entry>

Pull from Github:

git pull https://github.com/traccar/traccar.git

Then, I do:

sudo systemctl stop traccar
./gradlew assemble
sudo cp target/tracker-server.jar /opt/traccar
sudo systemctl start traccar

When I checked on /opt/traccar/logs/tracker-server.log, I found:


2022-08-13 22:16:17  INFO: Successfully acquired change log lock
2022-08-13 22:16:17  INFO: Cannot load service: liquibase.parser.ChangeLogParser: liquibase.parser.core.json.JsonChangeLogParser Unable to get public no-arg constructor
2022-08-13 22:16:17  INFO: Cannot load service: liquibase.parser.ChangeLogParser: liquibase.parser.core.yaml.YamlChangeLogParser Unable to get public no-arg constructor
2022-08-13 22:16:17  INFO: Reading from traccar.DATABASECHANGELOG
2022-08-13 22:16:21  INFO: Reading from traccar.DATABASECHANGELOG
2022-08-13 22:16:21  INFO: Cannot load service: liquibase.hub.HubService: Provider liquibase.hub.core.StandardHubService could not be instantiated
2022-08-13 22:16:21  INFO: Successfully released change log lock
2022-08-13 22:16:22  INFO: Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system instability.
2022-08-13 22:16:22  WARN: Failed to initialize a channel. Closing: [id: 0x9c990ba0] - com.rabbitmq.client.ConnectionFactory - ClassNotFoundException (... < RabbitManager:54 < *:50 < MainModule:345 < < gener:-1 < ...)
2022-08-13 22:16:22 ERROR: Main method error - StacklessClosedChannelException (...)

I've read the documentation of Build Traccar. It says: It will automatically download all dependencies and generate tracker-server.jar in the target subfolder
But, why it cannot found my dependency (com.rabbit.client).

Thank you

Rama Rahardia year ago

Hi,
I think it has something to do with missing amqp-client.jar file ('amqp-client-5.18.0.jar') on {working_dir}/lib/ directory

If you're using Windows, try to copy the jar file to C:\Program Files\Traccar\lib