azizkhani.net

I know that I know nothing

Fluent Object Creation

clock January 23, 2013 22:02 by author Administrator
Many posts have been written on this subject (overwhelmingly many) but I just wanted to contribute my two-cents and write a short post about how I use the Fluent Object Creation pattern or object builders in Java to instantiate Value Objects.

 Value Objects are abstractions that are defined by their state (value) rather than their address in memory. Examples of value objects are things like money, a number, a coordinate, etc. They are used not to describe Business Objects but rather descriptions of concrete indivisible entities.  Also, they make great candidates for adding them to collections and maps.


In Java, Value Objects should be declared final and provide no setter methods, basically making it's state immutable after creation, this is a very important requirement. Declaring them final makes them unable to serve as parent objects. This is done by design since value objects should model small and concrete entities. The reason being is that we should be able to create and compare multiple copies of these objects, which is always done by state not by reference. In addition, you should declare proper equals() and hashCode() methods to qualify for a proper value object.

In C++, the same principles apply. In C++ you should make use of the Copy Constructor and overload the assignment and comparison operators.  

The Fluent Object Creation pattern makes value object instantiation elegant and clean. There are many benefits that can be gained by using fluent object creation as we will see shortly. 

 

 

 

The end result of applying this pattern from the API user's perspective will look like the following:

 


Money fiveEuros = new Money.Builder()
   .currency(Currency.EURO)
   .value(5.0L)
   .countryOfOrigin("Spain")
   .type("Coin")
   .reverse("Map of Europe")
   .obverse("Map of Spain")
   .addColor("Bronze")
   .addColor("Silver")
   .year("1880")
.build();


I think you would agree that this pattern flows a lot more smoother as opposed to this:


Money fiveEuros = new Money();
fiveEuros.setCurrency(Currency.EURO);
fiveEuros.setValue(5.0L);
fiveEuros.countryOfOrigin("Spain");
fiveEuros.type("Coin");
fiveEuros.reverse("Map of Europe");
fiveEuros.obverse("Map of Spain");
List<String> colors = new ArrayList<String>();
for(String color: new String[] {"Bronze", "Silver"}) {
   colors.add(color);
}
fiveEuros.setColors(colors);
fiveEuros.setYear("1880");


Which seems broken and has lots of typing and repetition. This is an example of building a pretty big  value object in my opinion, most of tend to be very small.

Before we talk about the benefits of creating objects this way, let's have a look at the structure of this pattern:


public final class Money {
   
      private Long value;
      private String countryOfOrigin;     
      private Currency currency;
      private String type; 
      private String reverse;
      private String obverse;    
      private List<String> colors;
     private Date year;    
 
      private Money() {   }

     // -- getters, hashCode, equals -- //

     // Static inner Builder class
   publicstaticclass Builder {
        private Money _temp = new Money();
 
        public Builder countryOfOrigin(String countryOfOrigin) {
_temp.contryOfOrigin = countryOfOrigin;
         returnthis;
}
 
public Builder currency(Currency c) {
_temp.currency = c;
returnthis;
}
 
public Builder type(String t) {
_temp.type = t;
returnthis;
}
 
public Builder reverse(String r) {
_temp.reverse = r;
returnthis;
}


       public Builder obverse(String o) {
_temp.obverse = o;
return this;
}


public Builder addColor(String c) {
if(_temp.colors == null) {
              _temp.colors = new ArrayList<String>(); 
           }
           _temp.colors.add(c);
return this;
}
 
         public Builder year(String y) {
              if(y == null || y.isEmpty()) {
                  _temp.year = new Date();
              }
              else {
                  _temp.year = DateFormat.parse(y);
              }
return this;
}
 
public Money build() {
                // Validate object
               if(Strings.isNullOrEmpty(_temp.name) || _temp.currency == null) {
                  throw new IllegalArgumentException("Coin currency and value required");
               }
return_temp;
}
    } }


This is also a matter of taste, but I prefer the static inner class approach. I like the canonical nature of referring to the builder as Money.Builder. Also making it static is required since the builder instance needs to live independently of the enclosing class.

