Objectify 5.1 released

3,060 views
Skip to first unread message

Jeff Schnitzer

unread,
Oct 9, 2014, 5:51:47 PM10/9/14
to objectify...@googlegroups.com
I just pushed Objectify 5.1 to maven central, give it a couple hours to show up.

One major new feature, defer().  It is documented here:


The reason for the dot-version bump is that there is some minor API breakage. It will not affect you if you are just using Objectify in the vanilla way (with the ObjectifyFilter). If you are using Objectify in your test cases (and you should!) or via the remote API, you will need to make a few changes.

In the old way, you had to manually call ObjectifyFilter.complete() at your simulated request boundaries. In the new way, you do this:

ObjectifyService.run(new VoidWork() {
    public void vrun() {
        // anything you want here, you can now use ofy()
    }
});

An alternative if you want to twiddle this in junit or testng test cases which use @BeforeMethod/@AfterMethod:

class MyTest {
    Closeable session;

    @BeforeMethod
    public void setUp() {
        session = ObjectifyService.begin();
    }

    @AfterMethod
    public void tearDown() {
        session.close();
    }
}

Note that this is probably not what you _actually_ want to do in test cases unless your test cases correspond to exactly one request. My application tests typically simulate several requests, so I generally use the first form of this.

Jeff

Freddy Boucher

unread,
Oct 10, 2014, 2:13:30 AM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
I got this Exception while launching my app with Objectify 5.1


WARNING: Nested in java.lang.ExceptionInInitializerError:

java.lang.IllegalStateException: You have not started an Objectify context. You are probably missing the ObjectifyFilter. If you are not running in the context of an http request, see the ObjectifyService.run() method.

at com.googlecode.objectify.ObjectifyService.ofy(ObjectifyService.java:44)

at com.learnkeeper.server.OfyService.ofy(OfyService.java:31)

at com.learnkeeper.server.AuthenticationFilter.<clinit>(AuthenticationFilter.java:44)

at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)

at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

at java.lang.Class.newInstance(Class.java:374)

at org.mortbay.jetty.servlet.Holder.newInstance(Holder.java:153)

at org.mortbay.jetty.servlet.FilterHolder.doStart(FilterHolder.java:92)

at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)

at org.mortbay.jetty.servlet.ServletHandler.initialize(ServletHandler.java:662)

at org.mortbay.jetty.servlet.Context.startContext(Context.java:140)

at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1250)

at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)

at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)

at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)

at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)

at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)

at org.mortbay.jetty.Server.doStart(Server.java:224)

at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)

at com.google.appengine.tools.development.JettyContainerService.startContainer(JettyContainerService.java:255)

at com.google.appengine.tools.development.AbstractContainerService.startup(AbstractContainerService.java:288)

at com.google.appengine.tools.development.AutomaticInstanceHolder.startUp(AutomaticInstanceHolder.java:26)

at com.google.appengine.tools.development.AbstractModule.startup(AbstractModule.java:87)

at com.google.appengine.tools.development.Modules.startup(Modules.java:105)

at com.google.appengine.tools.development.DevAppServerImpl.doStart(DevAppServerImpl.java:258)

at com.google.appengine.tools.development.DevAppServerImpl.access$000(DevAppServerImpl.java:47)

at com.google.appengine.tools.development.DevAppServerImpl$1.run(DevAppServerImpl.java:213)

at com.google.appengine.tools.development.DevAppServerImpl$1.run(DevAppServerImpl.java:211)

at java.security.AccessController.doPrivileged(Native Method)

at com.google.appengine.tools.development.DevAppServerImpl.start(DevAppServerImpl.java:211)

at com.google.appengine.tools.development.gwt.AppEngineLauncher.start(AppEngineLauncher.java:97)

at com.google.gwt.dev.DevMode.doStartUpServer(DevMode.java:522)

at com.google.gwt.dev.DevModeBase.startUp(DevModeBase.java:1104)

at com.google.gwt.dev.DevModeBase.run(DevModeBase.java:844)

at com.google.gwt.dev.DevMode.main(DevMode.java:322)










@WebFilter("/AuthenticationFilter")


public class AuthenticationFilter implements Filter {




