Win a copy of Real-World Software Development: A Project-Driven Guide to Fundamentals in Java this week in the Agile and Other Processes forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Liutauras Vilda
  • Knute Snortum
  • Bear Bibeault
Sheriffs:
  • Devaka Cooray
  • Jeanne Boyarsky
  • Junilu Lacar
Saloon Keepers:
  • Ron McLeod
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
  • salvin francis
Bartenders:
  • Tim Holloway
  • Piet Souris
  • Frits Walraven

Static variables; ID generation and loggers

 
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This discussion was split from https://coderanch.com/t/691607/java/threadsafe-static-variable-method-related.

Let me qualify my statement in my own way: "Never EVER use static variables, with variable meaning that the value of the variable or the state of the object it references can change".

Even if the class is not intended to be thread-safe, static variables are just a no no.
 
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Let me qualify my statement in my own way: "Never EVER use static variables, with variable meaning that the value of the variable or the state of the object it references can change".

Even if the class is not intended to be thread-safe, static variables are just a no no.



That's a rather odd assertion to be making so forcibly. The technical term/qualifier for a variable that won't vary (constant) in Java is "final". There's a reason why static and final are not synonymous.

Here's a simple class illustrating a perfecty good use of a static variable:

Suppose we need a factory that can produce a sequential key value. It may or may not need to do this for multiple threads. If it does, we would make the factory method synchronized. If only one thread is ever going to request sequence values, then we can save the overhead and leave it non-synchronized. It's incidental to this discussion.

Like many factory classes, it's non-instantiated. It may serve as a base class for simular classes, but there's never going to be a need for an instance because it's only the factory method that's important.

Making this a singleton class would be overly complicated, bloating the application and introducing extra logic, thus potentially extra bugs.

So how do we satisfy all these criteria? We define a static class with a static method (the factory method) and we keep the sequence key as a static variable, incrementing it each time the factory needs to return a new value.

People do this all the time and have been doing it since before Java existed. It's one of the simplest ways of doing what should be a simple process.

I still assert that one should be careful about static-only classes. One of the most blatant warnings I get when designing with static classes is when I start creating methods with 47 parameters in them - usually an indicator that I should be passing instances around instead. But there are many cases where a stateful static class is the most elegant solution.
 
Stephan van Hulst
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:Suppose we need a factory that can produce a sequential key value.


Why? For what purpose? I can't easily think of a valid use case where this behavior isn't also dangerous.
 
Tim Holloway
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:

Tim Holloway wrote:Suppose we need a factory that can produce a sequential key value.


Why? For what purpose? I can't easily think of a valid use case where this behavior isn't also dangerous.



An extremely common case would be where you have a single-threaded app who is the sole writing client to a database where the primary key is a sequence number. The fastest way to generate a key would be to keep a "next-sequence" value in RAM. In the real-world cases I do this in, there's also a confirmation query made just to be safe (and to facilitate error recovery), but this is something that I've actually done.

Another case would be if you needed a random number generator. Mathematical RNGs (see Knuth) start from a seed and each successive value is produced based on the preceding value. Which, of course means that they aren't truly random, but that's immaterial.

A more complex case might be a blockchain generator, where the last block is referenced by the next block to be generated.



When you reference a class property, you don't have to actually have a handle on a class instance. Sometimes it's not possible to get an instance of something, but you can always get to the class, as long as a ClassLoader can find it.