I like this pattern because it has the following benefits:

  1. Greater object encapsulation: I can easily enforce object construction using builders by making the Money constructor private (this is just stylistic). This completely hides all of the intricacies of creating this object: list creation, date parsing, etc. From the user's perspective, what we end up with is an object that is simple to instantiate. My illustration is a very simple one, but imagine more complex object graphs.
  2. Code readability: Using this pattern to create objects, makes unit tests and code very easy to read and follow. 
  3. Less typing in the long run: Even though you have to add an extra builder method for every added attributes, the amount of typing you save in the long run makes it worth while. 


Conclusion

Using the fluent creation pattern is more work up front, but the benefits of having it pays off at the end. It makes instantiating objects very elegant and clean. You don't have to use it with Value Objects, most of the benefit of using Fluent Object Creation is when you need to build pretty complex object graphs, but I wanted to show that it can also suit small value objects.

 

 

 

refer:http://www.reflectivethought.net/2013/01/fluent-object-creation.html



Object Associations

clock January 20, 2013 21:37 by author Administrator


 


Composition: the Employee is encapsulated within the Company . There is no way for the outside world to get a reference to the Employee. The Employee is created and destroyed with the company

final class Company{
private final Employee Employee;
{
    Company(EmpDetails details) {
    engine = new Employee(details);
  }
   void assign() {
      emp.work();
   }
}

 

Aggregation: The Company also performs its functions through an Employee, but the Employee is not always an internal part of the Company . Employees can be exchanged, or even completely removed. As the employee is injected, the Employee reference can live outside the Company.

final class Company{
  private Employee engine;
  void addEmployee(Employee emp) {
    this.emp = emp;
  }
  void assign() {
    if (emp != null)
      emp.work();
  }
}

 

Dependency: The company does not hold the employee reference. It receives an employee reference only to the scope an operation. Company is dependent on the Employee object to perform an operation

final class Company{
  void assign(Employee emp) {
    if (emp != null)
      emp.work();
  }
}

Abstraction: Defines the basic operations the implementer should adher to. Employee interface lists the general behavior of an employee

public interface Employee{
 public void work();
 public void off();
 public void quit();
}

Realisation: A class implements the behavior defined by the other other class or interface

public abstract class Engineer implements Employee{
 public void work();
 public void off();
 public void quit();
}

Generalization: A class which is a special form of a parent class

 public class SWEngineer extends Engineer {
 public void work()
 {
  System.out.println('SW Engineer working');
 }
 public void off()
 {
  System.out.println('SW Engineer is off today');
 }
 public void quit()
 {
  System.out.println('SW Engineer is quitting');
 }

}

Association Defines a relationship between classes. Compositon and Aggregation are types of associations
Composition the Employee is encapsulated within the Company . There is no way for the outside world to get a reference to the Employee. The Employee is created and destroyed with the company
Aggregation The Company also performs its functions through an Employee, but the Employee is not always an internal part of the Company . Employees can be exchanged, or even completely removed. As the employee is injected, the Employee reference can live outside the Company.
Dependency The company does not hold the employee reference. It receives an employee reference only to the scope an operation. Company is dependent on the Employee object to perform an operation
Abstraction Defines the basic operations the implementer should adher to. Employee interface lists the general behavior of an employee
Realization A class implements the behavior defined by the other other class or interface
Generalization A class which is a special form of a parent class

UML Relationship Pointers

ref:http://techie-experience.blogspot.gr/2013/01/quick-summary-object-associations.html



Simple Cross Site Scripting (XSS) Servlet Filter

clock January 12, 2013 16:42 by author Administrator

Ran into some issues on some of our Java sites today and needed a quick fix to protect the sites from malicious Cross Site Scripting (XSS) attempts. If you're not aware of what XSS is and have websites that have sensitive user data, you may want to read up, you're probably vulnerable, which means your users are vulnerable. I'm not claiming this is a perfect solution, but it was easy to implement and corrected the vulnerabilities with form and url injection. We basically have a Servlet Filter that's going to intercept every request sent to the web application and then we use an HttpServletRequestWrapper to wrap and override the getParameter methods and clean any potential script injection.


Here's the Filter:

package com.greatwebguy.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class CrossScriptingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
    public void destroy() {
        this.filterConfig = null;
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
    }
}
   

