Saturday, November 29, 2014

Java Configuration API - Java 7 or Java 8 ?

Java 7 vs Java 8 for Apache Tamaya Config

Since the Apache mailing list is preventing me at all cost my more comprehensive addition to the current Tamaya discussion about Java 7 vs. Java 8, I now decided to write a blog.

I will shortly show here a small example how a modern configuration API written in Java 8 significantly differs from one written in Java 7 and why it is worth to do it in Java 8, so read ahead:


  1. the target is to build a modern API.
  2. there are many developers that do not use Spring or other technologies, where adoption is much faster.
  3. Wildfly as well as Weblogic 12.1.3 are Java 8 certified AFAIK (to be verified, since EE7 TCK does not run on Java 8...)
  4. Adoption within one year will be great.
  5. Java 8 is more than Lambdas and streams!
  6. Java 8 is the future! And we design for the future, we do not want to be one additional config framework.
  7. We can still provide a backport for Java 7. The core of Tamaya will be quite small. It should be possible to provide a backport within a couple of hours.

I give you an example here, e.g. let us start on the PropertyProvider:


In Java 8:


public interface PropertyProvider {


   Optional<String> get(String key);
   boolean containsKey(String key);
   Map<String, String> toMap();
   MetaInfo getMetaInfo();


   default boolean hasSameProperties(PropertyProvider provider) {...}
   default Set<String> keySet(){...}
   default ConfigChangeSet load(){...}
   default boolean isMutable(){...}
   default void apply(ConfigChangeSet change){...}
}


In Java 7:


With Java 7 this would be (to provide similar comfort, but on the cost of implementation dependencies and limited flexibility because of missing behavioural inheritance):


public interface PropertyProvider {


   String get(String key); // @throws ConfigException if no value
   String getOrDefault(String key, String value);


   boolean containsKey(String key);
   Map<String, String> toMap();
   MetaInfo getMetaInfo();


   default boolean hasSameProperties(PropertyProvider provider) {...}
   default Set<String> keySet(){...}
   default ConfigChangeSet load(){...}
   default boolean isMutable(){...}
   default void apply(ConfigChangeSet change){...}
}


protected abstract class AbstractPropertyProvider implements PropertyProvider {
   public boolean hasSameProperties(PropertyProvider provider) {...}
   public Set<String> keySet(){...}
   public ConfigChangeSet load(){...}
   public boolean isMutable(){...}
   public void apply(ConfigChangeSet change){...}
}


Example 2: Configuration


Looking at Configuration and the singleton access there things get even worse:


In Java 8:


public interface Configuration extends PropertyProvider{


   <T> Optional<T> get(String key, Class<T> type);
   void addPropertyChangeListener(PropertyChangeListener l);
   void removePropertyChangeListener(PropertyChangeListener l);


   default OptionalBoolean getBoolean(String key){... }
   default OptionalInt getInteger(String key){... }
   default OptionalLong getLong(String key){... }
   default OptionalDouble getDouble(String key){...  }
   default <T> Optional<T> getAdapted(String key, PropertyAdapter<T> adapter){... }
   default Set<String> getAreas(){... }
   default Set<String> getTransitiveAreas(){... }
   default Set<String> getAreas(final Predicate<String> predicate){... }
   default Set<String> getTransitiveAreas(Predicate<String> predicate){... }
   default boolean containsArea(String key){... }
   default Configuration with(ConfigOperator operator){... }
   default <T> T query(ConfigQuery<T> query){...}
   default String getVersion(){...}


   public static boolean isDefined(String name){...}
   public static <T> T of(String name, Class<T> template){...}
   public static Configuration of(String name){...}
   public static Configuration of(){...}
   public static <T> T of(Class<T> type){... }
   public static void configure(Object instance){... }
   public static String evaluateValue(String expression){... }
   public static String evaluateValue(Configuration config, String expression){...  }
   public static void addGlobalPropertyChangeListener(PropertyChangeListener listener){... }
   public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener){...}
}


In Java 7:


public interface Configuration extends PropertyProvider{
   <T> T get(String key, Class<T> type); // throws ConfigException
   <T> T getOrDefault(String key, Class<T> type, T instance);
   void addPropertyChangeListener(PropertyChangeListener l);
   void removePropertyChangeListener(PropertyChangeListener l);


