background:
I build a class diagram for shopping on the net.
For creating a user interface with tow type (golden-User and silver-User) I use the factory pattern.
But the User class become to be very complex.
How can I create this class by bulider and on the other hand the ability to specify the user type such as the factory will remain on the class name
(will help me to recognize which type is by polymorphism and not by if&else)
The Decorator pattern is a simple solution:
public class Main {
public static void main(String[] args) {
User silverUser = new UserDecorator(new SilverUser("Kyriakos", "Georgiopoulos"));
User goldenUser = new UserDecorator(new GoldenUser("GoldenUser firstName", "GoldenUser lastName"));
User nullUser = new UserDecorator(null);
System.out.println(silverUser.firstName() + " " + silverUser.lastName() + " is " + silverUser.type());
System.out.println(goldenUser.firstName() + " " + goldenUser.lastName() + " is " + goldenUser.type());
System.out.println(nullUser.firstName() + " " + nullUser.lastName() + " is " + nullUser.type());
}
}
interface User {
String firstName();
String lastName();
String type();
}
class SilverUser implements User {
private final String firstName;
private final String lastName;
SilverUser(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String firstName() {
return firstName;
}
public String lastName() {
return lastName;
}
public String type() {
return "SilverUser ";
}
}
class GoldenUser implements User {
private final String firstName;
private final String lastName;
GoldenUser(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String firstName() {
return firstName;
}
public String lastName() {
return lastName;
}
public String type() {
return "GoldenUser ";
}
}
class UserDecorator implements User {
private final User user;
UserDecorator(User user){
this.user = user;
}
public String firstName() {
return user != null && user.firstName() != null && user.firstName().length() > 0 ?
user.firstName() : "";
}
public String lastName() {
return user != null && user.lastName() != null && user.lastName().length() > 0 ?
user.lastName() : "";
}
public String type() {
return user != null ? user.type() : "NullPointerException";
}
}
The intent of the two patterns are different: while Factory creates an object instance (which can hold more other class instances) the Builder's goal is to create object step-by-step and reduce overloaded constructors.
For example (with java snippets):
Factory method
interface for user:
public interface User {
}
GoldUser class:
class GoldUser implements User {
// ... field declarations
// Ctor
GoldUser(fields...){}
// ... methods
}
SilverUser class:
class SilverUser implement User {
// ... field declarations
// Ctor
SilverUser(fields...){}
// ... methods
}
User Factory Class:
public class UserFactory {
// ... user versions
public static int GoldUser = 0;
public static int SilverUser = 1;
// ... private Ctor because we don't want to instantiate this class - only in this example
private UserFactory (){}
// ... creating appropriate User instance
public static User createUser(int userType){
switch (userType){
case GoldUser: return new GoldUser;
case SilverUser: return new SilverUser;
default throw new WrongUserTypeException("Wrong User Type");
}
}
}
in your other class:
// ... code stuff here
User user=UserFactory.createUser(1); // will return new SilverUser instance
// ... other code stuff here
Builder pattern
If you have many fields in your class and only some of them are compulsory, you don't have to create many constructors, a builder will enough:
class UserBuilder{
private static Service_A serviceA; // required
private static Service_B serviceB; // required
private static Service_C serviceC;
private static Service_D serviceD;
private static Service_E serviceE;
// since this builder is singleton
private static UserBuilder builderInstance = new UserBuilder();
private UserBuilder () {};
public static UserBuilder getBuilderInstance (Service_A service_A, Service_B service_B){
serviceA = service_A;
serviceB = service_B;
serviceC = null;
serviceD = null;
serviceE = null;
return builderInstance;
}
public static UserBuilder addServiceC (Service_C service_C) {
serviceC = service_C;
return builderInstance;
}
public static UserBuilder addServiceD (Service_D service_D) {
serviceC = service_D;
return builderInstance;
}
public static UserBuilder addServiceE (Service_E service_E) {
serviceE = service_E;
return builderInstance;
}
public static User build(){
return new User (serviceA, ServiceB, ServiceC, ServiceD, ServiceE);
}
And later you can build a customized User:
UserBuilder aUserBuilder = UserBuilder.getBuilderInstance(aServiceA, aServiceB);
// ... other stuff
aUserBuilder.addServiceE(aServiceE);
///... more stuff
User aUser= aUSerBuilder.addServiceC(aServiceC)
.build(); // will return the fresh built User instance
Hope I could help you!
Regards,
Cs
In this particular case you're not supposed to use Factory to create different instances of the same class. It can be used to create different implementations of one common abstraction. Try implementing IUser interface. Then implement this interface by two classes: GoldenUser and SilverUser. Your Factory will create instance of either GoldenUser or SilverUser and return it as IUser. Also instead of interface IUser you could probably create User abstract class, that will be inherited by GoldenUser and SilverUser.
Related
I want to write a point cut for class instantiation in various packages,like classes inside the subpackages inside com.kepler.xenon (eg.com.kepler.xenon.modules.ticklers.pojo.Tickler,
com.kepler.xenon.modules.product.pojo.Product etc).
//This is my advice
#Aspect
#Component
public class OxAspect {
#After("execution(* com.oxane.xenon..*new(..)) && #within(java.lang.Deprecated)")
public void myAdvice(final JoinPoint jp){
System.out.println(jp.getSignature().getName()+""+jp.getTarget().getClass());
}
}
//This is my class
package com.kepler.xenon.modules.ticklers.pojo;
#Deprecated
public Class Ticklers{
#Id
#TableGenerator(name = "TICKLERS_ID", table = "ID_GENERATOR", pkColumnName = "GEN_KEY", valueColumnName = "GEN_VALUE", pkColumnValue = "TICKLERS_ID", allocationSize = 1, initialValue = 1)
#GeneratedValue(strategy = GenerationType.TABLE, generator = "TICKLERS_ID")
#Column(name = "TICKLERS_ID", unique = true, nullable = false)
private int ticklersId;
#Column(name = "TASK", nullable = false, length = 256)
private String taskName;
public int getTicklersId() {
return ticklersId;
}
public void setTicklersId(int ticklersId) {
this.ticklersId = ticklersId;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
}
What i want is that if anyone tries to access the class which is deprecated,then pointcut filters that call and triggers advice.
I have done it for methods but i am failing to do it for classes.
I am adding aspect which works for methods,controller and Dao
#Aspect
#Component
public class OxAspect {
private final OxAspectService oxAspectService;
public OxAspect(OxAspectService oxAspectService) {
this.oxAspectService=oxAspectService;
}
#Pointcut("execution(#java.lang.Deprecated * com.oxane.xenon..*(..))"
+ " || execution(* com.oxane.xenon..*.*(..)) && #within(java.lang.Deprecated)")
public void deprecated() {
}
#Before("deprecated()")
public void log(final JoinPoint jp) {
oxAspectService.logDeprecatedMethod(jp);
}
}
Edit:
I have done some research on spring io and found that it can't be done using spring aop. I have to use load time weaving or compile time weaving to achieve what i want. For that i have to use pure aspect j implementation. Correct me if i am wrong.
If I were you I will devide #Pointcut to signle condition like below:
#Pointcut("execution(* com.oxane.xenon..*(..))")
public void anyClassInSubpackage() {
}
#Pointcut("#annotation(java.lang.Deprecated)")
public void deprecatedClass() {
}
#Pointcut("execution(* com.oxane.xenon..*new(..))")
public void anyMethodInSubpackege() {
}
#Pointcut("#within(java.lang.Deprecated)")
public void deprecatedMethod() {
}
#Before("(anyClassInSubpackage() && deprecatedClass()) || (anyMethodInSubpackege() && deprecatedMethod())")
public void myAdvice(final JoinPoint jp){
//TODO
}
is there a way to map a DTO using MatStruct which have a few final data members as well and cannot have a default constructor , like :
public class TestDto {
private final String testName;
private int id;
private String testCase;
public TestDto(String testName) {
this.testName = testName;
}
public String getTestName() {
return testName;
}
public int getId() {
return id;
}
public String getTestCase() {
return testCase;
}
public void setId(int id) {
this.id = id;
}
public void setTestCase(String testCase) {
this.testCase = testCase;
}
}
please suggest how could this DTO be mapped using MapStruct.
You can use #ObjectFactory that would construct an instance of your DTO.
For example:
#Mapper
public interface MyMapper {
#ObjectFactory
default TestDto create() {
return new TestDto("My Test Name");
}
//the rest of the mappings
}
You can also enhance the #ObjectFactory to accept the source parameter, that you can use to construct the TestDto. You can even use a #Context as an Object Factory.
NB: You don't have to put the #ObjectFactory method in the same Mapper, or even a MapStruct #Mapper. You can put it in any class (or make it static) and then #Mapper(uses = MyFactory.class)
In my exploration of JPA, I have the code below (which I understand should not be used in production). Running my code produces the following error:
java.lang.IllegalStateException:
Exception Description: Cannot use an EntityTransaction while using JTA.
The Resource code is as follows:
#Path("users")
public class UsersAPI {
#Context
UriInfo uriInfo;
#Inject
UserBean accountsBean;
#GET
#Path("deduplicate")
public Response deduplicateDB(){
List<UserProfile> profiles = accountsBean.getAll();
int profilesNum = profiles.size();
for(int i = 0; i < profilesNum; ++i){
for(int k = 0; k < profilesNum; ++k){
if(i != k){ //if it's not the same profile
if(profiles.get(i).getUsername().equals(profiles.get(k).getUsername())){
accountsBean.remove(profiles.get(k));
profiles.remove(k);
}
}
profilesNum = profiles.size();
}
}
return Response.ok().build();
}
}
The code in the ProfilesBean is as follows:
#Local
#Stateless
public class UserBean {
#PersistenceContext
EntityManager eManager;
public void save(UserProfile data){
eManager.merge(data);
}
public void remove(UserProfile data){
eManager.getTransaction().begin();
eManager.remove(data);
eManager.getTransaction().commit();
}
public List<UserProfile> getAll(){
Query q = eManager.createQuery("SELECT profile FROM Users profile");
return (List<UserProfile>)q.getResultList();
}
}
Here is the code for the Entity class:
#Entity(name="Users")
public class UserProfile {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
String password;
#Column(unique=true)
String username;
public UserProfile(String username){
setUsername(username);
}
public UserProfile(){
this(null);
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
It seems like the error comes from my misusing the platform somehow. How can I fix this code and not misuse the platform in the future?
If you are using JTA as transaction-type in persistence.xml file just leave JTA handles your transactions
public void remove(UserProfile data){
eManager.remove(eManager.merge(data));
}
UPDATE:
In a more clear solution you could use "find", but you need to provide the object id
public void remove(UserProfile data){
UserProfile e = em.find(UserProfile.class, data.getId());
eManager.remove(e);
}
I am getting the below exception:
Could not find PortableFactory for factory-id: 1
com.hazelcast.nio.serialization.HazelcastSerializationException: Could
not find PortableFactory for factory-id: 1
On the client side I have the following code:
public class ClientTest {
public static void main(String[] args) {
List<String> nodes = new ArrayList<String>();
nodes.add("localhost:5701");
ClientConfig clientConfig = new ClientConfig();
ClientNetworkConfig networkConfig = new ClientNetworkConfig();
networkConfig.setAddresses(nodes);
clientConfig.setNetworkConfig(networkConfig);
SerializationConfig serCong = clientConfig.getSerializationConfig();
serCong.addPortableFactory(1, new UserFactoryImpl());
serCong.setPortableVersion(1);
HazelcastInstance hzClient1 = HazelcastClient.newHazelcastClient(clientConfig);
IMap<String, User> map = hzClient1.getMap("user");
System.out.println(map.size() + "hiten");
User user1 = new User();
user1.setFirstName("hiten");
user1.setLastName("singh");
map.put("1", user1);
//hz1.getLifecycleService().terminate();
System.out.println(map.size() + "after");
User user2 = new User();
user2.setFirstName("hiten1");
user2.setLastName("singh1");
map.put("2", user2);
UserEntryProcessor entryProc = new UserEntryProcessor();
User userRes = (User)map.executeOnKey("1", entryProc);
}
static class UserEntryProcessor implements EntryProcessor<String, User>, HazelcastInstanceAware {
private transient HazelcastInstance hazelcastInstance;
#Override
public Object process(Entry<String, User> entry) {
User user = entry.getValue();
if(user != null) {
System.out.println(user.getFirstName());
}
return user;
}
#Override
public EntryBackupProcessor<String, User> getBackupProcessor() {
return null;
}
#Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
}
static class UserFactoryImpl implements PortableFactory{
public final static int USER_PORTABLE_ID = 1;
public final static int FACTORY_ID = 1;
public Portable create(int classId) {
switch (classId) {
case USER_PORTABLE_ID:
return new User();
}
return null;
}
}
static class User implements Portable {
private String firstName;
private String lastName;
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Override
public int getFactoryId() {
return UserFactoryImpl.FACTORY_ID;
}
#Override
public int getClassId() {
return UserFactoryImpl.USER_PORTABLE_ID;
}
#Override
public void writePortable(PortableWriter writer) throws IOException {
writer.writeUTF("first_name", firstName);
writer.writeUTF("last_name", lastName);
}
#Override
public void readPortable(PortableReader reader) throws IOException {
firstName = reader.readUTF("first_name");
lastName = reader.readUTF("last_name");
}
}
}
Yes it does, just as you figured out the factory and the classes need to be available. Currently there is no built-in solution to not share classes for more sophisticated use cases than simple gets / puts. I have JSON support and some other ideas cooking but nothing really done yet.
I want to have multiples implementation of the IUserRepository each implementation will work with a database type either MongoDB or any SQL database. To do this I have ITenant interface that have a connection string and other tenant configuration. The tenant is been injected into IUserRepository either MongoDB or any SQLDB implementation. What I need to know is how properly change the injected repository to choose the database base on the tenant.
Interfaces
public interface IUserRepository
{
string Login(string username, string password);
string Logoff(Guid id);
}
public class User
{
public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
public interface ITenant
{
string CompanyName { get; }
string ConnectionString { get; }
string DataBaseName { get; }
string EncriptionKey { get; }
}
Is important to know that the tenant id is been pass to an API via header request
StartUp.cs
// set inject httpcontet to the tenant implemantion
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
// inject tenant
services.AddTransient<ITenant, Tenant>();
// inject mongo repository but I want this to be programmatically
services.AddTransient<IUserRepository, UserMongoRepository>();
Sample Mongo Implementation
public class UserMongoRepository : IUserRepository
{
protected ITenant Tenant
public UserMongoRepository(ITenant tenant) :
base(tenant)
{
this.Tenant = tenant;
}
public string Login(string username, string password)
{
var query = new QueryBuilder<User>().Where(x => x.Username == username);
var client = new MongoClient(this.Tenant.ConnectionString);var server = client.GetServer();
var database = client.GetServer().GetDatabase(this.Tenant.DataBaseName);
var user = database.GetCollection<User>.FindAs<User>(query).AsQueryable().FirstOrDefault();
if (user == null)
throw new Exception("invalid username or password");
if (user.Password != password)
throw new Exception("invalid username or password");
return "Sample Token";
}
public string Logoff(Guid id)
{
throw new NotImplementedException();
}
}
Tenant
public class Tenant : ITenant
{
protected IHttpContextAccessor Accesor;
protected IConfiguration Configuration;
public Tenant(IHttpContextAccessor accesor, IDBConfiguration config)
{
this.Accesor = accesor;
this.Configuration = new Configuration().AddEnvironmentVariables();
if (!config.IsConfigure)
config.ConfigureDataBase();
}
private string _CompanyName;
public string CompanyName
{
get
{
if (string.IsNullOrWhiteSpace(_CompanyName))
{
_CompanyName = this.Accesor.Value.Request.Headers["Company"];
if (string.IsNullOrWhiteSpace(_CompanyName))
throw new Exception("Invalid Company");
}
return _CompanyName;
}
}
private string _ConnectionString;
public string ConnectionString
{
get
{
if (string.IsNullOrWhiteSpace(_ConnectionString))
{
_ConnectionString = this.Configuration.Get(this.CompanyName + "_" + "ConnectionString");
if (string.IsNullOrWhiteSpace(_ConnectionString))
throw new Exception("Invalid ConnectionString Setup");
}
return _ConnectionString;
}
}
private string _EncriptionKey;
public string EncriptionKey
{
get
{
if (string.IsNullOrWhiteSpace(_EncriptionKey))
{
_EncriptionKey = this.Configuration.Get(this.CompanyName + "_" + "EncriptionKey");
if (string.IsNullOrWhiteSpace(_EncriptionKey))
throw new Exception("Invalid Company Setup");
}
return _EncriptionKey;
}
}
private string _DataBaseName;
public string DataBaseName
{
get
{
if (string.IsNullOrWhiteSpace(_DataBaseName))
{
_DataBaseName = this.Configuration.Get(this.CompanyName + "_" + "DataBaseName");
if (string.IsNullOrWhiteSpace(_DataBaseName))
throw new Exception("Invalid Company Setup");
}
return _DataBaseName;
}
}
}
Controller
public class UsersController : Controller
{
protected IUserRepository DataService;
public UsersController(IUserRepository dataService)
{
this.DataService = dataService;
}
// the controller implematation
}
You should define a proxy implementation for IUserRepository and hide the actual implementations behind this proxy and at runtime decide which repository to forward the call to. For instance:
public class UserRepositoryDispatcher : IUserRepository
{
private readonly Func<bool> selector;
private readonly IUserRepository trueRepository;
private readonly IUserRepository falseRepository;
public UserRepositoryDispatcher(Func<bool> selector,
IUserRepository trueRepository, IUserRepository falseRepository) {
this.selector = selector;
this.trueRepository = trueRepository;
this.falseRepository = falseRepository;
}
public string Login(string username, string password) {
return this.CurrentRepository.Login(username, password);
}
public string Logoff(Guid id) {
return this.CurrentRepository.Logoff(id);
}
private IRepository CurrentRepository {
get { return selector() ? this.trueRepository : this.falseRepository;
}
}
Using this proxy class you can easily create a runtime predicate that decides which repository to use. For instance:
services.AddTransient<IUserRepository>(c =>
new UserRepositoryDispatcher(
() => c.GetRequiredService<ITenant>().DataBaseName.Contains("Mongo"),
trueRepository: c.GetRequiredService<UserMongoRepository>()
falseRepository: c.GetRequiredService<UserSqlRepository>()));
You can try injecting a factory rather than the actual repository. The factory will be responsible for building the correct repository based on the current user identity.
It might require a little more boiler plate code but it can achieve what you want. A little bit of inheritance might even make the controller code simpler.