Here's the wrapper:

 

package com.greatwebguy.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class RequestWrapper extends HttpServletRequestWrapper {
    public RequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }
    public String[] getParameterValues(String parameter) {
      String[] values = super.getParameterValues(parameter);
      if (values==null)  {
                  return null;
          }
      int count = values.length;
      String[] encodedValues = new String[count];
      for (int i = 0; i < count; i++) {
                 encodedValues[i] = cleanXSS(values[i]);
       }
      return encodedValues;
    }
    public String getParameter(String parameter) {
          String value = super.getParameter(parameter);
          if (value == null) {
                 return null;
                  }
          return cleanXSS(value);
    }
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return cleanXSS(value);
    }
    private String cleanXSS(String value) {
                //You'll need to remove the spaces from the html entities below
        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
        value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
        value = value.replaceAll("'", "& #39;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("script", "");
        return value;
    }
}

 

Add this to the top of your web.xml:

<filter>
    <filter-name>XSS</filter-name>
    <display-name>XSS</display-name>
    <description></description>
    <filter-class>com.greatwebguy.filter.CrossScriptingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>XSS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

I'm sure the cleanXSS replacements aren't the most efficient way of doing this, you could replace it StringEscapeUtils.escapeHtml from commons lang to simplify it a little, it's up to you, it all depends on what your site is doing and whether it's going to be a pain having all the html escaped, you could also adjust the url-pattern of the filter to be more specific to your application urls, so that everything under your app isn't running through the filter.

Some things to be aware of with this approach, you'll need to account for what you've encoded or in some cases you'll end up with weird characters in your database and possibly in validation of your input boxes. Some would recommend a more positive validation rather than negative validation and only allow a certain range of characters, it's up to you, but it is something to think about.

 

refrence:http://greatwebguy.com/programming/java/simple-cross-site-scripting-xss-servlet-filter/



Good Developer, Bad Developer

clock January 10, 2013 21:13 by author Administrator
Good developer Bad developer
Good developer is an artist, a craftsman who enjoys the process of creation. Bad developer considers himself as a programmer, responsible for generating lines of code.
Good developer understands the problems of the customers Bad developer understands only the technical problem at hand. Good developer does not define the why, but constantly strives to understand why. He’s responsible for the how, and still sees the big picture. Bad developer is focused on building classes and methods and configuration files, but does not get the big picture.
Good developer understands the complete architecture of the product. Bad developer knows only the components he’s written. Good developer fully understands the technologies that are used within the product. He understands what they are used for, and how they work internally.
Good developer is not afraid of new technologies but embraces them by quickly getting a grip. Bad developer only sticks to what he knows. His immediate reaction to any technical change is negative.
Good developer is constantly learning and improving his skills. Good developer reads technical articles, and finishes several technical books a year. Bad developer does not have time to learn. He’s always too busy with other stuff.
Good developer cares about the product quality. He is also very much concerned with the process quality. Good developer pushes himself to create bug-free code; bad developer leaves it to QA to find bugs to fix.
Good developer develops features which create value for customers. Bad developer completes tasks. Good developer will never claim the requirements are incomplete, and will make sure to fully understand the features he’s working on. Bad developer will wait until the finest details are available. To emphasize: good developer is the CEO of the feature – he’s going to make sure he always has the information needed to accomplish the feature, and in case information is missing he’ll make sure he gets it.
Good developer is not afraid to go into anyone’s code. Bad developer is afraid of others looking into his. Good developer understands that it shouldn’t take more time to write self-explanatory and well-documented code. Bad developer always needs to allocate extra time to document and simplify.
Good developer will never feel his code is good enough, and will always continue to clean and fix. Good developer always strives to create elegant solutions but understands that his job is to deliver value to customers. Bad developer thinks only about the elegance of his code and leave the job of delivering value to others.
   