   boolean getBoolean(String key){... } // throws ConfigException
   boolean getBooleanOrDefault(String key, boolean defaultVal){... }
   int getInteger(String key){... } // throws ConfigException
   int getIntegerOrDefault(String key, int defaultVal){... } 
// throws ConfigException
   long getLong(String key){... } // throws ConfigException
   long getLongOrDefault(String key, long defaultVal);
   double getDouble(String key){... } // throws ConfigException
   double getDoubleOrDefault(String key, double defaultVal);
   <T> getAdapted(String key, PropertyAdapter<T> adapter){... } 
// throws ConfigException
   <T> getAdaptedOrDefault(String key, PropertyAdapter<T> adapter, T defaultVal){... } // throws ConfigException
   Set<String> getAreas(){... }
   Set<String> getTransitiveAreas(){... }
   Set<String> getAreas(final Predicate<String> predicate){... }
// Duplicate predicate class, or introduce additional interface
   Set<String> getTransitiveAreas(Predicate<String> predicate){... } 
// Duplicate predicate class, or introduce additional interface
   boolean containsArea(String key){... }
   Configuration with(ConfigOperator operator){... }
   <T> T query(ConfigQuery<T> query){...}
   String getVersion(){...}
}


public final class ConfigManager{
  private ConfigManager(){}


   public static boolean isDefined(String name){...}
   public static <T> T of(String name, Class<T> template){...}
   public static Configuration of(String name){...}
   public static Configuration of(){...}
   public static <T> T of(Class<T> type){... }
   public static void configure(Object instance){... }
   public static String evaluateValue(String expression){... }
   public static String evaluateValue(Configuration config, String expression)
{... }
   public static void addGlobalPropertyChangeListener(
PropertyChangeListener listener){... }
   public static void removeGlobalPropertyChangeListener(
PropertyChangeListener listener){...}
}


protected abstract class AbstractConfiguration extends AbstractPropertyProvider implements Configuration{
   boolean getBoolean(String key){... } // throws ConfigException
   boolean getBooleanOrDefault(String key, boolean defaultVal){... }
   int getInteger(String key){... } // throws ConfigException
   int getIntegerOrDefault(String key, int defaultVal){... } // throws ConfigException if not found
   long getLong(String key){... } // throws ConfigException
   long getLongOrDefault(String key, long defaultVal);
   double getDouble(String key){... } // throws ConfigExceptio
   double getDoubleOrDefault(String key, double defaultVal);
   <T> getAdapted(String key, PropertyAdapter<T> adapter){... } 
// throws ConfigException
   <T> getAdaptedOrDefault(String key, PropertyAdapter<T> adapter, T defaultVal)  
{...} // throws ConfigException
   public Set<String> getAreas(){... }
   public Set<String> getTransitiveAreas(){... }
   public Set<String> getAreas(final Predicate<String> predicate){... }
   public Set<String> getTransitiveAreas(Predicate<String> predicate){... }
   public boolean containsArea(String key){... }
   public Configuration with(ConfigOperator operator){... }
   public <T> T query(ConfigQuery<T> query){...}
   public String getVersion(){...}
}


And even when looking from the client side:


Java 8:


String value = Configuration.of().get("a.b.c").orElse(MyClass::calculateDefault);


Java 7:


String value = ConfigurationManager.getConfiguration().getOrDefault("a.b.c", null);
if(value==null){
 value = calculateDefault();
}


So obviously the strength of Java 8 are far beyond Streams and Lambdas:
  • The API footprint for clients overall is half the size.
  • The implementations of APIs/SPIs is much more easier and does not introduce implementation dependencies on abstract classes
  • Users must known much less artefacts to use the API!
  • It is much more flexible and extendable (eg method references)
  • ...

The above case with the deferred calculation is additionally a simple but common use case for Lambda usage. Considering implementation use cases like filtering and mapping/combining of configuration to other things Streams are incredibly useful as well. Similarly we would loose for sure great support from some of the most communities like SouJava and LJC.

So I hope I have now convinced really everybody that it is NOT worth to stick on Java 7, just because we would have faster adoption ;-) ! Do the API right for Java 8 and if enough people ask for do a backport. With the current relative small size of Tamaya a backport should be doable in about 3-4 hours ;)