  static {


    ofy();


  }




  @Override


  public void destroy() {}




  @Override


  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,


      ServletException {}




  @Override


  public void init(FilterConfig arg0) throws ServletException {}


}











class OfyService {


  public static ObjectifyFactory factory() {


    return ObjectifyService.factory();


  }




  public static Objectify ofy() {


    return ObjectifyService.ofy();


  }




  static {


    ObjectifyService.register(DicoEntry.class);


    ObjectifyService.register(Word.class);


    ObjectifyService.register(Record.class);


    ObjectifyService.register(DicoRequest.class);


    ObjectifyService.register(Essay.class);


    ObjectifyService.register(UnexpectedEssay.class);


    ObjectifyService.register(User.class);


    ObjectifyService.register(FederatedUser.class);


    ObjectifyService.register(SessionUser.class);


    ObjectifyService.register(OAuth2User.class);


    ObjectifyService.register(OAuth2.class);


    ObjectifyService.register(GoogleOAuth2.class);


    ObjectifyService.register(FacebookOAuth2.class);


    ObjectifyService.register(Book.class);


    ObjectifyService.register(RootBook.class);


    ObjectifyService.register(UserBook.class);


    ObjectifyService.register(BookHist.class);


    ObjectifyService.register(BookHist.SetNameModif.class);


    ObjectifyService.register(BookHist.AddWordModif.class);


    ObjectifyService.register(BookHist.RemoveWordModif.class);


    ObjectifyService.register(BookHist.InstantiationModif.class);


    ObjectifyService.register(Permission.class);


  }


}







 

Freddy Boucher

unread,
Oct 10, 2014, 2:18:02 AM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
But if I remove the call to register my entities in my Filter it works...

  static {

   ofy();

 }



Jeff Schnitzer

unread,
Oct 10, 2014, 2:25:53 AM10/10/14
to objectify...@googlegroups.com
Right. You can't run ofy() without the ObjectifyFilter (or by setting up the context manually). And static blocks in a filter are run when the filter is loaded.

What is the purpose of that 'static { ofy(); }` block?

Jeff

--
You received this message because you are subscribed to the Google Groups "objectify-appengine" group.
To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appen...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Freddy Boucher

unread,
Oct 10, 2014, 2:31:01 AM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
In my Filter I manipulate Entities so I need to register the Entities Classes right?

@Override

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,

     ServletException {

   // Load and Save Entities

 }



Freddy Boucher

unread,
Oct 10, 2014, 2:44:04 AM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
And sorry, the following block

public class AuthenticationFilter implements Filter {

 static {

   ofy();

 }
}


is just a way to be sure that we call the static block of my OfyService class before anything else

Jeff Schnitzer

unread,
Oct 10, 2014, 3:21:50 AM10/10/14
to objectify...@googlegroups.com
You don't need to do that. Since you use OfyService.ofy() when you want to use Objectify, the JVM ensures that the OfyService class has been loaded before that code executes. The static block which registers your classes is run when the class loads.

You don't need to do anything more. Just by using the static OfyService.ofy() method normally, you ensure that the right stuff happens at the right time.

Jeff

Huseyn Guliyev

unread,
Oct 10, 2014, 4:32:01 PM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
Jeff, 
this might seem a simple thing, but I am sure many will stumble upon this. Would be great to update motomapia to use latest api, so people have an up-to-date reference.

Thanks,
Huseyn
To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appengine+unsub...@googlegroups.com.

Huseyn Guliyev

unread,
Oct 10, 2014, 10:06:34 PM10/10/14
to objectify...@googlegroups.com, je...@infohazard.org
One thing I encountered was,  cold start vs hot start config. It might be useful to know if ofyservice has started for current thread.

to be able do this:

try (Closeable closeable = ObjectifyService.beginIfHasntAlreadyBegun()) {
xxxxx
}

where 

public static Closeable beginIfHasntAlreadyBegun() {
final Deque<Objectify> stack = STACK.get();

return stack.isEmpty()?null:begin()

}

Nicholas Okunew