retrieve currently logged-in users using the SessionRegistry

clock December 28, 2012 17:04 by author Administrator

http://krams915.blogspot.de/2010/12/spring-security-mvc-querying.html

 

http://code.google.com/p/spring3-security-mvc-integration-tutorial/downloads/detail?name=spring-mvc.zip&can=2&q=



Roles in the IT World

clock December 4, 2012 23:03 by author Administrator

Reference: Roles in the IT World



kent :Competence = 1 / Complexity

clock December 1, 2012 23:11 by author Administrator

This was one of my most popular tweets ever:

the complexity created by a programmer is in inverse proportion to their ability to handle complexity

I wanted to follow up a little, since some of the responses suggested that I wasn't perfectly clear (in a tweet. imagine that.)

The original thought was triggered by doing a code review with a programmer who was having trouble getting his system to work. The first thing I noticed was that he clearly wasn't as skilled as the programmers I am used to working with. He had trouble articulating the purpose of his actions. He had trouble abstracting away from details.

 

He showed me some code he had written to check whether data satisfied some criterion. The function returned a float between 0.0 and 1.0 to indicate the goodness of fit. However, the only possible return values at the moment were exactly 0.0 and 1.0. I thought, "Most programmers I know would return a boolean here." And that's when it hit me.

 

This programmer's lack of skill led him to choose a more complicated solution than necessary (the code was riddled with similar choices at all scales). At the same time, his lack of skill made him less capable of handling that additional complexity. Hence the irony: a more skilled programmer would choose a simpler solution, even though he would be able to make the more complicated solution work. The programmer least likely to be able to handle the extra complexity is exactly the one most likely to create it. Seems a little unfair.

 

I'm interested in how to break this cycle, and whether it is even possible to break this cycle. I'm certain that this programmer knew about booleans. For some reason, though, he thought he would need something more complicated, or he thought he should look impressive, or he thought the extra complexity wasn't significant, or something. How can someone with ineffective beliefs about programming be helped to believe differently?

 

wooooooow wooooooow woooooooow

 



kent beck new post

clock December 1, 2012 22:58 by author Administrator

In the optimization model of software design there are one or more goals in play at any one time--reliability, performance, modifiability, and so on. Changes to the design can move the design on one or more of these dimensions. Each change requires some overhead, and so you would like few changes, but each change also entails risk, so you would like the changes to be as small as possible, but each change creates value, so you would like changes to be as big as possible. Balancing cost, risk, and progress is the art of software design.

 

If you've been reading along, you will know that my Sprinting Centipede strategy is to reduce the cost of each change as much as possible so as to enable small changes to be chained together nearly continuously. From the outside it is clear that big changes are happening, even though from the inside it's clear that no individual change is large or risky.

 

One knock on this strategy is how it deals with the situation where incremental improvement is no longer possible, where the design has reached a local maximum. For example, suppose you have squeezed all the performance you can out of a single server and you need to shard the workload. This can be a large change to the software and can't be achieved by incremental improvements.

 

It's tempting to pull out a clean white sheet of paper when faced with a local maximum and a big trough. However, the risk compounds when replacing a large amount of functionality in one go. Do we have to give up the risk management advantages of incremental change just because have painted ourselves into a (mixed) metaphorical corner?

 

The problem is worse than it seems on the surface. If we have been making little incremental changes daily for months or years, our skills at de novo development will have atrophied. Not only are we putting a big bunch of functionality into production at once, we developed that functionality at less than 100%. Bad mojo.

 

The key is being able to abandon the other half of the phrase "incremental improvement". If we are willing to mindfully practice incremental degradation, then we can escape local maxima, travel through the Valley of Despair, and climb the new Mountain of Blessedness all without abandoning the safety of small steps. Here are some examples.

 

