0

My singleton is called Resources. It should only be instantiated once by this Singleton standard I used:

package site.kevindhu.models;

import site.kevindhu.entity.Player;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Resources {
    public static Resources resources;
    public Map<String, Object> data;

    static {
        resources = new Resources();
    }

    private Resources() {
        data = new HashMap<>();
        data.put("players", new HashSet<Player>());
        data.put("newPlayers", new HashSet<Player>());
    }


    public static Resources getInstance() {
        return resources;
    }
}

However, it is not working correctly!

When I deploy a .ear to run my glassfish server, it goes into this block twice:

static {
        resources = new Resources();
    }

As a result, the "singleton" actually creates two different Resources each time I run the server.

I know I do twice because I debug it calls two different Resources objects whenever I attempt to call Resources.resources.

Is this possibly because I am deploying a .ear file? How do the specifics of this double instantiation work?

pvg
  • 2,673
  • 4
  • 17
  • 31
Kevin Hu
  • 41
  • 9
  • Most likely your class is getting loaded by different classloaders. Depending on what you're writing, a custom singleton like this is probably not the best of ideas in an app server. – pvg Jun 04 '17 at 01:21
  • I would also suggest it is a classloader issue. Are there multiple WAR files within your EAR? Each one will likely have its own ClassLoader, and therefore a separate instance of Resources. See [this answer](https://stackoverflow.com/a/4132721/3586783) for a little more info. – pacifier21 Jun 04 '17 at 01:45
  • I actually changed my deployment to a .war file, and it still occuring. – Kevin Hu Jun 04 '17 at 01:51
  • Again, that doesn't really mean anything much other than that different classloaders are loading your class. How that happens also depends on how and where you're referring to it. There are plenty of facilities in the EE apis to handle app or module specific resources and resource mapping, there's really no point trying to reinvent them poorly. – pvg Jun 04 '17 at 01:59
  • Update, I've narrowed down that when I call methods that use Resources.resources, it will call it TWICE in the same method call. One time, the debugger will show all the variables as BLUE, and the other time, it will be RED. Here is what I mean: [link](http://imgur.com/a/rCHes) – Kevin Hu Jun 04 '17 at 02:05
  • The Resources class here isn't too much, it just stores a HashMap that I can call later. Are you saying there is a much better EE API available I should use to store this hashmap? I want this to have global access (anyone can access the hashmap) and be instantiated right from the start – Kevin Hu Jun 04 '17 at 02:08
  • Yes there are better apis for this. Plus this sort of thing is asking for concurrency trouble. As to the debugger, all that is really necessary is for two different classloaders to load this class. What matters is, they won't interact so it really should not make any difference to you. – pvg Jun 04 '17 at 02:39

1 Answers1

0

The best way to go is to let the compiler handle it for you:

/** Singleton. */
public enum Resources {
  RESOURCES;

  private final Map<String, Team> teams = new HashMap<>();

  public boolean add(Team team) {
    return team != null
        && teams.put(team.getName(), team) == null;
  }

  public Team find(String name) {
    return name == null ? null : teams.get(name);
  }

  public Team find(Team team) {
    return team == null ? null : get(team.getName());
  }

  public Map<String, Team> getTeams() {
    return Collections.unmodifiableMap(teams);
  }

  // remove, iterators, etc.
}

public class TeamImpl implements Team {
  private final String name;
  private final Map<String, Player> roster = new HashMap<>();

  public TeamImpl(String name) {
    if (name == null) {
      throw new IllegalArgumentException("name must not be null");
    }
    this.name = name;
    assert this.name != null;
  }

  @Override
  public boolean equals(Object other) {
    // base comparison on team name 
  }

  @Override
  public int hashCode() {
    assert this.name != null;
    return name.hashCode();
  }

  // methods from interface Team:

  @Override
  public String getName() {
    return name;
  }

  @Override
  public Set<Player> getRoster() {
    return Collections.unmodifiableSet(new HashSet<>(roster.values()));
  }

  @Override
  public boolean add(Player player) {
    return player != null
        && roster.put(player.getName(), player) == null;
  }

  @Override
  public Player find(String name) {
    return name == null ? null : roster.get(name);
  }

  // remove, iterators, etc.
}
Lew Bloch
  • 3,364
  • 1
  • 16
  • 10