unread,
Oct 11, 2014, 9:14:57 PM10/11/14
to objectify...@googlegroups.com, je...@infohazard.org
I get the impression (from sample code posted here and SO), that people are creating their own ObjectifyService with static access pattern wrapping  and delegating to the ofy ObjectifyService.

Why? Does it serve a purpose? Or was it present in some demo code at some point?

To unsubscribe from this group and stop receiving emails from it, send an email to objectify-appen...@googlegroups.com.

Jeff Schnitzer

unread,
Oct 12, 2014, 4:52:53 AM10/12/14
to objectify...@googlegroups.com
The important point is that all entity classes must be registered with the singleton ObjectifyFactory before you start using Objectify.

There are myriad ways to accomplish this. One way would be to register a ServletContextListener in your web.xml. Another way would be to register a Servlet with init-on-startup set to "before anything else". Or set up a first Filter mapped to /* that registers everything.

...or you could just rely on basic behavior of the JVM.

Static blocks are executed once when a class is loaded. A class is loaded before any of its methods are executed. So if you always use Objectify through your own ofy() method, you can be certain that the static block of that class is executed first. Register your entity POJO classes in that static block and you are guaranteed not to have any problems.

You're welcome to use the other methods if they make you happier. It's not rocket science; take a look at the near-trivial source code in ObjectifyService. Personally I use a static block.

Jeff


Roman T

unread,
Oct 14, 2014, 2:39:37 AM10/14/14
to objectify...@googlegroups.com, je...@infohazard.org
I have the same Exception since Ofy 5.1 in unit tests (actually integration tests). There is no ObjectifyFilter in the Unittets. How should I run unit tests in ObjecifyService.run() ?


пятница, 10 октября 2014 г., 8:13:30 UTC+2 пользователь Freddy Boucher написал:

Jeff Schnitzer

unread,
Oct 14, 2014, 2:57:36 AM10/14/14
to objectify...@googlegroups.com
The first post in this thread outlines several ways, but it depends on how you have written your tests.

In your tests you should have some notion of a 'request' even if it is just conceptual. If "each test is a request by itself", then you can use @Before/@After in conjunction with ObjectifyService.begin() to demarcate the requests. However, this is probably not actually how your tests work - it isn't how my tests work.

My tests typically call several JAX-RS methods, each of which should be treated as a separate request. In the past, I had some tricks that effectively called (the now gone) ObjectifyFilter.complete() method between each JAX-RS method call. A better way, if you can retrofit your tests, is to do something like this:

    doSomeTestLogic();
    ObjectifyService.run(new VoidWork() {
        public void vrun() {
            makeTheJaxRsMethodCall();
        }
    });
    doSomeMoreTestLogic();

This would be prettier with JDK8 closures but the idea is straightforward - you're wrapping some unit of work in a context which represents a request. It would probably be smart to add even more context like authentication in that wrapper too.

For me, since I have zillions of tests and it would be an absurd amount of work to migrate them to this format, I faked it:

 * My @BeforeMethod starts an objectify context (ObjectifyService.begin())
 * My @AfterMethod closes the objectify context
 * That bit of code I used to use to complete() requests now simply closes the existing context and opens a new one.

It's a very poor-man's way of faking ObjectifyService.run(). Future tests will actually use ObjectifyService.run().

Jeff

--

Tuchin Roman

unread,
Oct 14, 2014, 3:03:36 AM10/14/14
to objectify...@googlegroups.com
Thanks for you answer. So there is no chance, that you change Ofy like it in 5.0.* worked, where it did work good with unit tests?

--
You received this message because you are subscribed to a topic in the Google Groups "objectify-appengine" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/objectify-appengine/O4FHC_i7EGk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to objectify-appen...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Pozdrawiam, Roman Tucin
Best regards, Roman Tuchin
С уважением, Роман Тучин
Mit freundlichen Grüßen, Roman Tutschin

Jeff Schnitzer

unread,
Oct 14, 2014, 3:25:23 AM10/14/14
to objectify...@googlegroups.com
A better way to put it would be to say that previous versions of Objectify allowed you to be sloppy defining your request boundaries. This was not a good thing. It was very easy to accidentally create false negative tests through session contamination.

Jeff


Reply all
Reply to author
Forward
0 new messages