121 comments:

  1. Nice comparison! But I really don't know when my customers will be ready for Java 8. Maybe in a year or two. Currently working for a customer still on Java 6!

    ReplyDelete
    Replies
    1. Apache Tamaya will also require some time to be really final in version 1.0. 6 month would be quite fast, I assume...

      Delete
  2. Are you sure that you can use "default" in Java 7 interfaces:

    >>> With Java 7 this would be (to provide similar comfort, but on the cost of implementation dependencies and limited flexibility because of missing behavioural inheritance):

    ...

    default boolean hasSameProperties(PropertyProvider provider) {...}
    default Set keySet(){...}
    default ConfigChangeSet load(){...}
    default boolean isMutable(){...}
    default void apply(ConfigChangeSet change){...}
    }

    ReplyDelete
  3. Java language was discovered by James Gosling of Sun Micro systems in 1991. Although C, C++ like programming languages were present in the market but due to fix platform constraint, web developers were unable to develop high end applications.Java

    ReplyDelete
  4. Both the source code and annotated specification information exist side by side leading to a simplified development model for
    Java developers(Java Developer "www.dev2one.com"). This information access simplicity is critical to outsource Java development
    where Java developers need to be on the same page.
    java

    ReplyDelete
  5. Is there a mailing list for Apache Tamaya? The webpage just says ". Basically it is enough to just drop as a mail on our [developer mailing list][1].", without a hyperlink. :(

    ReplyDelete
  6. Thank you for sharing such a nice and interesting blog with us. i have seen that all will say the same thing repeatedly. But in your blog, I had a chance to get some useful and unique information. I would like to suggest your blog in my dude circle. please keep on updates. hope it might be much useful for us. keep on updating.
    Sharepoint Training in Chennai

    ReplyDelete
  7. I found some useful information in your blog, it was awesome to read, thanks for sharing this great content to my vision, keep sharing.I'm very happy for this blog site my comment post.
    java training in chennai

    ReplyDelete
  8. That was a great message in my carrier, and It's wonderful commands like mind relaxes with understand words of knowledge by information's.
    Weblogic Training in Chennai

    ReplyDelete
  9. It's interesting that many of the bloggers to helped clarify a few things for me as well as giving.Most of ideas can be nice content.The people to give them a good shake to get your point and across the command.
    Java Training in Chennai

    ReplyDelete
  10. Really Good blog post.provided a helpful difference between java7 and java 8 API .keep updating...
    Digital marketing company in Chennai

    ReplyDelete
  11. Thanks for your post! Through your pen I found the problem up interesting! I believe there are many other people who are interested in them just like me! Thanks your shared!...
    happy wheels

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Superb. I really enjoyed very much with this article here. Really it is an amazing article I had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts. java training in chennai

    ReplyDelete
  14. Informative article, thanks for sharing your views and knowledge for us... it is very glad to read your article about java...

    Java courses in chennai

    ReplyDelete
  15. The Spring Framework is a lightweight framework for developing Java enterprise applications. It provides high performing, easily testable and reusable code. Spring handles the infrastructure as the underlying framework so that you can focus on your application.Spring is modular in design, thereby making creation, handling and linking of individual components so much easier. Spring implements Model View Container(MVC) design pattern.
    spring custom validator example

    ReplyDelete
  16. Thank you for posting, its a nice post and very informative, looking for some more stuff.
    Best IT Training in Bangalore

    ReplyDelete
  17. Needed to compose you a very little word to thank you yet again regarding the nice suggestions you’ve contributed here.
    mean-stack-training-institute-in-chennai

    ReplyDelete
  18. Your new valuable key points imply much a person like me and extremely more to my office workers. With thanks from every one of us.

    Best AWS Training in Chennai | Amazon Web Services Training in Chennai

    ReplyDelete
  19. The knowledge of technology you have been sharing thorough this post is very much helpful to develop new idea. here by i also want to share this.
    Devops training in Chennai
    Devops training in Bangalore
    Devops Online training
    Devops training in Pune

    ReplyDelete
  20. The knowledge of technology you have been sharing thorough this post is very much helpful to develop new idea. here by i also want to share this.
    Selenium Training in Chennai | Selenium Training in Bangalore | Selenium Training in Pune | Selenium online Training

    ReplyDelete
  21. Some us know all relating to the compelling medium you present powerful steps on this blog and therefore strongly encourage contribution from other ones on this subject while our own child is truly discovering a great deal. Have fun with the remaining portion of the year.
    Python training in pune
    AWS Training in chennai
    Python course in chennai

    ReplyDelete
  22. Thanks for your informative article, Your post helped me to understand the future and career prospects & Keep on updating your blog with such awesome article.
    DevOps online Training
    Best Devops Training institute in Chennai

    ReplyDelete
  23. Great content thanks for sharing this informative blog which provided me technical information keep posting.
    python training in velachery | python training institute in chennai



    ReplyDelete
  24. Wow it is really wonderful and awesome thus it is very much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your blog.
    Data Science Training in Indira nagar | Data Science Training in Electronic city

    Python Training in Kalyan nagar | Data Science training in Indira nagar

    Data Science Training in Marathahalli | Data Science Training in BTM Layout


    ReplyDelete
  25. Thanks for one marvelous posting! I enjoyed reading it; you are a great author. I will make sure to bookmark your blog and may come back someday.
    safety course in chennai

    ReplyDelete
  26. Superb. I really enjoyed very much with this article here. Really it is an amazing article I had ever read. I hope it will help a lot for all. Thank you so much for this amazing posts and please keep update like this excellent article.thank you for sharing such a great blog with us. expecting for your.
    Salesforce Training in Chennai
    German Classes in Chennai
    Salesforce certification Training in Chennai
    Salesforce.com training in chennai
    German Training in Chennai
    german classes chennai

    ReplyDelete
  27. Great work. Quite a useful post, I learned some new points here.I wish you luck as you continue to follow that passion.

    Cloud Training
    Cloud Training in Chennai

    ReplyDelete
  28. Great post! I am actually getting ready to across this information, It’s very helpful for this blog.Also great with all of the valuable information you have Keep up the good work you are doing well.
    Java training in Chennai | Java training in Tambaram

    Java training in Chennai | Java training in Velachery

    Java training in Chennai | Java training in Omr

    Oracle training in Chennai

    ReplyDelete
  29. Thanks for your sharing such a useful information. this was really helpful to me.

    naradhar
    Technology

    ReplyDelete
  30. The knowledge of technology you have been sharing thorough this post is very much helpful to develop new idea. here by i also want to share this.
    Best Devops training in sholinganallur
    Devops training in velachery
    Devops training in annanagar
    Devops training in tambaram

    ReplyDelete
  31. Thanks for your sharing such a useful information. this was really helpful to me.

    payrollmanagementservice
    Guest posting sites

    ReplyDelete
  32. Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.
    angularjs Training in btm

    angularjs Training in electronic-city

    angularjs online Training

    angularjs Training in marathahalli

    angularjs interview questions and answers

    ReplyDelete
  33. There are so many choices out there that I’m completely confused. Any suggestions? Thanks a lot.
    nebosh course in chennai

    ReplyDelete

  34. Excellent blog, good to see someone is posting quality information.
    DevOps Online Training

    ReplyDelete
  35. Do you mind if I quote a couple of your posts as long as I provide credit and sources back to your blog?
    iosh safety course in chennai

    ReplyDelete
  36. Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.
    aws training in bangalore
    RPA Training in bangalore
    Python Training in bangalore
    Selenium Training in bangalore
    Hadoop Training in bangalore

    ReplyDelete
  37. Thank For Sharing Your Information The Information Shared Is Very Valuable Please Keep Updating Us Time Went On Just Reading The Article Python Online Course

    ReplyDelete
  38. Very interesting post! Thanks for sharing your experience suggestions.
    Devops Training in Chennai | Devops Training Institute in Chennai

    ReplyDelete
  39. You made such an interesting piece to read, giving every subject enlightenment for us to gain knowledge. Thanks for sharing the such information with us

    devops online training

    aws online training

    data science with python online training

    data science online training

    rpa online training

    ReplyDelete
  40. I am very happy to visit your blog. This is definitely helpful to me, eagerly waiting for more updates.
    Machine Learning course in Chennai
    Machine Learning Training in Chennai

    ReplyDelete
  41. Hey, would you mind if I share your blog with my twitter group? There’s a lot of folks that I think would enjoy your content. Please let me know. Thank you.
    Java Training in Chennai | J2EE Training in Chennai | Advanced Java Training in Chennai | Core Java Training in Chennai | Java Training institute in Chennai

    ReplyDelete
  42. Thank you for sharing. Python Training in Bangalore is the most demanded training in the industry. With around 30% of jobs in the field of information technology demand good knowledge in Python programming. At Indian Cyber Security Solutions, we provide Python Training in Bangalore. This course is designed in such a way that it covers all topics from the basic to the advanced level. Python Course done by ICSS in Bangalore. Indian Cyber Security Solutions is the Best Python Institute in Bangalore.

    ReplyDelete
  43. I am happy for sharing on this blog its awesome blog I really impressed. thanks for sharing. Great efforts.

    Get Best SAP HR HCM Training in Bangalore from Real Time Industry Experts with 100% Placement Assistance in MNC Companies. Book your Free Demo with Softgen Infotech.

    ReplyDelete
  44. I must appreciate you for providing such a valuable content for us. This is one amazing piece of article. Helped a lot in increasing my knowledge.

    oracle training in bangalore

    ReplyDelete
  45. I am so happy to found your blog post because it's really very informative. Please keep writing this kind of blogs and I regularly visit this blog. Have a look at my services.  
    This is really the best Top 20 Salesforce CRM Admin Development Interview Questions highly helpful. I have found these Scenario based Salesforce developers interview questions and answers very helpful to attempt job interviews. Wow, i got this scenario based Salesforce interview questions highly helpful.

    ReplyDelete
  46. Great post! I am actually getting ready to across this information, It’s very helpful for this blog. Also great with all of the valuable information you have Keep up the good work you are doing well.
    CRS Info Solutions Salesforce training for beginners

    ReplyDelete

  47. Awesome article! It is in detail and well formatted that i enjoyed reading. which inturn helped me to get new information from your blog. After reading your article I was amazed. I know that you explain it very well. And I hope that other readers will also experience how I feel after reading your article
    Data Science Training In Chennai | Certification | Data Science Courses in Chennai | Data Science Training In Bangalore | Certification | Data Science Courses in Bangalore | Data Science Training In Hyderabad | Certification | Data Science Courses in hyderabad | Data Science Training In Coimbatore | Certification | Data Science Courses in Coimbatore | Data Science Training | Certification | Data Science Online Training Course

    ReplyDelete
  48. I want to post a remark that "The substance of your post is amazing" Great work.

    Data Science Training

    ReplyDelete
  49. A good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one
    Data Science Course in Bangalore

    ReplyDelete
  50. I adore your websites way of raising the awareness on your readers.
    Data Science Training in Bangalore

    ReplyDelete
  51. After reading your article I was amazed. I know that you explain it very well. And I hope that other readers will also experience how I feel after reading your article.4
    Best Data Science Courses in Bangalore

    ReplyDelete
  52. it is really wonderful and awesome thus it is very much useful for me to understand many concepts and helped me a lot. it is really explainable very well and i got more information from your blog.
    Salesforce Training in Chennai

    Salesforce Online Training in Chennai

    Salesforce Training in Bangalore

    Salesforce Training in Hyderabad

    Salesforce training in ameerpet

    Salesforce Training in Pune

    Salesforce Online Training

    Salesforce Training




    ReplyDelete
  53. I really enjoy the reading you blog. information shared was very helpful thank you.
    Data Science Course in Hyderabad 360DigiTMG

    ReplyDelete
  54. Excellent exchange of information ... I am very happy to read this article ... thank you for giving us information. Fantastic. I appreciate this post.

    Data Analytics Course in Bangalore

    ReplyDelete
  55. Stupendous blog huge applause to the blogger and hoping you to come up with such an extraordinary content in future. Surely, this post will inspire many aspirants who are very keen in gaining the knowledge. Expecting many more contents with lot more curiosity further.

    Digital Marketing training in Bhilai

    ReplyDelete
  56. Top quality blog with unique content and information provided was very valuable waiting for next blog update thank you .
    Ethical Hacking Course in Bangalore

    ReplyDelete
  57. Excellent blog information shared was very informative looking forward for next blog thank you.
    Data Analytics Course Online

    ReplyDelete
  58. Great post and i check your blog everyday and try to learn something from your blog. Thank you and waiting for your new post.
    unindent does not match any outer indentation level python

    ReplyDelete
  59. Good Post! , it was so good to read and useful to improve my knowledge as an updated one, keep blogging.After seeing your article I want to say that also a well-written article with some very good information which is very useful for the readers....thanks for sharing it and do share more posts likethis.
    https://www.3ritechnologies.com/course/selenium-online-training/

    ReplyDelete

  60. Really fantastic and interesting blog enjoyed reading this one waiting for next blog thanks for sharing.
    Data Science Training in Hyderabad

    ReplyDelete
  61. Tremendous blog quite easy to grasp the subject since the content is very simple to understand. Obviously, this helps the participants to engage themselves in to the subject without much difficulty. Hope you further educate the readers in the same manner and keep sharing the content as always you do.

    Data Science training

    ReplyDelete
  62. Cloudi5 is the web development company in coimbatore. cloudi5 offers so many services they are digital marketing, social media marketing, search engine optimization, landing page optimization, email marketing, website creation, website redesign, e-commerce web design, google ads, android app development and web development.

    ReplyDelete
  63. It's good to visit your blog again, it's been months for me. Well, this article that I have been waiting for so long. I will need this post to complete my college homework, and it has the exact same topic with your article. Thanks, have a good game.

    Business Analytics Course in Bangalore

    ReplyDelete
  64. Usually, I come thorough multiple blogs daily but today I found your post very relevant and informative. It is really appreciable work by you. Good luck with the upcoming work.

    Digital Marketing Services in delhi
    website development packages

    ReplyDelete


  65. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.
    Data Science Course in Chennai

    ReplyDelete
  66. I wanted to leave a little comment to support you and wish you the best of luck. We wish you the best of luck in all of your blogging endeavors.

    Ethical Hacking Training in Bangalore

    ReplyDelete
  67. It's a really cool blog. The information provided is very useful. You have really helped a lot of people who visit the blog and given them useful content.
    Cyber Security Course in Bangalore

    ReplyDelete
  68. Really, this article is truly one of the best in the article. And this one that I found quite fascinating and should be part of my collection. Very good work!
    Ethical Hacking Course in Bangalore

    ReplyDelete
  69. Really impressed! Information shared was very helpful Your website is very valuable. Thanks for sharing.
    Data Scientist Course in Jaipur

    ReplyDelete
  70. Thanks for sharing such an amazing blog! Kindly update more information
    Five Reasons to Use Google Ads
    5 Reasons to Use Google Ads

    ReplyDelete
  71. Very Informative blog thank you for sharing. Keep sharing.

    Best software training institute in Chennai. Make your career development the best by learning software courses.

    android training in chennai
    blue prism training in chennai
    Docker Training in Chennai

    ReplyDelete
  72. Needed to compose you a very little word to thank you yet again
    regarding the nice suggestions you’ve contributed here.
    hadoop training in chennai
    software testing training in chennai
    javascript training in chennai

    ReplyDelete
  73. It's like you've got the point right, but forgot to include your readers. Maybe you should think about it from different angles.'


    Business Analytics Course in Gorakhpur

    ReplyDelete
  74. Really impressed! Information shared was very helpful Your website is very valuable. Thanks for sharing.
    Business Analytics Course in Bangalore

    ReplyDelete
  75. This is great stuff!! need to share lots of articles for the reader who like your blog and thanks for sharing your ideas and tips
    1Z0-819: Oracle Java SE 11 Developer

    ReplyDelete
  76. Good work. Thanks for sharing, this blog will help many people in java configuration. Keep it up. Would you like to start with Digital Marketing Courses in Delhi? The courses are ready-to-implement with constantly updated curriculum, practical-oriented lessons, assignments and case studies, industry-recognized certificate, support for placement/internship, free demo session. The courses are suitable for all learner's level. Do visit:
    Digital Marketing Courses in Delhi

    ReplyDelete
  77. Interesting post! I appreciate you sharing this very helpful information. In order to continue blogging and expand my skill set, I would like to read your next post.
    Testing tools institute in hyderabad

    ReplyDelete
  78. "This is one of the best articles I've read on this topic. Your unique perspective and thorough analysis were both thought-provoking and insightful. Keep up the great work!"
    Modular Kitchen and Wardrobe in Raipur

    ReplyDelete