Suppose we have a class that is awkwardly factored into methods. Say there is 100 lines of logic, the coding standards demand no more than 10 lines per function, and someone took the original 100 line function and chopped it every 10 lines (I'm not smart enough to make this stuff up). What's the best way to get to a sensible set of helper methods? Incremental improvement is hard because related computations can easily be split between functions. Incremental degradation, though, is easy (especially with the right tools): inline everything until you have one gigantic, ugly function. With the, er..., stuff all in one pile, it's relatively easy to make incremental improvements.

 

Suppose we need to switch from one data store to another. Normalization is good, right? So the incremental way to convert is to denormalize. Everywhere we write to the old store, write to the new store. Bulk migrate all the old data. Begin reading from the new store and comparing results to make sure they match. When the error rate is acceptable, stop writing to the old store and decommission.

 

The literature and tools for incremental change betray a bias towards improvement. Fowler's "Refactoring" covers extracting methods more thoroughly than inlining them. Refactoring tools often implement varieties of extract before inline. To be fair, that's the more common direction to move. However, mastering incremental design demands being equally prepared to improve or degrade the design at any time, depending on whether it's possible to incrementally improve. In fact, sometimes when I have degraded the design and discover I still can't make incremental progress, I release my inner pig and make a really big mess.

 

tl;dr If you can't make it better, make it worse.



Top 20 Web Frameworks for the JVM

clock November 25, 2012 14:53 by author Administrator

 



50 Tricks for Faster Web Applications

clock November 25, 2012 13:05 by author Administrator

Jatinder Mann, an Internet Explorer PM at Microsoft, held the session 50 performance tricks to make your HTML5 apps and sites faster at BUILD 2012, providing many tips for creating faster web applications.

The advice provided by Mann was organized around six principles outlined below.

1. Quickly Respond to Network Requests.

  • Avoid Redirections. 63% of the top 1,000 websites use redirections. They could increase the speed of their pages by 10% by not performing a redirect.
  • Avoid Meta-refresh. 14% of the world’s URLs use meta-refreshes.
  • Minimize server response time by using CDNs located as close as possible to the user
  • Maximize the usage of concurrent connections by downloading resources from different domains
  • Reuse connections. Don’t close the connection when responding to a request.
  • Make sure data served by partner sites is not delaying page load
  • Understand the network timing components –Redirect, Cache, DNS, Request, Response, etc. –. Use the Navigation Timing API on IE 9&10 to measure the time spent by the browser on each operation.

2. Minimize Bytes Downloaded. Minimize the amount of data downloaded when a web page is loaded. The average website downloads 777KB of data out of which 474KB being images, 128KB scripts, and 84KB Flash.

  • Request gzipped content
  • Keep resources locally in packages, such as the Package Resource Index generated for the Windows Store apps. That way they are readily available when necessary.
  • Cache dynamic resources in HTML5 App Cache. This cache downloads the resources only once avoiding multiple network trips. The cache automatically re-downloads resources when the version of the application changes.
  • Provide cacheable content whenever possible by using the “Expires” field in the response.
  • Use conditional requests by setting the If-Modified-Since field of the request. 
  • Cache data requests –HTTP, XML, JSON, etc. – because about 95-96% of the requests don’t change over the day. Although a reasonable idea, less than 1% of the websites cache the requests received.
  • Standardize file naming capitalization. While a server may recognize Icon.jpg as icon.jpg, they are different resources for the web platform, generating different network requests.

3. Efficiently Structure Markup. For IE use the latest markup standardization since it is the fastest. Earlier IE6-IE9 markup styles are recognized by IE 10 but they are not as fast as the latest one.

  • Use the HTTP header field “X-UA-Compatible: IE=EmulateIE7” instead of the HTML tag <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"> to force IE run in a legacy mode that might be needed for certain business web applications. It’s faster that way.
  • Stylesheets should be linked at the top of the page inside the <head>, after the <title> in order to provide a smooth rendering.
  • Stylesheets should never be linked at the bottom of the page. The page may be flashing while loading.
  • Avoid “@import” for hierarchical styles because it synchronously blocks the creation of CSS data structures and screen painting.
  • Avoid embedded and inline styles because it forces the browser to make a context switch between the HTML and CSS parsers.
  • Only include the necessary styles. Avoid downloading and parsing style that won’t be used.
  • Link JavaScript only at the bottom of the page. This makes sure that the images, CSS, etc. are already loaded so the scripts can do they job without waiting on the resources, and avoiding context switching.
  • Do not link JavaScript in the head of the page. Use the “defer” attribute if some scripts must be loaded at the beginning.
  • Avoid inline JavaScript to avoid context switching.
  • Use the “async” attribute to load JavaScript to make the entire script loading and executing asynchronous.
  • Avoid duplicate code. 52% of the world’s web pages contain 100 lines or more of duplicate code such as linking a JavaScript file twice.
  • Standardize on one JS framework, be it jQuery, Dojo, Prototype.js, etc.. The browser won’t have to load multiple frameworks that provide basically the same functionality.
  • Don’t load scripts –FB, Twitter, etc. - just to be cool. They compete for resources.

4. Optimize Media Usage. Images are the most utilized resource, on average a website downloading 58 images.

  • Avoid downloading too many images, keeping their number to maximum 20-30 due to page load time.
  • Use image sprites to combine multiple images into one. This technique reduces the number of network connections, and the number of bytes downloaded and GPU cycles.
  • Create image sprites by hand because tools may leave large empty spaces leading to larger downloads and more GPU cycles.
  • Use PNG: best compromise between download size, decoding time, compatibility, and compression rate. JPEG may be used for photographs.
  • Use the native image resolution to avoid unnecessary bytes download and CPU processing for scaling.
  • Replace images with CSS3 gradients when possible.
  • Replace images with CSS3 border radius when possible.
  • Use CSS3 transforms to create move, rotate or skew effects.
  • Use Data URI for small single images. It saves an image download.
  • Avoid complex SVGs which require longer downloads and processing.
  • Specify an image preview when including an HTML5. The browser won’t have to download the entire video to figure out what the preview image should be.
  • Use HTML5 instead of Flash, Silverlight, or QuickTime. HTML5 is faster and the plug-in runtime takes system resources.
  • Proactively download rich media asynchronously and keep it in the app cache.

5. Write Fast JavaScript.

  • Use Integers when doing math operations in JavaScript, if possible. Floating point operations take much longer in JavaScript than their corresponding integer operations. Convert floating points into integers with Math.floor and Math.ceil, especially for computationally intensive operations.
  • Minify JavaScript code for smaller downloads and better runtime performance.
  • Initialize JS on demand. Load JS dynamically when needed.
  • Minimize DOM interactions by caching variable such as document, body, etc.
  • Use the built-in DOM code such as element.firstChild or node.nextSibling. They are highly optimized, better than what a third party library might provide.
  • Use querySelectorAll for accessing a large number of DOM elements.
  • Use .innerHTML to construct dynamic pages.
  • Batch markup changes.
  • Maintain a Small and Healthy DOM – maximum 1,000 elements.
  • JSON is faster than XML.
  • Use the browser’s JSON native methods.
  • Don’t abuse the usage of regular expressions.

6. Know What Your Application is Doing

  • Understand JavaScript timers: setTimeout and clearInterval. Don’t let timers run unless you use them for something. Also, combine timers.
  • Align timers to the display frame at 16.7 ms if the monitor refreshes at 60Hz.
  • Use requestAnimationFrame for animations to do graphics work in IE 10/Chrome/Firefox. It makes a call back when it is the time to paint, so there is no need for a timer.
  • Use the visibility API (document.hidden, Visibilityhange) to determine the visibility state of the application, and throttle down activity when the page is hidden. Saves CPU and battery life.

Mann recommended using Windows Performance Tools to measure the performance of web pages in IE and optimizing pages for less CPU time and increasing parallelism.

 



About the author

 Welcome to this web site . This page has two purposes: Sharing information about my professional life such as articles, presentations, etc.
This website is also a place where I would like to share content I enjoy with the rest of the world. Feel free to take a look around, read my blog


Java,J2EE,Spring Framework,JQuery,

Hibernate,NoSql,Cloud,SOA,Rest WebService and Web Stack tech...

RecentPosts

Month List

Sign In