I'm thinking at the moment about the solution of a problem which might be already solved by a pattern but I can not find the correct one. I'll try to explain what I would like to do with a simplefied example.
There is a class which handles to connection to a database, let's call it DatabaseManager.java. With this class I would like to handle the fetchment of data from a database and I also would like to apply filter.
public class DatabaseManager {
DatabaseFilter databaseFilter = new DatabaseFilter();
public DatabaseManager() {
// Do some stuff to init db connection
}
public DatabaseFilter configureFilter() {
return databaseFilter;
}
public String getStringDataset() {
String dataset = null;
// Fill dataset with applied filter data
return dataset;
}
}
With the method call configureFilter() on the databse object, I would like to get the DatabaseFilter which contains particular nested filter classes.
public class DatabaseFilter {
int[] filteredIds = null;
public IdFilter onId() {
return new IdFilter();
}
public class IdFilter {
private void exclude(int[] ids) {
filteredIds = ids;
}
}
}
On this way, I could write the follwing nice syntax to configure a filter which excludes particular filter.
DatabaseManager database = new DatabaseManager();
database.configureFilter().onId().exclude(idArray);
// Get filtered dataset
database.getStringDataset();
Is this an appropriate way to solve problems like this and is this a particular pattern? Are there any disadvantages?
Related
I have been struggling with this, and I can't seem to get it right. I have a Java Spring Boot project and I need to create an API that returns the following JSON in what I called AggregatedResponse:
{
"shipments": {
"987654321": ["BOX", "BOX", "PALLET"]
},
"track": {
"123456789": "COLLECTING"
},
"pricing": {
"NL": 14.242090605778
"CN": 20.503467806384
}
}
Each of the objects (shipments, track, pricing) need to be fetched from an external API.
This means that I need to create the AggregatedResponse and use a setter whenever I'm receiving the different data from the external APIs.
The number in the shipments (987654321) comes from the request param passed to the external API that retrieves shipments. The track number (123456789) also comes from an external API.
This is the Java structure I have so far:
public class AggregatedResponse {
private TrackResponse tracking;
private ShipmentsResponse shipments;
private PricingResponse pricing;
public ShipmentsResponse getShipments() {
return shipments;
}
public void setShipmentsResponse(ShipmentsResponse value) {
this.shipments = value;
}
public TrackResponse getTrackResponse() {
return tracking;
}
public void setTrackResponse(TrackResponse trackResponse) {
this.tracking = trackResponse;
}
public PricingResponse getPricing() {
return pricing;
}
public void setPricing(PricingResponse value) {
this.pricing = value;
}
}
The problem I have is that like I mentioned, the shipments and track JSON object have dynamic names, so I have tried different strategies for creating the TrackResponse and the ShipmentsResponse.
How could I do this?
Create a Map from the data instead of class so you can define the keys and serialize it.
I'd like to cache a list of objects that are available for all methods and need it periodically update. I'm wondering if this is safe with multiple threads as per the Spring Boot server. Do I keep the list as static? Or is there a better way to do this?
For example:
#Controller
public class HomeController
{
private static List<String> cachedTerms = new ArrayList<>();
#GetMapping("/getFirstCachedTerm")
public String greeting()
{
if(!cachedTerms.isEmpty())
{
return cachedTerms.get(0);
}else
{
return "no terms";
}
}
//Scheduled to update
private static void updateTerms()
{
//populating from disk IO
cachedTerms.clear();
cachedTerms.add("hello");
}
}
Found out how. By using CopyOnWriteArray, which can be read even while being altered (and thread safe), and by using the #Scheduled tag to automatically run the update.
#Controller
public class HomeController
{
private static final List<String> TERMS_CACHE= new CopyOnWriteArrayList<String>();
#GetMapping("/FirstTerm")
public String getFirstTerm()
{
for(String term: TERMS_CACHE)
{
return term;
}
}
//Scheduled to update
#Scheduled(initialDelay = 1000, fixedRate = 1000)
private static synchronized void updateTerms()
{
//populating from disk IO
TERMS_CACHE.clear();
TERMS_CACHE.add("hello");
}
}
I am using Swagger version 2 with Java Spring. I have declared a property and it works fine and it generates a drop down list of value I assigned.
#ApiParam(value = "Pass any one Shuttle provider ID from the list", allowableValues = "1,2,3,4,10")
private Long hotelId;
Now, I need a way to populate this list which is passed in allowableValues from my database as it could be random list as well as huge data. How can I assign list of values dynamically from database in this allowableValues?
This question is bit old, I too faced the same problem so thought of adding here which may help some one.
//For ApiModelProperty
#ApiModelProperty(required = true, allowableValues = "dynamicEnum(AddressType)")
#JsonProperty("type")
private String type;
Created a component which implements ModelPropertyBuilderPlugin
#Component
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
public class ApiModelPropertyPropertyBuilderCustom implements ModelPropertyBuilderPlugin {
private final DescriptionResolver descriptions;
#Autowired
public ApiModelPropertyPropertyBuilderCustom(DescriptionResolver descriptions) {
this.descriptions = descriptions;
}
public void apply(ModelPropertyContext context) {
try {
AllowableListValues allowableListValues = (AllowableListValues) FieldUtils.readField(context.getBuilder(),
"allowableValues", true);
if(allowableListValues!=null) {
String allowableValuesString = allowableListValues.getValues().get(0);
if (allowableValuesString.contains("dynamicEnum")) {
String yourOwnStringOrDatabaseTable = allowableValuesString.substring(allowableValuesString.indexOf("(")+1, allowableValuesString.indexOf(")"));
//Logic to Generate dynamic values and create a list out of it and then create AllowableListValues object
context.getBuilder().allowableValues(allowableValues);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
Similary for ApiParam we can create component which will implement ParameterBuilderPlugin
#Override
public void apply(ParameterContext context) {
#SuppressWarnings("Guava") final Optional<ApiParam> apiParam =
context.resolvedMethodParameter().findAnnotation(ApiParam.class);
if (apiParam.isPresent()) {
final String allowableValuesString = apiParam.get().allowableValues();
//Your logic here
context.parameterBuilder().allowableValues(allowableValues);
}
}
You need to create constructor in SwaggerConfiguration class.
#Autowire service and withdraw data you need from database
assign this to final variable
assign this final variable to allowableValues in annotation
enjoy not efficient api
private final String allowableValues;
public SwaggerConfiguration() {
List<YourEntitiy> list = someService.findAll();
//code to get every value you need and add create comma separated String
StringJoiner stringJoiner = new StringJoiner(",");
stringJoiner.add(list.get(0).getValue());
this.allowableValues = stringJoiner.toString();
}
#ApiParam(allowableValues = allowableValues)
But I think it's bad idea getting all ids from database just to create allowable values. Just validate in api method if that id exist and/or Create new api to get ids from database, use pagination from Spring Data project, like PageImpl<> javadocs
Basically, i have a class where i have my arrays in, which is like this
public final class DepotDatabase {
private Driver[] arrayDrivers;
public DepotDatabase() {
arrayDrivers = new Driver[4];
arrayDrivers[0] = new Driver(1234, 1234, 0); // sample driver
arrayDrivers[1] = new Driver(4444, 4444, 0); // sample driver
arrayDrivers[2] = new Driver(1337, 1337, 1); // sample manager
arrayDrivers[3] = new Driver(1234, 1234, 0); // sample driver
}
and i want to print this array in another class, i did set up the array in another class
public Driver(int username, int password, int managerCheck) {
this.username = username;
this.password = password;
this.managerCheck = managerCheck;
}
but now i want to be able to print out all the drivers, but in another class which will be called ViewDrivers or something similar
You can create a method inside DepotDatabase to print the array, then create an object from and call print method.
public final class DepotDatabase {
private Driver[] arrayDrivers;
public void printArray() {
for (int i = 0; i < arrayDrivers.length; i++) {
Driver d = arrayDrivers[i];
System.out.println("Username : " + d.getUsername());
System.out.println("Password : " + d.getPassword());
System.out.println(" Manager Check: " + d.getManagerCheck());
}
}
the from the test class you can do:
public void execute() {
DepotDatabase ddb = new DepotDatabase();
ddb.printArray();
}
That's why you'll need to have getters and setters. You should have:
public Driver[] getDrivers() {
return arrayDrivers;
}
and in the other class, you simply call it (and print it or whatever).
Read this tutorial.
If you plan to print your array in another class you show create an accessor to it.
The common pattern for Java is to use "get plus name off attribute", getDrivers() you should also avoid the class name in such geter as it may changed due to application life.
public final class DepotDatabase {
//your code
public Driver[] getDrivers() {
return this.arrayDrivers;
}
}
Next question to answer is a returning the whole array is good idea. When you return it as above you loose control on it. And every one that call that method will be able to change the content of it.
To prevent this you should use so called Defensive copying
public Driver[] getDrivers() {
return Arrays.copyOf(arrayDrivers, arrayDrivers.length);
}
Then person will get an copy of it an will not harm your class.
The issue with this is that consumer of your class will have to call this method every time to get fresh list of cars.
To solve this issue you may want to user the [collection framework] where instead of array you cold define:
List<Driver> drivers new ArrayList<>();
and provide the drivers as [immutable] list
public Iterable<Driver> getDrivers() {
return java.util.Collections.unmodifiableList(drivers);
}
Iterable is an interface, that allow you to obtain an interator the the list consumer of class wold have possibility to traverse it. IF you wan to allow him to check that list contains some driver you can set the return type as Collection
class Storage {
private String items[] = new String[10];
public String[] getItems() {
return Arrays.copyOf(items, items.length);
}
}
class Store {
Storage storage = new Storage();
private void printStorage() {
String[] items = storage.getItems();
for (String item : items) {
}
}
}
I am importing an excel sheet with 30 columns using Apache POI framework. I am reading each cell and inserting it into database.
I need to validate each of these columns for different conditions, other than using multiple if else conditions and calling different methods for each columns is there any other way to go about this?
I have less experience with java. There could be multiple ways to solve your problem. However I have used Strategy Pattern to design a common validation class which can be used for different type of items without ugly if-else block. Though I had to create separate validation method for different type of fields, but I feel this is better than having lot of if else block.
interface IValidator<T> {
boolean validate(T field);
}
class SomeFieldOne<T> implements IValidator<T> {
public boolean validate(T field) {
print("SomeFieldOne validation");
return true; // return true/false based on validation
}
}
class SomeFieldTwo<T> implements IValidator<T> {
public boolean validate(T field) {
print("SomeFieldTwo validate");
return true; // return true/false based on validation
}
}
class Context {
private IValidator validator;
public Context(IValidator validator) {
this.validator = validator;
}
public boolean validate(String field) {
return this.validator.validate(field);
}
}
public class TestValidation {
public static void main(String[] args) {
Context context;
context = new Context(new SomeFieldOne());
print(context.validate("some field one"));
context = new Context(new SomeFieldTwo());
print(context.validate("some field two"));
// test other fields ....
// .........
}
}