Search code examples
javarestosgiactivemq-classicosgi-bundle

How to connect OSGI bundles (Send entity though bundles)?


I have a REST service, which contains three classes in one module(bundle) User.java -> Entity UserService.java -> REST service UserValidation.java -> Special validator for the entity. Server send entity to this validator and get validation result (true or false):

User.java

@XmlRootElement(name = "User")
public class User {
    private long id;
    private String name;
    private String surname;
    private String patronymic;

    /*Getters and Setters*/
}

UserService.java

public class UserServiceImpl implements UserService {

    private UserDAO userDbDao = new UserDatabaseDAO();

    @POST
    @Path("/users/")
    public Response addUser(User user) {
        UserValidator userValidator = new UserValidator(user);
        if (userValidator.isValid()) {
            User newUser = userDbDao.createUser(user);
            return Response.ok().type("application/xml").entity(newUser).build();
        } else {
            return Response.status(Response.Status.BAD_REQUEST).entity(userValidator.getErrorMessage()).build();
        }
    }
}

UserValidator.java

public class UserValidator {
    private static final int MAX_SIZE_NAME = 50;
    private static final int MIN_SIZE_NAME = 2;
    private User user;

    public UserValidator(User user) {
        this.user = user;
    }

    private BadUserResponse badUserResponse = new BadUserResponse();

    private boolean isNameValid(String name) {
        if (name == null) {
            badUserResponse.setNsp("Null in fields name/surname/patronymic");
            return false;
        }
        String tempName = name.trim();
        if (tempName.length() < MIN_SIZE_NAME || tempName.length() > MAX_SIZE_NAME) {
            badUserResponse.setNsp(String.format("Fields name/surname/patronymic too long or too short (Allowed length from %d to %d)", MIN_SIZE_NAME, MAX_SIZE_NAME));
            return false;
        }
        for (int i = 0; i < tempName.length(); i++) {
            if (!Character.isLetter(tempName.charAt(i))) {
                badUserResponse.setNsp("Fields name/surname/patronymic contains wrong symbols (Only letters allowed)");
                return false;
            }
        }
        return true;
    }
    public boolean isValid() {
        return (isNameValid(user.getName()) &
                isNameValid(user.getSurname()) &
                isNameValid(user.getPatronymic()));
    }

    public BadUserResponse getErrorMessage() {
        return badUserResponse;
    }

BadUserResponse.java

@XmlRootElement(name="baduserresponce")
public class BadUserResponse {
    private String nsp;

    public String getNsp() {
        return nsp;
    }

    public void setNsp(String nsp) {
        this.nsp = nsp;
    }
}

But now, I need to split this into separate bundles. Because, as you can see, they uses functionality of each other. For example UserService.java just used this UserValidator userValidator = new UserValidator(user);

I need to connect these bundles somehow (OSGI Service, ActiveMQ).

In my opinion it works something like this:

  1. UserService bundle get User entity from REST method.
  2. Put all of the User fields (name, surname, patronymic) to ActiveMQ queue (because UserValidator bundle don't know what's is User entity).
  3. UserValidator bundle get User's fiels from queue and validate them.
  4. UserValidator bundle put validation result (true/false) to queue.
  5. UserService bundle get validation result from queue and send User to DAO.

But this is just a concept. Am I wrong?

What's the best way to pass entity though bundles and how should I do this?


Solution

  • You current way of simply initiating the UserValidator via new is technically fine even if they live in different bundles. If your validator is only needed in this place and is simple I would even leave it in the same bundle.

    The other options can make sense to decouple your bundles. Using messaging allows you to avoid sync calls. It can also be use to send the data to a remote machine. JMS messaging is quite heavy weight though. You need a broker and depend on the API. In your case you also directly need the result of the validation. So you would simulate a sync call with JMS. So I would rather avoid this.

    Using an OSGi service allows you to decouple from the implementation of the service. In this case it makes sense to create an interface for UserValidator. I would also put this interface into a separate bundle. You then need to register the service in the bundle that implements the validator and bind the service in the bundle that uses the validator. OSGi services are very light weight and by default synchronous. So I think they would fit your problem well. For registering and binding services do not use the OSGi API directly. Instead use declarative services with annotations. They take away most of the complexity in dealing with OSGi services.

    Btw. I am not sure how you do REST. I suggest to have a look at the Aries JAX-RS Whiteboard.