So I've been trying to implement a proper EventStream solution using Play Framework 1.3, however I just can't get it to work properly. An exception keeps getting thrown but I'm not sure what the cause is.
Can anyone provide an example implementation or point me in the direction of one?
This is the method I have in my controller.
public static void stream() {
response.contentType = "text/event-stream";
response.encoding = "UTF-8";
response.status = 200;
response.chunked = true;
// a record of the last ID that this request... requested
Long lastId = 0L;
// since this request has only been initiated, the first thing to do is return a list of whats in the queue
List<F.IndexedEvent> archive = StreamQueue.getRecentNotifications(lastId);
if (!archive.isEmpty()) {
// write that list out in the response
response.writeChunk(//a string representing the entire list as a single event);
// update out internal reference to the lastId
lastId = archive.get(archive.size() - 1).id;
}
// keep this connection alive
while (true) {
// await the promise of more notifications for the stream
List<F.IndexedEvent<StreamQueue.Notification>> notifications = await(StreamQueue.getNextNotification(lastId));
// for each notification, write it out as a separate event
for (int i = 0; i < notifications.size(); i++) {
response.writeChunk(//a string representing a single event using notifications.get(i));
// update out internal reference to the lastId
lastId = notifications.get(i).id;
}
}
}
And this is my implementation of the queue StreamQueue.java:
public class StreamQueue {
final ArchivedEventStream<StreamQueue.Notification> notifications = new ArchivedEventStream<StreamQueue.Notification>(100);
static StreamQueue instance = null;
public static StreamQueue get() {
if(instance == null) {
instance = new StreamQueue();
}
return instance;
}
// the method to call when I want to send an event
public static void alert(Notification.Type type, Object... args){
get().addMessage((String) args[0]);
}
public static F.Promise<java.util.List<F.IndexedEvent<Notification>>> getNextNotification(Long lastId) {
return get().getNotificationsStream().nextEvents(lastId);
}
public static java.util.List<F.IndexedEvent> getRecentNotifications(Long lastId) {
return get().getNotificationsStream().availableEvents(lastId);
}
public ArchivedEventStream<StreamQueue.Notification> getNotificationsStream() {
return notifications;
}
private void addMessage(String message){
notifications.publish(new StreamQueue.MessageEvent(message));
}
public static abstract class Notification extends F.IndexedEvent {
public enum Type {
ISSUE,
MESSAGE;
}
final public Type type;
final public Long timestamp;
public Notification(Type type) {
super(type);
this.type = type;
this.timestamp = System.currentTimeMillis();
}
public Long getId() {
return this.id;
}
public String toJson() {
return new JSONSerializer().exclude("class").serialize(this);
}
}
public static class MessageEvent extends StreamQueue.Notification {
public final String message;
public MessageEvent(String message) {
super(Type.MESSAGE);
this.message = message;
}
public String toJson() {
return new JSONSerializer().include("message").exclude("*").serialize(this);
}
}
}
And finally, here's the exception I'm getting:
Internal Server Error (500) for request GET /stream
Execution exception
InvocationTargetException occured : null
play.exceptions.JavaExecutionException
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:230)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.reflect.InvocationTargetException
at play.mvc.ActionInvoker.invokeWithContinuation(ActionInvoker.java:524)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:475)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:451)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:446)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:160)
... 1 more
Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.Map
at controllers.StreamController.stream(StreamController.java)
... 6 more
The exception seems to be thrown as a result of ArchivedEventStream.nextEvents(...) which I'm calling from StreamQueue.getNextNotification(...). The cause appears to be a Long being cast to a Map but at no point do I attempt to do that.
Thanks in advance for your help!
UPDATE
So I tried my project in Play 1.2.5 and the streaming works perfectly. However, the project demands we use Play 1.3.
The cause is at bottom of the stacktrace:
Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.Map
I think you are trying to cast Notification.timestamp to map in StreamController.
Related
Hi
In the game Minecraft you have to send data between the client and the server in order to sync stuff, one of the things that need to be synced in particular circumstances is TileEntities with a render. The data is stored on the server and sent to the client which is thereafter used for render.
You create a class that contains the data that needs to be synced and attach it to a channel creation API called "SimpleImpl". My Network object and a Message Object:
public class IntercraftPacketHandler
{
private static int index = 1;
private static final ResourceLocation CHANNEL_NAME = new ResourceLocation(Reference.MODID,"network");
private static final String PROTOCOL_VERSION = new ResourceLocation(Reference.MODID,"1").toString();
public static SimpleChannel getNetworkChannel()
{
final SimpleChannel channel = NetworkRegistry.ChannelBuilder.named(CHANNEL_NAME)
.clientAcceptedVersions(version -> true)
.serverAcceptedVersions(version -> true)
.networkProtocolVersion(() -> PROTOCOL_VERSION)
.simpleChannel();
// Sync Capability Identity Hidden data message.
channel.messageBuilder(MessageIdentityHidden.class,index)
.encoder(MessageIdentityHidden::encode)
.decoder(MessageIdentityHidden::decode)
.consumer(MessageIdentityHidden::handle)
.add(); index++;
// Send TreeTapTileEntity data to client.
channel.messageBuilder(MessageTreeTap.class,index)
.encoder(MessageTreeTap::encode)
.decoder(MessageTreeTap::decode)
.consumer(MessageTreeTap::handle)
.add(); index++;
// Send ChunkLoaderTileEntity data to client.
channel.messageBuilder(MessageChunkLoader.class,index)
.encoder(MessageChunkLoader::encode)
.decoder(MessageChunkLoader::decode)
.consumer(MessageChunkLoader::handle)
.add(); index++;
return channel;
}
}
public class MessageChunkLoader
{
private BlockPos pos;
private boolean canLoad;
public MessageChunkLoader(BlockPos pos,boolean canLoad)
{
this.pos = pos;
this.canLoad = canLoad;
}
public void handle(Supplier<NetworkEvent.Context> ctx)
{
ctx.get().enqueueWork(() -> {
try {
ChunkLoaderBaseTileEntity tile = (ChunkLoaderBaseTileEntity) Minecraft.getInstance().world.getTileEntity(pos);
tile.canLoad = canLoad;
} catch (NullPointerException err) {
System.out.println(String.format("Could not find ChunkLoaderTileEntity at %s %s %s!",pos.getX(),pos.getY(),pos.getZ()));
}
});
}
public static void encode(MessageChunkLoader message, PacketBuffer buffer)
{
buffer.writeBlockPos(message.pos);
buffer.writeBoolean(message.canLoad);
}
public static MessageChunkLoader decode(final PacketBuffer buffer)
{
return new MessageChunkLoader(buffer.readBlockPos(),buffer.readBoolean());
}
}
I then initialize it in my main mod class used by objects in my mod project.
#Mod(Reference.MODID)
public class IntercraftCore
{
public static final SimpleChannel NETWORK = IntercraftPacketHandler.getNetworkChannel();
...
The problem and this post's question; right now I create a new message class from the formula I follow in MessageChunkLoader (public static encode & decode method and a handle method). I would like to create a more generic class for creating message classes for TileEntities, but I'm having problems with that. Here's the current class:
public abstract class MessageTileEntity<T extends TileEntity>
{
protected final BlockPos pos;
protected final Class<T> clazz;
public MessageTileEntity(BlockPos pos, Class<T> clazz)
{
this.pos = pos;
this.clazz = clazz;
}
public abstract void handle(Supplier<NetworkEvent.Context> ctx);
protected T getTileEntity()
{
try {
return clazz.cast(Minecraft.getInstance().world.getTileEntity(pos));
} catch (NullPointerException e) {
System.out.println(String.format("Could not find %s at [%d %d %d]!",clazz.getSimpleName(),pos.getX(),pos.getY(),pos.getZ()));
throw e;
} catch (ClassCastException e) {
System.out.println(String.format("TileEntity at [%d %d %d] is not %s!",pos.getX(),pos.getY(),pos.getZ(),clazz.getSimpleName()));
throw e;
}
}
public static void encode(MessageTileEntity message, PacketBuffer buffer)
{
}
public static MessageTileEntity decode(final PacketBuffer buffer)
{
return null;
}
}
The main problem is I lack the proper Java skills to make it like I want it to function. The method handle is easy as it's non-static and needs to be custom to every TileEntity message, but the methods encode and decode which needs to be static gives me problems. I have no idea what I'm trying to achieve is possible, asking won't hurt. Maybe the solution is easier than I think.
I'm a novice at java so please forgive if the answer is obvious. I've been tasked with converting a java project to Maven and I'm running into the below compilation error.
no suitable constructor found for EtHttpException(java.lang.String,java.lang.String)
FWIW this same error occurs in multiple other classes
Base Class
public class EtHttpException extends Exception {
private java.lang.String currentURL = null;
private java.lang.String status = null;
private java.lang.String request = null;
/**
EtHttpException constructor comment.
*/
public EtHttpException(String status, String newCurrentURL,Exception e) {
super(status);
currentURL = newCurrentURL;
}
public EtHttpException(String status, String newCurrentURL, String newRequest,Exception e) {
super(status);
currentURL = newCurrentURL;
request = newRequest;
}
public java.lang.String getCurrentURL() {
return currentURL;
}
public java.lang.String getRequest() {
return request;
}
}
Error occurs here
public class EtHttpsConnection {
private String sendRequest(String requestMessage, String currentURL)
throws EtHttpException {
String responseMessage = null;
int size = 0;
int offset = 0;
int length = 400;
byte[] buffer = new byte[4096];
try {
setRequest(requestMessage);
responseMessage=connect(currentURL);
trace(currentURL);
trace(requestMessage);
String postRequestMessage =
transformMessageToPostFormat(requestMessage, currentURL);
trace(postRequestMessage);
} catch (Exception e) {
e.printStackTrace();
String msg = e.toString();
disconnect();
throw new EtHttpException(msg, currentURL);
}
// return response
return responseMessage;
}
Additional place error occurs
no suitable constructor found for EtHttpException(java.lang.String,< nulltype >)
public class NoCurrentURLException extends EtHttpException {
public NoCurrentURLException() {
super("No current URL was retrieved from URLList", null);
}
}
I'm fairly sure there's one place I can make a change that will resolve these errors, can someone provide any insight?
no suitable constructor found for
EtHttpException(java.lang.String,java.lang.String)
The error message is telling you that there is no constructor for EtHttpException which takes two Strings. If you look at EtHttpException, you'll see two constructors: one takes two Strings and an Exception; the other takes three Strings and an Exception.
You're trying to use two Strings - there is not a constructor which matches this signature.
To fix it, change the point where you throw an exception in your EtHttpsConnection class to:
throw new EtHttpException(msg, currentURL, e);
Then change the constructor of your NoCurrentURLException class to use something like this:
super("No current URL was retrieved from URLList", null, null);
I am building a RSS reader using the Play Framework, play authenticate, and mongodb accessed through Morphia. It's running on a Ubuntu server with a VIA Nano processor U2250 (1.6GHz Capable) and 2ghz ram apparently. Despite every tweak and optimization I could think of, I still observe insane delays in the answering time: from 2seconds to 1 minute to answer a simple HTTP GET request to mark an item as read (I'll talk about this example because it is simple). Note that i am not facing this problem on local tests on my personal machine, just on remote deployment.
Profiling gives me the following distribution of spent time to answer a dozen "mark as read" requests :
org.bson.io.PoolOutputBuffer.pipe() 11%
org.bson.io.Bits.readFully() 10%
scala.concurrent.forkjoin.ForkJoinPool.scan() 8%
org.jboss.netty.channel.socket.nio.SelectorUtil.select() 7%
org.bson.io.PoolOutputBuffer.write 5%
com.google.code.morphia.mapping.DefaultCreator.getNoArgsConstructor() 5%
from which I gathered the I/O with the database was the bottleneck. I have made the references explicit using a custom class Ref<> because Key<> was handling package names fairly poorly. Therefore the aforementioned mark as read request only does 3 db queries : retrieve the item by its ref (id), retrieve the inbox by its ref (id) stored in the session cache, remove the item from the inbox and store it back again. My interactions with the database are encapsulated in a static helper class :
public class MorphiaHelper {
static private Mongo mongo;
static private Morphia morphia;
static private Datastore datastore;
public static void mapModels() {
morphia.map(...);
}
public static void startup() {
try {
mongo = new MongoClient();
morphia = new Morphia();
datastore = MorphiaHelper.morphia.createDatastore(MorphiaHelper.mongo, "dbname");
mapModels();
updateModelsIfNeeded();
datastore.ensureIndexes();
datastore.ensureCaps();
} catch (Exception e) {
Logger.error("Database failed to initialize:" + e.getMessage());
}
Logger.debug("** mongo and morphia initialization on datastore: " + MorphiaHelper.datastore.getDB());
}
public static void shutdown() {
mongo.close();
Logger.debug("** mongo and morphia closed **");
}
public static <T> Key<T> save(T entity){
return datastore.save(entity);
}
public static <T, V> T get(Ref<V> ref) throws NotFoundException{
try {
Class<T> classT = (Class<T>)Class.forName(ref.getClassName());
return datastore.get(classT, ref.getId());
} catch (ClassNotFoundException e) {
throw new NotFoundException("Reference to morphia not found:" + ref.toString());
}
}
...
}
and rely on the aforementionned inner static Ref class :
public static interface Referencable{
Ref getRef();
}
public static class Ref<T> implements Serializable, Comparable {
Map<String, String> id;
private static String nameToKey(String name){
return name.replace(".", " ");
}
private static String keyToName(String key){
return key.replace(" ", ".");
}
public static <T> Ref<T> fromIdString(Class<T> className, String idString) {
return new Ref<T>(className, idString);
}
public static Ref<?> fromString(String sourceRefString) throws ClassNotFoundException {
if (! sourceRefString.contains("=")){
throw new ClassNotFoundException();
}
String[] parseString = sourceRefString.split("=", 2);
Class<?> className = Class.forName(keyToName(parseString[0]));
return fromIdString(className, parseString[1]);
}
#Deprecated
private Ref() {}
public Ref(Class<T> _className, String _id) {
id = Collections.singletonMap(nameToKey(_className.getName()), _id);
}
private Entry<String, String> getIdFromMap(){
return id.entrySet().iterator().next();
}
public Object getId(){
return getIdFromMap().getValue();
}
public String toString(){
return getIdFromMap().toString();
}
#Override
public boolean equals(Object other){
return (other.getClass().equals(this.getClass())
&& ((Ref)other).getIdFromMap().getKey().equals(this.getIdFromMap().getKey())
&& ((Ref)other).getIdFromMap().getValue().equals(this.getIdFromMap().getValue()));
}
#Override
public int hashCode() {
return getIdFromMap().hashCode();
}
public String getClassName() {
return keyToName(getIdFromMap().getKey());
}
#Override
public int compareTo(Object arg0) {
return this.toString().compareTo(((Ref) arg0).toString());
}
}
Since I haven't been able to clearly locate the problem, I am also offering you just in case the code of the controller :
public static Result read(final String id) throws NotFoundException{
Item item = MorphiaHelper.get(Ref.fromIdString(Item.class, id));
Application.getLocalInbox().remove(item);
return ok();
}
This is Application, getting us the inbox :
public class Application extends Controller {
public static Inbox getLocalInbox() throws NotFoundException {
return Inbox.getInboxOfUser(getLocalUserRef());
}
public static User getLocalUser() throws NotFoundException {
User user = UserDAO.findUser(PlayAuthenticate.getUser(session()));
session("UserRef", user.getRef().toString());
return user;
}
public static Ref<User> getLocalUserRef() throws NotFoundException {
if (session("UserRef") == null){
session("UserRef", getLocalUser().getRef().toString());
}
try {
return (Ref<User>) Ref.fromString(session("UserRef"));
} catch (Exception e){
throw new NotFoundException("could not retrieve current user ref");
}
}
}
So yeah nothing very interesting here. This is inbox.remove :
public void remove(Item item) {
// TODO can be optimized if we dont check the existence of tags
boolean listContainedElement = false;
for(Ref<Tag> tag : item.getTagsRef()){
List<Ref<Item>> list = inbox().get(tag);
if (list != null){
Ref<Item> key = item.getRef();
boolean remove = list.remove(key);
listContainedElement = listContainedElement || remove;
if (list.size() == 0){
inbox().remove(tag);
}
}
}
List<Ref<Item>> list = inbox.get(Tag.getUntagged());
if (list != null){
listContainedElement = listContainedElement || list.remove(item.getRef());
}
if (listContainedElement) seen(item);
MorphiaHelper.save(this);
}
where
Map<String, List<Ref<Item>>> inbox = new HashMap<String, List<Ref<Item>>>();
Map<Ref<Tag>, List<Ref<Item>>> inbox(){
return new MorphiaMap(inbox);
}
is a way to bypass the fact that morphia cannot handle anything else than Strings as map keys and keep code clean, MorphiaMap being a class storing an attribute "Map innerMap;" and forwarding all the commands with the String to Ref translation.
I'm completely at a loss here so any kind of advice would be much appreciated. I've been tweaking the system for weeks to improve performances but I'm afraid I'm missing the elephant in the room.
Thank you in advance
Best regards
I have some class storing keys with important information. No one else is allowed to create a key, since a key relys on static information (like certain directory structures etc.).
public final class KeyConstants
{
private KeyConstants()
{
// could throw an exception to prevent instantiation
}
public static final Key<MyClass> MY_CLASS_DATA = new Key<MyClass>("someId", MyClass.class);
public static class Key<T>
{
public final String ID;
public final Class<T> CLAZZ;
private Key(String id, Class<T> clazz)
{
this.ID = id;
this.CLAZZ = clazz;
}
}
}
This example is simplyfied.
I wanted to test the consequences of a wrong key (exception handling, etc.) and instantiated the class via reflection in a JUnit test case.
Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(String.class, Class.class);
c.setAccessible(true);
#SuppressWarnings ("unchecked")
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c.newInstance("wrongId", MyClass.class);
Then I asked myself how could I prevent further instantiation of the key class (i. e. preventing further object creating via reflection)?
enums came to my mind, but they don't work with generics.
public enum Key<T>
{
//... Syntax error, enum declaration cannot have type parameters
}
So how can I keep a set of n instances of a generic class and prevent further instantiation?
So how can I keep a set of n instances of a generic class and prevent
further instantiation?
If you truly want to use this pattern, then no one (including you) should be able to instantiate a Key object. In order to keep a set of n instances in a class with this pattern, you could have a private constructor, a static method for access and a SecurityManager to prevent reflection. And since you want to be able to access the keys as pubic constants, I would try something like this..
public class KeyConstants{
// Here are your n instances for public access
public static final int KEY_1 = 1;
public static final int KEY_2 = 2;
.
.
.
public static final int KEY_N = 'n';
// now you can call this method like this..
// Key mKey = KeyConstants.getKey(KeyConstants.KEY_1);
public static Key getKey(int key){
List keys = Key.getInstances();
switch(key){
case KEY_1:
return keys.get(0);
case KEY_2:
return keys.get(1);
.
.
.
case KEY_N:
return keys.get(n);
default:
// not index out of bounds.. this means
// they didn't use a constant
throw new IllegalArgumentException();
}
}
static class Key<T>{
private static List<Key> instances;
private String ID;
private Class<T> CLAZZ;
private Key(String id, Class<T> clazz){
this.ID = id;
this.CLAZZ = clazz;
}
public static List<Key> getInstances(){
if(instances == null){
instances = new ArrayList<Key>();
//populate instances list
}
return instances;
}
}
}
Use SecurityManager to prevent reflection access.
//attempt to set your own security manager to prevent reflection
try {
System.setSecurityManager(new MySecurityManager());
} catch (SecurityException se) {
}
class MySecurityManager extends SecurityManager {
public void checkPermission(Permission perm) {
if (perm.getName().equals("suppressAccessChecks"))
throw new SecurityException("Invalid Access");
}
}
This will throw a SecurityException anytime someone attempts to access a private variable or field in your class (including access attempts via reflection).
I'm not sure I fully understand your question, but if a private constructor is not sufficient, can you use a more dynamic approach and throw an exception in the constructor after a signal is given? For example:
public static class Key<T>
{
private static boolean isLocked = false;
// Call this method when you want no more keys to be created
public static void lock() { isLocked = true; }
...
private Key(String id, Class<T> clazz)
{
if (isLocked) throw new IllegalStateException("Cannot create instances of Key");
this.ID = id;
this.CLAZZ = clazz;
}
}
Then - and this is the disadvantage - you will have to call Key.lock() once you want to prevent more instances being created.
As you showed in your code to prevent instantiating KeyConstants you can throw some Exception inside private-non-argument constructor.
Harder part is way to block creating KeyConstants.Key constructor from outside of KeyConstants class.
Some wild idea
Maybe create Exception in your constructor and check how its stack trace looks like. When I add this code to constructor
private Key(String id, Class<T> clazz) {
StackTraceElement[] stack = new Exception().getStackTrace();
for (int i=0; i<stack.length; i++){
System.out.println(i+") "+stack[i]);
}
this.ID = id;
this.CLAZZ = clazz;
}
and create instance of Key with reflection like
Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(
String.class, Class.class);
c.setAccessible(true);
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c
.newInstance("wrongId", MyClass.class);
I get
0) KeyConstants$Key.<init>(Test.java:38)
1) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
2) sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
3) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
4) java.lang.reflect.Constructor.newInstance(Constructor.java:525)
so maybe just if 4th element of stack is java.lang.reflect.Constructor.newInstance throw Exception to prevent executing rest of constructors code like:
if (stack.length>=4 && stack[4].toString().startsWith("java.lang.reflect.Constructor.newInstance")){
throw new RuntimeException("cant create object with reflection");
}
I came across some Multiton patterns recently, where I tried to handle problems with unique enum keys, that gave me the idea of another approach.
The keys can be used for information flow as I intended, or even as keys for typesafe heterogeneous container, where they can perform compile-time casting.
Key-defining class
public class KeyConstants
{
public static final KeysForIntegers SOME_INT_KEY = KeysForIntegers.KEY_2;
public static final KeysForStrings SOME_STRING_KEY = KeysForStrings.KEY_1;
public interface Key<Type>
{
public Class<Type> getType();
}
/* Define methods that classes working with the keys expect from them */
public interface KeyInformation
{
public String getInfo1();
// and so on...
}
public enum KeysForStrings implements Key<String>, KeyInformation
{
KEY_1("someId");
public final String ID;
private KeysForStrings(String id)
{
ID = id;
}
#Override
public String getInfo1()
{
return "Good piece of information on " + ID + ".";
}
#Override
public Class<String> getType()
{
return String.class;
}
}
public enum KeysForIntegers implements Key<Integer>, KeyInformation
{
KEY_2("bla");
public final String ID;
private KeysForIntegers(String id)
{
this.ID = id;
}
#Override
public String getInfo1()
{
return "Some info on " + ID + ".";
}
#Override
public Class<Integer> getType()
{
return Integer.class;
}
}
}
Example key-using class
public class KeyUser
{
public static void main(String[] args)
{
KeysForIntegers k1 = KeyConstants.SOME_INT_KEY;
KeysForStrings k2 = KeyConstants.SOME_STRING_KEY;
processStringKey(k2);
useIntKey(k1);
Integer i = useIntKey(KeyConstants.SOME_INT_KEY);
processStringKey(KeyConstants.SOME_STRING_KEY);
}
/* My methods should just work with my keys */
#SuppressWarnings ("unchecked")
public static <TYPE, KEY extends Enum<KeysForIntegers> & Key<TYPE> & KeyInformation> TYPE useIntKey(KEY k)
{
System.out.println(k.getInfo1());
return (TYPE) new Object();
}
public static <KEY extends Enum<KeysForStrings> & KeyInformation> void processStringKey(KEY k)
{
System.out.println(k.getInfo1());
// process stuff
}
}
I have another approach, you can bound an interface in a way to only be implemented by enum.
With that approach you have a fixed set of instances at compile time.
If you want to add lazy loading, the enums implementing it should be proxies that load the desired object if it is requested. The class or classes that are hidden behind the proxies should only be visible to them, so that they have exclusive access to the constructor.
public class User {
public static <S> S handleKey(FixedInstanceSet<S,?> key) {
return key.getKey();
}
}
interface FixedInstanceSet<S, T extends Enum<T> & FixedInstanceSet<S,T>>
{
public S getKey();
}
enum StringKeys implements FixedInstanceSet<String, StringKeys> {
TOP, DOWN, LEFT, RIGHT;
#Override
public String getKey() { return null; }
}
enum IntKeys implements FixedInstanceSet<Integer, IntKeys > {
TOP, DOWN, LEFT, RIGHT;
#Override
public Integer getKey() { return null; }
}
/*
* Bound mismatch: The type NotWorking is not a valid substitute for the bounded
* parameter <T extends Enum<T> & FixedInstanceSet<S,T>> of the type
* FixedInstanceSet<S,T>
*/
//class NotCompiling implements FixedInstanceSet<String, NotCompiling> {
//
// #Override
// public String getKey() { return null; }
//}
If I understand you correctly, you don't want your class to be instantiated.
You can set the default constructor to private
private Key() throws IllegalStateException //handle default constructor
{
throw new IllegalStateException();
}
This will prevent its improper instantiation.
Update:
added throw IllegalStateException
So i'm a little bit confused as i've never used an enum before. I want to use this enum in my main method. For some reason, i can't (i keep getting errors anytime i even try to do Status s; in main). I can however call my TestingEnum method from main and of course this works... but i am 100% sure that using the enum this way is just plain wrong. Could someone tell me how i'd go about using this in main properly?
If i try to do: Status s; in my main method, i get this error - "connot find symbol Status s;"
BACKGROUND: new to java and enums...
class MyClass {
public Status s;
public enum Status {
STATUS_OPEN(1),
STATUS_STARTED(2),
STATUS_INPROGRESS(3),
STATUS_ONHOLD(4),
STATUS_COMPLETED(5),
STATUS_CLOSED(6);
private final int status;
Status(int stat) {
this.status = stat;
}
public int getStatus() {
return this.status;
}
}
private void setStatus(Status stat) {
s = stat;
}
public void TestingEnum() {
Status myStat = Status.STATUS_ONHOLD;
setStatus(myStat);
}
#Override
public String toString() {
StringBuilder result = new StringBuilder();
String NEW_LINE = System.getProperty("line.separator");
result.append(NEW_LINE + " Status: " + s + NEW_LINE);
return result.toString();
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
// PROBLEM SETTING STATUS HERE
// I can't do this:
Status s;
}
}
Move the enum to its own class file, or access it with a reference to the enclosing class.
It looks like you defined the Enum as an inner class of another class. If you're doing this, you need to access it with the syntax OuterClass.Status to access it. You made it public, so that will work. You can access it from within the class with no problem because it's contained in the scope of the parent class.
So you can either add the OuterClass. before Status, or you can move the Enum into its own file like any other class.
From the limited code I think the problem is that you try to access
public Status s;
which is not static from the static method main
public static void main(String[] args) {
...
}
Create a instance of your class from main and have a method on that instance use s or declare s as static.
You cannot just instantiate an Enum just before a public class like that. One way to resolve the issues is to have an outer class which will have your Enum class as inner class like this:
public class MyStatus {
static enum Status {
STATUS_OPEN(1),
STATUS_STARTED(2),
STATUS_INPROGRESS(3),
STATUS_ONHOLD(4),
STATUS_COMPLETED(5),
STATUS_CLOSED(6),
ABANDONED(7);
private final int status;
Status(int stat) {
this.status = stat;
}
public int getStatus() {
return this.status;
}
}
private Status s;
public void setStatus(Status stat) {
s = stat;
}
public void TestingEnum() {
Status myStat = Status.ABANDONED;
setStatus(myStat);
}
#Override
public String toString() {
StringBuilder result = new StringBuilder();
String NEW_LINE = System.getProperty("line.separator");
result.append(NEW_LINE + " Status: " + s + NEW_LINE);
return result.toString();
}
}
Also ABANDONED wasn't defined so I just added it in the end.
Based on your edit. The problem you have is the name of the class is nested and called MyClass.Status
Try
public static void main(String[] args) {
MyClass obj = new MyClass();
//PROBLEM SETTING STATUS HERE
//I can't do this:
MyClass.Status s = MyClass.Status.STATUS_OPEN;
}
BTW: Your IDE should be able to auto fix this mistake.
The only compilation error I see is that you have used
Status myStat = Status.ABANDONED;
without defining it. I suggest you add this enum or use one you have defined.
I resolved this error by creating a nonstatic enum as outside the class.
public enum Status {
STATUS_OPEN(1),
STATUS_STARTED(2),
STATUS_INPROGRESS(3),
STATUS_ONHOLD(4),
STATUS_COMPLETED(5),
STATUS_CLOSED(6),
ABANDONED(7);
private final int status;
Status(int stat) {
this.status = stat;
}
public int getStatus() {
return this.status;
}
}
Created MyStatus class without inner enum block:
public class MyStatus {
private Status s;
public void setStatus(Status stat) {
s = stat;
}
public void TestingEnum() {
Status myStat = Status.ABANDONED;
setStatus(myStat);
}
#Override
public String toString() {
StringBuilder result = new StringBuilder();
String NEW_LINE = System.getProperty("line.separator");
result.append(NEW_LINE + " Status: " + s + NEW_LINE);
return result.toString();
}
}
Resolved for me.
You would not be able to refer to Status in main() because, while Status is public, it is not static. You would need to create an instance of your enclosing class and then use it to create an instance of the enum:
MyEnclosingClass clz = new MyEnclosingClass();
Status status = clz.new Status();
This should resolve the error I believe you are probably getting.