how to save session infos with room Id in redis, spring boot, kurento? - spring

I want to implement scalable signaling server. I use kurento client, kurento media server to implement N:M group call with spring boot signaling server.
I understand the tutorial docs, and checked it works well.
However, I have to consider scale-out problems in my project, so I thought it will be nice to save session info in redis so that multiple was can share room info, sessions info.
But I have no idea to save MediaPipeline object or the object info to redis.
Is it possible to use jpa with redis?
this is Room class
public class Room implements Closeable {
private final Logger log = LoggerFactory.getLogger(Room.class);
private final ConcurrentMap<Long, UserSession> sessions = new ConcurrentHashMap<>();
private final MediaPipeline pipeline;
private final Long roomId;
this is UserSession class
public class UserSession implements Closeable {
private final Logger log = LoggerFactory.getLogger(UserSession.class);
private final Participant participant;
private final WebSocketSession session;
private final MediaPipeline pipeline;
private final Long roomId;
private final WebRtcEndpoint outgoingMedia;
private final ConcurrentMap<Long, WebRtcEndpoint> incomingMedia = new ConcurrentHashMap<>();
I want my backend servers to share user session info and room info with media pipeline, but I don't know how to save these info to redis. Any ideas?

Related

Spring Data REST - prevent property edit based on role

I use Spring Boot with Spring Data REST for data access and Spring Security for access restriction.
Assume I've got simple entity:
#Entity
public class Person {
#Id #GeneratedValue
private Long id;
private String firstName;
private Boolean isAuthorizedForClasifiedData;
}
I've got two roles in the application: USER and ADMIN.
Is there any simple way to prevent USER from changing value of isAuthorizedForClasifiedData while allowing ADMIN to update it?
The only solution I came up with is writing your own setter method.
public void setIsAuthorizedForClasifiedData(Boolean isAuthorizedForClasifiedData) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Optional<? extends GrantedAuthority> role_admin = authentication.getAuthorities().stream().filter(role -> role.getAuthority().equals("ROLE_ADMIN")).findAny();
role_admin.orElseThrow(() -> new YourOwnForbiddenException());
this.test = test;
}

Service cannot auto-wire in Entity class

I needed a RoleMappingService class(which is annotated by #Service) object into a Employee class (which is annotated by #Entity)
below are my classes
********************* RoleMappingsService class **********************
#Service
public class RoleMappingsService {
#Autowired
RolesMappingDao rolesMappingDao;
public List<RolesMappings> getRolesMappingByauthSystemRole(String authSystemRole) {
return rolesMappingDao.getRolesMappingByauthSystemRole(authSystemRole);
}
}
############### Employee class
#Configurable
#Component
#Entity
#NamedQuery(name = "Employee.findAll", query = "SELECT e FROM Employee e")
public class Employee implements Serializable, UserDetails {
#Autowired
#Transient
RoleMappingsService roleMappingsService;
public static final String STATUS_ACTIVE = "ACTIVE";
public static final String STATUS_INACTIVE = "INACTIVE";
public static final String STATUS_LOCKED = "LOCKED";
public static final String STATUS_ONLEAVE = "ONLEAVE";
public static final String STATUS_EXPIRED = "EXPIRED";
private static final long serialVersionUID = 1L;
#Id
#Column(name = "emp_id")
private String empId;
#Column(name = "emp_password")
private String empPassword;
#Column(name = "emp_email")
private String empEmail;
#Column(name = "emp_address")
private String empAddress;
#Column(name = "emp_age")
private int empAge;
#Column(name = "emp_firstname")
private String empFirstname;
}
Here Autowire is not working for roleMappingsService and the object is always found null. However I tried to autowire same object in some other service and there Autowire is perfectly working.
( I know Entity class is only used for representing database table but in my case I need to set some field values which depend on another table so need to fetch data using service)
JB Nizet is totally right
I'll try to provide more explanations here.
Spring can Autowire only beans, objects that it manages, and not arbitrary objects.
Entities are usually created from within a JPA (Hibernate) and are not something that you want to manage by Spring.
There is a related discussion available here but bottom line you should never do something like this.
Why not?
Here are a couple of questions/reasons:
Maybe these entities will go outside spring context at all (serialization), what should that reference contain? Should we also serialize the service? How?
What will happen if the method that turns to the service will be called "outside" the spring driven application (maybe even in different JVM)?
If there are, say 1000 objects returned by that query, do you really want all of them to reside in application context? Or maybe should they be of "prototype" scope?
As you see, it doesn't play nice with spring concepts. I think the reason for it is that Hibernate and JPA do not "support" an idea of methods inside entities, it's just a different framework. I know there are other frameworks that do allow such a concept, but Hibernate/JPA just doesn't, period
So instead of trying to inject the service into the entity bean, probably you should redesign the application so that the service method will be called from outside, maybe via some facade, and entities will be just populated by query, and then "enriched" with additional information if we're talking about SELECT queries, or, alternatively, some information should be pre-set on entity objects, generated by the Business Logic Layer and only then the entity object should be stored in DB

Spring data mongodb #DBRef list

I am trying to have a list in a model using #DBRef but I can't get it to work.
This is my User model:
#Data
#Document
public class User {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#Indexed(unique = true)
#NotBlank
private String email;
#NotBlank
private String name;
#NotBlank
private String password;
#DBRef
private List<Server> servers;
}
Server model:
#Data
#Document
public class Server {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#NotBlank
private String name;
#NotBlank
private String host;
}
The structure is very simple, every user can have multiple servers. But when I add servers to the user the server is created, but the servers array contains one null entry("servers" : [ null ]). So the server isn't added to the user. This is how I create a server and add it to an user:
#PostMapping
public Mono create(#Valid #RequestBody Server server, Mono<Authentication> authentication) {
return this.serverRepository.save(server).then(authentication.flatMap(value -> {
User user = (User) value.getDetails();
user.getServers().add(server);
return userRepository.save(user);
})).map(value -> server);
}
So I simply create and save a server, add the server the user and then save the user. But it doesn't work. I keep having an array with one null entry.
I've seen this page: http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb. But it is for saving the child document, not for linking it. Also it is for a single document, not for an array or list.
Why is my list not being saved correctly?
All my libraries are coming from spring boot version 2.0.0.M6.
UPDATE
When removing #DBRef from the user's servers property the servers are getting saved, but they of course get double created, in the server collection and in every user.servers. So the error has something to do with references.
After some googling I found the answer...
https://jira.spring.io/browse/DATAMONGO-1583
https://jira.spring.io/browse/DATAMONGO-1584
Reactive mongo doesn't support this.
Actually there is a way to resolve DbRefs without to using the blocking driver. Yes - the references are resolved in a blocking fashion, but does not require a second connection. In order to achieve this we have to write our own DbRefResolver: NbDbRefResolver.java. In the provided resolver there is a flag: RESOLVE_DB_REFS_BY_ID_ONLY. If is switched on will not going to resolve the DbRefs from the database, but instead will resolve them to fake objects with id only. It is up to implementation to fill the references later in non-blocking fashion.
If the flag RESOLVE_DB_REFS_BY_ID_ONLY is set to false it will eagerly resolve the references by using the non-blocking driver, but will block the execution until the references are resolved.
Here is how to register the DbRefResolver in the app: DbConfig.kt
Files attached are provided here: https://jira.spring.io/browse/DATAMONGO-1584
Me did it like that for roles :
#Unwrapped(onEmpty = Unwrapped.OnEmpty.USE_NULL)
private Collection<Role> roles;
you can check the doc (2021) here : https://spring.io/blog/2021/04/20/what-s-new-in-spring-data-2021-0

Spring Data Redis Repository support does not read back embedded complex objects

I have a spring-boot application (1.4RC1, I know it's RC, but Spring Data Redis 1.7.2 is not) where I'm using spring-boot-starter-redis.
The application uses a Spring Data Repository (CrudRepository) which should save an object (using #RedisHash annotation) with String and Boolean properties and one custom class property, which also has only Strings and Longs as properties.
When I save an object (via the repository), everything went fine and I can see all the properties in the database as I would expect.
When I want to read the data from the database (via the repository) I only get the properties from the parent object. The custom class property is null.
I would expect to get the property loaded from the database as well. As the documentation states you can write a custom converter, but since I don't need to do that, when I want to write the data, I shouldn't need to write a reading converter as well.
I wonder if I need to annotate the custom class property, but I couldn't find anything in the documentation. Can you point me in the right direction?
The classes are as follows:
Class sample:
#Data
#EqualsAndHashCode(exclude = {"isActive", "sampleCreated", "sampleConfiguration"})
#RedisHash
public class Sample {
#Id
private String sampleIdentifier;
private Boolean isActive;
private Date sampleCreated;
private SampleConfiguration sampleConfiguration;
public Sample(String sampleIdentifier, SampleConfiguration sampleConfiguration){
this.sampleIdentifier = sampleIdentifier;
this.sampleConfiguration = sampleConfiguration;
}
}
Class SampleConfiguration:
#Data
public class SampleConfiguration {
private String surveyURL;
private Long blockingTime;
private String invitationTitle;
private String invitationText;
private String participateButtonText;
private String doNotParticipateButtonText;
private String optOutButtonText;
private Long frequencyCappingThreshold;
private Long optOutBlockingTime;
}
I added #NoArgsConstructor to my Sample class as Christoph Strobl suggested. Then the repository reads the SampleConfiguration correctly. Thanks, Christoph!

spring security datamodel

I'm currently using the spring-security libraries and I asked myself the following question: How should I combine my database model with the spring-security tables?
As you know spring-security needs two tables (users and authorities) to define an authentication manager in the database. From my pov there are now two possibilities where I store my additional user-information (like email, lastname, last-logged-on, ....)
I could have a plain user-table for authentication purposes and another one for the rest (linked by the username)
I extend the user-table of spring-security with my necessary attributes.
What is the best design from your perspective? What are your experiences?
Lomu
I created a POJO User which represents the User entity as conceived by the Spring Security library, and secondly I created a POJO ProfiledUser to represent a specialized type of user of my application. It is called ProfiledUser because I needed a user associated to a profile. Of course, a similar approach can be applyied for every type of user you need to represent. Basically, if you need more than one type of user you can make your classes to extend the User POJO.
In the following you find the class, with the JPA annotations.
#Entity
#Table(name="USERS")
#Inheritance(strategy=InheritanceType.JOINED)
public class User implements UserDetails {
private static final long serialVersionUID = 1L;
private long id;
private String username;
private String password;
private boolean enabled = true;
Set<Authority> authorities = new HashSet<Authority>();
//...getters & setters
}
#Entity
#Table(name="PROFILED_USERS")
public class ProfiledUser extends User{
private static final long serialVersionUID = 1L;
//some custom attributes
private PersonalData personalData;
private ContactData contactData;
private AddressData addressData;
//...getters & setters
}
If you need to represent only one type of user, I think it should work to add attributes to the User class. However, I prefer to separate the abstract concept of user defined by the Spring Security framework from my business logic. So I'd recommend to implement your own SomethingUser and extend the User class.
A person is a person and you should have a class/table representing a person†.
A user is a user, and is different from a person (hence the two different words), and you should have a class/table representing a user.
Can a person exist without a user? Yes
Can a user exist without a person? No, a username belongs to someone.
#Entity
abstract class Party {
#Id
Long id;
String name;
#OneToMany
List<User> usernames = new ArrayList<>();
}
#Entity
class Individual extends Party {
DateTime dateOfBirth;
}
#Entity
class User {
#ManyToOne
Party party;
String username;
String password; //you better use BCrypt/Blowfish hashing!
Boolean enabled = true;
}
You could instead use a #OneToOne relationship if you only want one username per party.
† Actually you should have a more abstract class/table representing a legal party.

Resources