A very specific, if somewhat horrible example of this is seen in JavaServer Faces. JSF backing beans MUST be POJOs. That means that they cannot promise to contain any superclasses or interfaces that can make information about the JEE HTTP or JSF context available to logic within those beans. This is information that is not serializable, so you also cannot inject it into the bean, since the bean may have session-level or broader scope, and the spec very explicitly demands that such objects be serializable (many webapp servers will outright reject beans that aren't).

Given those restrictions, you have to use the FacesContext class, since you can obtain a class anywhere without the need for injection or service lookups - in fact, in this case, the FacesContext provides the service lookup. That's the only sanctioned way to obtain such useful things as the HttpServletRequest, HttpServletResponse, HttpSession objects, [i]etc.[i] within JSF. Likewise, the FacesContext also allows accessibility to JSF's own internal mechanisms, such as the error message list. While I always maintain that the more JSF-specific code you have in a JSF webapp, the more likely you're doing it wrong, sometimes it's the only possible solution. Usually I hedge my bets and put all that stuff in a support class to minimize its overall footprint - and to make it easier to both re-use beans outside of JSF and to test beans offline - but nevertheless, it's still necessary.
 
Stephan van Hulst
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:An extremely common case would be where you have a single-threaded app who is the sole writing client to a database where the primary key is a sequence number.


Why not let the database generate IDs, and why not use GUIDs?

Another case would be if you needed a random number generator. Mathematical RNGs (see Knuth) start from a seed and each successive value is produced based on the preceding value. Which, of course means that they aren't truly random, but that's immaterial.


Why isn't the generator an object that's passed around, containing its own internal seed?

A more complex case might be a blockchain generator, where the last block is referenced by the next block to be generated.


Again, why isn't the generator an object?

That's the only sanctioned way to obtain such useful things as the HttpServletRequest, HttpServletResponse, HttpSession objects, [i]etc.[i] within JSF.


So where would the static variables be?
 
Tim Holloway
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1a. Because in the particular project in question, the database wouldn't do that properly.
1b. GUIDs are HORRIBLE database keys. They're effectively long random strings, so you cannot slice a database according to any logical function. Which can interfere with optimization and housekeeping. They have no inherent sequence, so you'd better have at least one other unique chronometric field in the table if you want to be able to access by order added. Use GUIDs as, say, invoice ID numbers and see how customers react. The one thing GUIDs are good for is (virtually) ensuring that every record in the table has a truly Globally Unique ID, regardless of where it was originally created. And that's a great help when you have a system with multiple feeds, but not for most other applications.

2. Who passes around random number generators? Unless you are specifically looking for repeatability of "random" numbers on a per-thread basis in a multi-threaded environment, one RNG is all most people need, and there's no need to instance it.

3. See Item #2. It's even less likely that you want multiple instances of a generator for a blockchain. Because it's producing a chain. A one-dimensional stream of objects, each of which securely connects to its parent object. Having more than one generator defeats the whole concept.

Unless you're supporting plug-in factories, the easier and most common method for most of us to create any sort of factory is as a class, Because, as I mentioned, classes aren't subject to propagation requirements like instances are. Even a lot of the pluggable factory systems have a static class that acts as a fa├žade for the plugged-in factory. And while many factories are idempotent, some are not, and therefore will probably be statically housing some kind of state variables.

Which reminds me. The most popular logging classes also use static accessors to obtain the logger instance. Because, again, that avoids the need to explicity inject or construct them.

4. To tell you the truth, I treat it as magic. The methods on FacesContext are accessors, so virtually speaking there are static properties behind them, but the actual means of populating them is intentionally opaque. But if you attempt to use FacesContext outside of the JSF framework (for example, in a generic servlet), those methods will fail. Likewise the static methods that return Http request objects don't return the same object on other requests.

As icing on the cake, classes will never be null. You never have to code a test to make sure that they're there. So less coding work, slightly less overhead, and if it's not set up properly, it's Someone Else's Fault.
 
Stephan van Hulst
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:1b. GUIDs are HORRIBLE database keys. They're effectively long random strings, so you cannot slice a database according to any logical function. Which can interfere with optimization and housekeeping.


Why do you want to perform such a logical function on an ID? Either the property that uniquely identifies a record is meaningful, in which can you use neither auto-increment nor a GUID, or the ID isn't meaningful, and you wouldn't perform logic on it.

They have no inherent sequence, so you'd better have at least one other unique chronometric field in the table if you want to be able to access by order added.


Okay. That's fine isn't it?

Use GUIDs as, say, invoice ID numbers and see how customers react.


This is a good argument. I don't have an elegant solution to this. I guess for invoices, I would generate an ID by mangling the properties that make the invoice unique, or use a database that DOES generate identifiers correctly.

The one thing GUIDs are good for is (virtually) ensuring that every record in the table has a truly Globally Unique ID, regardless of where it was originally created. And that's a great help when you have a system with multiple feeds, but not for most other applications.


I would argue that it's not the responsibility of the application to know this, but of the service that it uses to store data. Regardless of whether you use a GUID or something else, I don't think you should generate it statically from your application.

2. Who passes around random number generators? Unless you are specifically looking for repeatability of "random" numbers on a per-thread basis in a multi-threaded environment, one RNG is all most people need, and there's no need to instance it.


I do, in most of my classes that need a source of randomness. Greatly improves my ability to unit test the application as well.

3. See Item #2. It's even less likely that you want multiple instances of a generator for a blockchain. [...] Having more than one generator defeats the whole concept.


I don't agree that needing only one instance of an object automatically means it should be static. Singleton classes cause so much bugs in large applications. People use them because they can't be bothered to properly declare dependencies between objects. That's just lazy programming.

Which reminds me. The most popular logging classes also use static accessors to obtain the logger instance. Because, again, that avoids the need to explicity inject or construct them.


Really, what's the problem with just injecting them explicitly?
 
Tim Holloway
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK, let me first bring up a reminder that the actual topic of this thread has to do with static variable and thread safety. We have diverged from this because you assert that is no place in Java (despite the language designers' support) for static variables.

I offered some examples of real-world use cases. They were not meant to be exhaustive and I'm sure that plenty of people out there could offer more if they wished.

Now, first of all, on the topic of databases. In the Real World, we are not always given the choice of what database we may use. We have to work within its limitations. Some cases I've dealt with did not allow me to employ database-generated keys. They were paying me money to do a job, not to switch databases, so I did so and the simplest solution was a static variable.

On GUIDs and their virtues and vices. Why not use a separate sequence field from a GUID key? Because for one thing, you just kicked the can down the road. We still need an unique sequence number but now we also have a GUID which is totally useless except as a designator of uniqueness, which is something that you can do on any database index field by meta-designating it as unique. I come from a background where online storage was so expensive that we begrudged every byte and back then, something as long and ugly as as a GUID would have made management very unhappy. Storage is cheap these days, but it still adds up when you're talking billions of records. And, as I said earlier, there's no meaningful way to slice databases for optimal archiving and optimization when the key is basically random, so the operations staff doesn't care for that either.

On the other hand, there are also systems where there is no good central authority to hand out keys. I had particular grief with QuickBooks and transactions coming from satellite offices, for example, trying to get all the servers (including in-house apps) in sync without ID collisions (and Intuit assigned IDs and defined the APIs to sync, not me, so I couldn't control that). GUIDs would have been a big help here. Closer to home, I have a "to-do" app that allows me to enter and read notes from multiple sources - tablet, mobile phone, desktop. In order to ensure that each of the preceding gets exactly one and only one copy of each note without demanding continuous connection, a GUID makes sure that every note is universally unique.

JUava, incidentally has a core class to generate GUIDs. So somebody thought that applications generating GUIDs wasn't such a bad idea. Besides, if the app doesn't do it, where do you get them? Especially since if a DBMS (assuming you're connected to one) has trouble generating single sequences, something as messy as a GUID is probably not going to be available either.

I'm not sure about your point on singletons, but I do know that "lazy programming" is considered a virtue by some of the major names in the business, including Larry Wall, who designated it as one of the Three Virtues of a Great Programmer. Not sloppy programming - which is all too common, but programming that is simple, neat, and wherever possible steals from good, simple, neat - and well-debugged - sources. Coupling is bad in software. It reduces portability and it makes both changes and testing harder because of the ripple effects.

Finally, on injection. To inject something you either have to have an external agency that introspects the class to look for injectable stuff or you have to publish an explicit contract saying that you'll accept injection - a superclass, interface or annotation. The first method is high overhead. The second is mostly incompatible with POJOs, since POJOS aren't supposed to (by definition) have infrastructure-related property mandates. Yes, you can annotate a POJO for injection, but that's no good unless there's an injection framework willing to accept the annotation - CDI, Spring, or the like. Not every application runs under an IoC framework, and the loggers do not limit themselves to IoC environments. Hence the use of static locators, which are guaranteed to function in even the most basic Java run environments. And here again, although I use loggers as an example, they're not the sole example. JSF was designed to work with POJOs specifically because Struts, its predecessor, was so awful to work with thanks to all the superclass and interface mandates. Some of the same people who developed Struts also developed JSF and they were determined not to repeat the same mistakes.

Bottom line, though, is that I'm not exactly alone here. I'm quoting established and extremely popular precedents. I've no objection if you decide to write a book on Application Programming Without Static Variables, or even design and implement a programming language that excludes static variables. You could be on to something. Djikstra, after all, got a lot of static for insisting that the "go to" statement was bad. But the challenge here to provide something better. Something simpler, something cleaner, more reliable and more bug-proof. If you can do that, you, too can gain fame and possible fortune as a shaper of modern technology.

On the other hand, too many times I've had poor junior programmers post after solutions in my forums because their management arbitrarily forbade some industry-standard practice, so I tend to be a bit reactive about adding limitations unless they're probably beneficial.
 
Stephan van Hulst
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry for hijacking the topic. After this post, I'll leave it at that. Or I'll just split the topic.

I will take your points about me Putting My Money Where My Mouth Is, and about the fact that programming At Home is not the same as programming In Real Life. I am certain one never needs static variables in fresh code, but I'll have to consider how to fix existing applications in a cost-effective manner.

I have some final questions. Why is it important to maintain insertion order? I agree that for things that absolutely need to be processed in insertion order (database migrations or whatever), an increasing sequence number is the natural key. But then the ID has semantic meaning. I advocate GUIDs for records that don't have a meaningful combination of properties that make them unique. And how does an increasing sequence number help in slicing the database, when after many deletions over time, there could be many large gaps between successive records?

Can you explain what you mean by that POJOs are not supposed to have infrastructure-related property mandates? Does that mean that I can't simply pass a logger(-factory) in a constructor?
 
Tim Holloway
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry to be a party-pooper, but the database issue I keep referring to was new code. The database was IBM's DB2 and I was using Hibernate JPA2 as the access mechanism. It's entirely possible - since multiple major versions of both have been released since then - that the constraints I was operating under at the time no longer apply, but one has to make due with what's available. It obviously wasn't a choice, since I had to go to the trouble to test for the pre-existence of my internally generated sequence and repeat until I found a viable slot, and normally an auto-generated sequence number would be less coding and much less overhead for both client and server.

A database doesn't "maintain" insertion order (well, outside primitive exceptions like MS-Access and FoxPro), but there's a reason why sequences are so popular that they frequently have built-in DBMS support these days. Accounting systems like them for generating invoice and order numbers, for example. Retrieving a list of unpaid invoices in sequence number order, for example, means that you can have the fastest access to the list in a manner that is most convenient to the accounting process - since First-In/First-Out is one of the most popular ways to manage things. Ordering by date is inferior because multiple invoices or orders are very frequently created on the same date, and timestamps in databases are particularly warty. Plus if you expect people to be able to sort through invoices based on microsecond-level timestamps, you haven't seen how few brain cells most people are willing to devote to such processes. A nice simple number with, say 5 digits, is a lot easier to dig through when you have a stack of papers in hand. Not only accounting systems do this. My nightly backup system numbers each backup job that runs, for example.

Regarding slicing, it's been my experience that database deletions are more the exception than the rule. For various reasons - including audit purposes - most of the production systems I've worked with did not allow deletions. Often the only part of a system that did allow deletions was a staging area used, for example, to assemble transactions before they were either committed or rolled back (either of which caused the staging data to be deleted). So holes in the sequence are not an issue in general. On the other hand, if you have a sequence and use it for slicing, you can potentially free up entire disk drives by archiving to tape periodically. This is something more often seen in major IT shops, but then, they're the ones who employ more skilled DBA's and generally are using more sophisticated techniques for everything.

As I've said, GUIDs have a very definite set of uses. But they're not my identifier of choice because they have no semantic meaning. Their sole property is their uniqueness. If there are other properties in a table (including compound properties), that can be used to enforce uniqueness, then I prefer those, since such properties have additional and practical reasons for existence. As I said, storage space isn't as critical an issue as it used to be, but why waste it gratuitously? GUIDs take up a lot of space, relative to simple numeric and short-sequence character fields. And may Ganesha help you if you have read them out to someone over the phone. I have enough problems with things like MAC addresses. A touch of dyslexia isn't helping any, either.

And finally:

Stephan van Hulst wrote:Can you explain what you mean by that POJOs are not supposed to have infrastructure-related property mandates? Does that mean that I can't simply pass a logger(-factory) in a constructor?



Yes, I can. POJO means "Plain Old Java Object". Once you cannot arbitrarily substitute one bean for another, you've locked yourself in. You now have coupling issues and code reuse limitations. That doesn't mean that you cannot inject complex objects into a POJO, just that it means that you must not require a class of POJO objects to accept a specific property or properties, since then it's by definition no longer a POJO.

Also, it is an essential requirement in both JavaServer Faces (JSF) and in Java Persistence Architecture (JPA) that every bean must have a no-element constructor. For JPA, there is no way to define arguments for the constructor that JPA will use to auto-instantiate the bean. For JPA, JPA needs a no-element constructor for when it internally assembles entity objects. Because in many cases, JPA will not be populating those objects from application logic, it will be populating them from database value retrieval and that means that it simply won't have any knowledge of things like which logger you are using. Or what e-mailer, or any of a number of other such services that can be found via a static service locator without direct propagation from the calling framework.

Finally, I should mention that things like loggers, JDBC Connections, etc. are not serializable. This is critical in JSF and JPA. In fact, Tomcat will flatly refuse to accept a session-scope object that doesn't at least claim to be serializable*. For one thing, when a serializable object is serialized out and back in again, any non-serializable properties do not get initialized, which can break things. A logger retrieved from a static method as part of the internal initializer code doesn't have this problem.

---
*Yes, I realize that forcing a POJO to to implement java.io.Serializable sounds like a violation of the definition. But it gets a pass since Serializable is a class characteristic, not a promise to implement any sort of logic or specific class or method properties. And because it's a core JVM feature and therefore guaranteed to always be there.
 
Stephan van Hulst
Saloon Keeper
Posts: 11470
247
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Tim, food for thought. One thing:

Tim Holloway wrote:POJO means "Plain Old Java Object". Once you cannot arbitrarily substitute one bean for another, you've locked yourself in. You now have coupling issues and code reuse limitations. That doesn't mean that you cannot inject complex objects into a POJO, just that it means that you must not require a class of POJO objects to accept a specific property or properties, since then it's by definition no longer a POJO.


I still don't understand what you mean by this. Why isn't this a POJO:
 
Tim Holloway
Bartender
Posts: 21721
148
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As far as I can see offhand, it is a POJO. However, it's not what I might call a "universal" POJO. It cannot be instantiated in isolation.

As I mentioned previously, certain frameworks, including, but not limited to JavaServer Faces and Java Persistence Architecture demand the existence of a no-argument constructor. That's because they reserve the right to be able to instantiate their managed objects in total isolation without knowledge of objects outside their environment, such as your dataSource and loggerFactory. It's permissible and common to allow injection of such properties via "put" methods after construction, but the problem there is that you don't know what order the managing framework will inject the separate properties in and for the most part cannot and "should" not know, so there's a risk of failure by invoking a resource that hasn't been injected yet.

I'm not sure that a bean that lacks a no-element constructor can even be serialized properly.

Pet peeve. Code like this:


raises my hackles. I cannot count the times I've accidentally done this:


And sometimes spent hours overlooking the obvious.

Because of that, I always bracket my conditional statement clauses.

Any time "wasted" in adding a pair of braces is more than repaid by not playing "I only made 1 change why isnt it working?" And my IDE automatically pairs the braces, so I only have to type 1 of them anyway.
 
no wonder he is so sad, he hasn't seen this tiny ad:
Java file APIs (DOC, XLS, PDF, and many more)
https://products.aspose.com/total/java
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!