I'm trying to implement a custom marshaller in Grails. Here's the marshaller:
class AdultPlanningMarshaller implements ObjectMarshaller<JSON> {
boolean supports(Object theObject)
{
return theObject instanceof AdultPlanning
}
void marshalObject(Object theObject, JSON theConverter)
{
AdultPlanning adult = (AdultPlanning)theObject
JSONWriter writer = theConverter.getWriter()
writer.object()
writer.key('id').value(adult.id)
...
writer.endObject()
}
}
I'm registering it in bootstrap.groovy and when I run my integration tests, the supports method fires correctly and the marshalObject method is called with the right object and a JSON object.
When I hit the:
writer.object()
call, an exception gets thrown:
org.codehaus.groovy.grails.web.json.JSONException: Misplaced object: expected mode of INIT, OBJECT or ARRAY but was DONE
So it looks like the writer has already done something to completion, but I have no clue what.
There's not a lot of documentation on JSON marshallers and examples are thin on the ground, but I think I've done this right but it sure isn't working. Any hints would be appreciated.
Further work with the debugger seems to indicate that the object marshaller is being called twice, although breakpoints only happen on the 2nd call for some reason. The first time through it seems to work just fine since the JSONWriter that I get via theConverter.getWriter() when the breakpoint DOES work has the JSON of the object correctly marshalled. It's the 2nd call that is blowing up since the object has ALREADY been marshalled and the JSONWriter is no longer in the "init" state. There's nothing obviously available to tell the difference between the two calls, but why it the marshaller being called twice?
As requested, here's the controller. It's the show action that's being triggered:
class PrimaryController extends RestfulController implements AlwaysRenderJsonException {
def springSecurityService
def familyService
static responseFormats = ['json']
PrimaryController() {
/*
* Tell the base class the name of the resource under management.
*/
super(Primary)
}
#Override
protected Primary createResource() {
//def instance = super.createResource()
//TODO: Should be able to run the above line but there is an issue GRAILS-10411 that prevents it.
// Code from parent is below, as soon as the jira is fixed, remove the following lines:
Primary instance = resource.newInstance()
bindData instance, this.getObjectToBind()
//Code from super ends here
def family = familyService.safeGetFamily(params.long('familyId'))
familyService.addAdultToFamily(instance, family) // Add the primary member to the family.
return instance
}
/**
* Deletes a resource for the given id
* #param id The id
*/
#Override
def delete() {
if(handleReadOnly()) {
return
}
Child instance = queryForResource(params.id)
if (instance == null) {
notFound()
return
}
/*
* Because of the multiple belongsTo relationships of events, you have to get rid of all
* the events and make the profiles consistent BEFORE deleting the person instance.
*/
instance.removePerson()
request.withFormat {
'*'{ render status: NO_CONTENT } // NO CONTENT STATUS CODE
}
}
#Override
protected List<Primary> listAllResources(Map params) {
if (params.familyId == null)
{
throw new ESPException("params.familyId may not be null")
}
def user = springSecurityService.loadCurrentUser()
return \
AdultPlanning.where {
family.id == params.familyId \
&& family.user == user \
&& typeOfPerson == PeopleTypeEnum.PRIMARY
}.list()
}
#Override
protected Primary queryForResource(Serializable id) {
def inst = familyService.safeGetAdult(Long.parseLong(id), params.long('familyId'))
/*
* It was safe to access the requested id, but the requested id may NOT be a primary
* so we need to check.
*/
return (inst instanceof Primary ? inst : null)
}
/**
* Show the primary for the specified family.
*
* #return
*/
#Override
def show() {
Primary primary = familyService.safeGetFamily(params.long('familyId'))?.primary
respond primary
}
}
And the Integration test that triggers it:
void "We should be able to show a primary."() {
given:
family.addToAdults(new Primary(firstName: "Barney"))
family.save()
family.adults.each { it.save() }
when:
controller.response.reset()
resetParameters(controller.params, [familyId: family.id])
controller.request.method = 'GET'
controller.show()
then:
1 * mSpringSecurityService.loadCurrentUser() >> user
controller.response.json
controller.response.json.firstName == "Barney"
}
Well, this is embarrassing.
I use IntelliJ as my Java/Groovy IDE. I had a work related thing to do this morning and quit IntelliJ. When I restarted IntelliJ, the problem described above that had been completely reproducible was no longer happening and the appropriate JSON was being produced under all circumstances.
So it appears that the IntelliJ state somehow got corrupted and the restart cleared it out.
Problem solved.
I guess.
Thanks for the help/suggestions.
As OP mentioned, this error can be triggered because of IntelliJ :
org.codehaus.groovy.grails.web.json.JSONException: Misplaced object: expected mode of INIT, OBJECT or ARRAY but was DONE
Indeed, when debugging the marshaller (for example), IntelliJ displays the "toString()" of the variable, which causes the change of the mode from INIT to DONE.
You may want to remove your breakpoints when facing this issue ;)
The only reason for this can be that where you have started writer.object() for some nested object or array response but missed to write writer.endObject() or you have wrote it two times.
So double check your custom marshaller for all write object.
Reference: https://github.com/grails/grails-core/blob/65b42b66821b32d4efb3a229da99691a00575d60/grails-web-common/src/main/groovy/org/grails/web/json/JSONWriter.java#L258
Hope This helps!
Thanks,
SA
Related
I'm using a Calendar object to determine whether or not to increase the workload of a system based on current day/hour values. Given that this object uses static methods, I'm using PowerMock to mock the static methods with the following annotations:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Calendar.class })
The code under test is pretty simple (though my logic needs work, I know):
public void determineDefaultMaximumScans() throws ParseException{
parseTime();
Calendar cal = Calendar.getInstance();
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
System.out.println(cal.get(Calendar.DAY_OF_WEEK));
if(dayOfWeek == (Calendar.SATURDAY) || dayOfWeek == (Calendar.SUNDAY)){
setDefaultMax(calculateNewDefaultMax(getDefaultMax()));
System.out.println("defaultMax increased by 20%");
} else {
if(currentTime.after(afterHoursBegin) && currentTime.before(afterHoursEnd)){
System.out.println("Not afterhours. Maintaining current maximum.");
setDefaultMax(defaultMax);
System.out.println("Current Maximum number of scans: " + getDefaultMax());
}
}
}
My test case reads as follows:
#SuppressWarnings("static-access")
#Test
public void testDetermineMaximumScans() throws ParseException{
PowerMock.mockStatic(Calendar.class);
String beginningTime = "18:00";
String endingTime = "05:00";
mockAfterHoursBegin = parser.parse(beginningTime);
mockAfterHoursEnd = parser.parse(endingTime);
mockCurrentTime = parser.parse(parser.format(new Date()));
EasyMock.expect(Calendar.getInstance()).andReturn(mockCalendar);
EasyMock.expect(mockCalendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
EasyMock.replay(mocks);
offHourMaximumCalculator.determineDefaultMaximumScans();
EasyMock.verify(mocks);
}
As of now, all of my attempts to return a specific value result in the following assertion error. Now I vaguely understand why it's returning the default but I do not see why I can't force the value or how to get around this expectation. Mocks in general are still a frustrating mystery to me. What am I missing?
java.lang.AssertionError:
Expectation failure on verify:
Calendar.get(7): expected: 1, actual: 0
Mocks are fairly simple. But wanting to mock static methods is a big running after complexity. I generally do not recommend to mock something like a Calendar. If you do weird and complex thing with it, just encapsulate in something you can test and mock easily.
And in fact, we pretty much never use Calendar.getInstance(). It returns something according to the locale. But it's rare that you don't want a specific calendar i.e. GregorianCalendar. So just do new GregorianCalendar.
But anyway, add a protected method doing
protected Calendar newCalendar() {
return Calendar.getInstance(); // or new GregorianCalendar()
}
will take 2 minutes and then a simple partial mock will do the trick.
Finally, I also don't recommend to use Calendar. You have a much nicer API in java.util.date in Java 8.
All this said, here is how you should do it. Calendar is a system class, so you need to follow a real specific path which is explained here.
#RunWith(PowerMockRunner.class)
#PrepareForTest(Calendar.class)
public class MyTest {
#Test
public void testDetermineMaximumScans() throws ParseException {
PowerMock.mockStatic(Calendar.class);
Calendar calendar = mock(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn(calendar);
EasyMock.expect(calendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
// really important to replayAll to replay the static expectation
PowerMock.replayAll(calendar);
assertThat(Calendar.getInstance().get(Calendar.DAY_OF_WEEK)).isEqualTo(6);
// and verifyAll is you want to verify that the static call actually happened
PowerMock.verifyAll();
}
}
In Django, there are a couple of exceptions that are designed to be intercepted by the framework and turned into specific HTTP response codes, such as 404 Not Found and 403 Forbidden.
This is especially useful for request validation, because it allows you to factor out common validation logic into utility functions and cleanup your controller actions.
Whenever the utility functions decide that the current request must be aborted with a specific HTTP error code, they can do so by throwing the relevant exception, without any support code in the controller action, in the form of a return statement or a try/catch.
For example, given a tree of nested REST resources:
static mappings = {
"/authors" (resources: "author") {
"/sagas" (resources: "saga") {
"/books" (resources: "book") {
}
}
}
Then the URL pattern for the Book resource is /authors/$authorId/sagas/$sagaId/books/$id, which means that any of the show(), delete(), or update() actions in BookController have this signature and must include some boilerplate validation logic:
def actionName(int authorId, int sagaId, Book book) {
// -- common validation logic ----------
// fetch parent objects
def author = Author.get(authorId)
def saga = Saga.get(sagaId)
// check that they exists
if (author == null || saga == null || book == null) {
return render(status: NOT_FOUND)
}
// check consistency
if (book.author != author || book.saga != saga || saga.author != author) {
return render(status: BAD_REQUEST)
}
// -- end of commond code --------------
...
}
What is the Grails way of factoring this out into a common method, while still allowing it to terminate request processing whenever an exceptional condition occurs?
I would think the best way is a NotFoundException, ForbiddenException, BadRequestException, and so on, or maybe a generic exception that accepts a HTTP status code. Is there anything like it in Grails? If not, where is the best place to add it? A filter?
Edit: I see now that the standard method is to add an error controller with a matching URL pattern, such as:
"500" (controller: "error")
The problem with this is that Grails will still log full stacktraces for all exceptions, including those that are not programming errors. This spams log files with all sorts of useless tracebacks.
Is there a solution?
You catch the exception in the beforeInterceptor closure of your controller. I resolved this same problem by examining the exception thrown and then acting accordingly. For example:
class BaseController {
/**
* Define DRA exception handlers. This prevents the default Grails
* behavior of returning an HTTP 500 error for every exception.
*
* Instead the exceptions are intercepted and modified according to
* the exception that was thrown. These exceptions are not logged
* whereas application exceptions are.
*/
def beforeInterceptor = {
request.exceptionHandler = { exception ->
def cause = exception.cause
def exceptionBody = [:]
if(cause.class == BadRequestException) {
response.setStatus(HttpStatus.BAD_REQUEST.value()) // HTTP 400 BAD REQUEST
exceptionBody.httpStatus = HttpStatus.BAD_REQUEST.value()
exceptionBody.error = cause.message
}
// render the exception body, the status code is set above.
render exceptionBody as JSON
return true
}
}
}
In order to get this to work you will have to create an ErrorController or something where all server errors are processed and rendered. For example:
class ErrorController {
def serverError() {
def handler = request.exceptionHandler
if(handler) {
request.exceptionHandler = null
if(handler.call(request.exception)) {
return
}
}
}
I have tested this an it does work. I copied the code from a running project that I have been working on. You can build out the if statement in the beforeInterceptor to catch any type of Exception you wish.
Castle Windsor passes the registered concrete type to Controller's constructors. A typical implementation (no pun intended) is:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository;
}
I need to pass the ctor a second parameter, if possible, so that I can pass that val on to the Repository constructor (I know: tramp data alert, but I don't know if there's a straightforward way around it:
public DepartmentsController(IDepartmentRepository deptsRepository, int DBInstance)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository(DBInstance);
}
REPOSITORY
public DepartmentRepository(int dbInst)
{
string connStr = string.Format("Phoo{0}Bar", dbInst);
using (var conn = new OleDbConnection(connStr))
{
using (var cmd = conn.CreateCommand())
{
. . .
Is it possible to tweak what Castle Windsor sends to the Controller constructor this way? If so, how?
AND/BUT: For this to be of any value (to me, anyway), I need to be able to get the int val (that will be passed to the Controller) from the URL the client sends. IOW, if the client asks the server for data via:
http://locohost:4242/Platypus/GetAll/1
I need to pass a "1" as the second argument to PlatypusController.
If the user asks the server for data via:
http://locohost:4242/Platypus/GetAll/42
I need to pass a "42" as the second argument to PlatypusController.
etc.
This is what I did to solve my Controller/Repository data context Dilemma:
0) Added a database context argument to the Controller's routing attribute. IOW, this:
[Route("api/HHSUsers/GetAll")]
...got changed to this:
[Route("api/HHSUsers/GetAll/{dbContext=03}")]
1) Passed that database context arg to the Repository. To wit, this:
return _hhsusersrepository.GetAll();
...got changed to this:
return _hhsusersrepository.GetAll(dbContext);
...so that the Controller method is now:
[Route("api/HHSUsers/GetAll/{dbContext=03}")]
public IEnumerable<HHSUsers> GetAllHHSUsersRecords(int dbContext)
{
return _hhsusersrepository.GetAll(dbContext);
}
2) Changed the corresponding method in the Repository interface from:
IEnumerable<HHSUsers> GetAll();
...to this:
IEnumerable<HHSUsers> GetAll(string dbContext);
3) Changed the Repository method from this:
public HHSUsersRepository()
{
// All the data is loaded here in the ctor
}
public IEnumerable<HHSUsers> GetAll()
{
return hhsusers;
}
....to this:
public IEnumerable<HHSUsers> GetAll(string dbContext)
{
LoadHHSUsers(dbContext);
return hhsusers;
}
private void LoadHHSUsers(int dbContext)
{
string connStr = string.Format("Foo{0}Bar", dbContext);
// The same as previously from this point on, except that this:
// using (var conn = new OleDbConnection(#"Foo Bar Phoo Bar etc"...
// becomes:
// using (var conn = new OleDbConnection(connStr))
4) Tack the dbcontext val to the end of the URL when calling the method, so that it is this:
http://localhost:28642/api/HHSUsers/GetAll/42
...instead of this:
http://localhost:28642/api/HHSUsers/GetAll
If the data context to use is "03" I can omit the dbcontext arg from the URL, as 03 is the default value I set when I appended "=03" to the Controller's "dbContext" routing attribute arg.
I know some fancy-pants propeller-heads will find fault with this for some reason (for one reason because of the tramp data going here and there and everywhere like a hobo on steroids), but my response is the same as that of an athlete who is getting trash-talked by an opposing player and yet whose team is winning: just point at the scoreboard. IOW, this works for me, so that's pretty much all I care about. Style points are for runway models and, again, fancy-pants propeller-heads (AKA Star-Bellied Sneeches (as opposed to us plain
cats with the unstarred bellies)); see "The perfect is the enemy of the good."
This simple way has that self-same benefit -- of being (relatively) simple to grok and, thus, modify/refactor as necessary. Inelegant? Sure, but so was Joe Kapp.
I am converting Android 4.x code to use ActionBarSherlock so that our App can be compatible with Gingerbread.
So far so good, but it fails launching a new instance of a fragment.
My MainActivity extends SherlockFragmentActivity implements ActionBar.TabListener.
The code fails here where case is 0:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
mFragmentProjects = ProjectsFragment.newInstance(position);
return mFragmentProjects;
case 1:
mFragmentContacts = FragmentPeople.newInstance(position, 0);
return mFragmentContacts;
}
return ArrayListFragment.newInstance(position);
}
Where case is 0 it supposed to initialize the fragment but I get this exception:
ClassNotFoundException. The only other clue I have is:
"this" in PathClassLoader and in "name" it says android.app.ActionBar$TabListener
I guess this has something to do with TabListener or libraries not included / loading correctly? I have already cleaned the project.
The fragment ProjectsFragment extends SherlockListFragment.
newInstance is pretty straitforward:
static ProjectsFragment newInstance(int num) {
ProjectsFragment f = new ProjectsFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
It turns out that although MainActivity has no reference to ActivityY, the mere fact that ActivityY did not have the SherlockFragment code made it fail. To describe this differently:
MainActitivy uses ProjectsFragment which is a list
When you click on a list in ProjectsFragment it calls ListsActivity
ListsActivity references ItemsFragment
I had to change ItemsFragment to Sherlock code before MainActivity would work. It seems Java "looks ahead" in some way when you're working with pagers and tabs and fragments.
I'm using EF 4.1 Code First. I have an entity defined with a property like this:
public class Publication
{
// other stuff
public virtual MailoutTemplate Template { get; set; }
}
I've configured this foreign key using fluent style like so:
modelBuilder.Entity<Publication>()
.HasOptional(p => p.Template)
.WithMany()
.Map(p => p.MapKey("MailoutTemplateID"));
I have an MVC form handler with some code in it that looks like this:
public void Handle(PublicationEditViewModel publicationEditViewModel)
{
Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
if (publication.Id == 0)
{
_publicationRepository.Add(publication);
}
else
{
_publicationRepository.Update(publication);
}
_unitOfWork.Commit();
}
In this case, we're updating an existing Publication entity, so we're going through the else path. When the _unitOfWork.Commit() fires, an UPDATE is sent to the database that I can see in SQL Profiler and Intellitrace, but it does NOT include the MailoutTemplateID in the update.
What's the trick to get it to actually update the Template?
Repository Code:
public virtual void Update(TEntity entity)
{
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual TEntity Get(int id)
{
return _dbSet.Find(id);
}
UnitOfWork Code:
public void Commit()
{
_dbContext.SaveChanges();
}
depends on your repository code. :) If you were setting publication.Template while Publication was being tracked by the context, I would expect it to work. When you are disconnected and then attach (with the scenario that you have a navigation property but no explicit FK property) I'm guessing the context just doesn't have enough info to work out the details when SaveChanges is called. I'd do some experiments. 1) do an integration test where you query the pub and keep it attached to the context, then add the template, then save. 2) stick a MailOutTemplateId property on the Publicaction class and see if it works. Not suggesting #2 as a solution, just as a way of groking the behavior. I"m tempted to do this experiment, but got some other work I need to do. ;)
I found a way to make it work. The reason why I didn't initially want to have to do a Get() (aside from the extra DB hit) was that then I couldn't do this bit of AutoMapper magic to get the values:
Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
However, I found another way to do the same thing that doesn't use a return value, so I updated my method like so and this works:
public void Handle(PublicationEditViewModel publicationEditViewModel)
{
Publication publication = _publicationRepository.Get(publicationEditViewModel.Id);
_mappingEngine.Map(publicationEditViewModel, publication);
// publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
if (publication.Id == 0)
{
_publicationRepository.Add(publication);
}
else
{
_publicationRepository.Update(publication);
}
_unitOfWork.Commit();
}
I'm injecting an IMappingEngine now into the class, and have wired it up via StructureMap like so:
For<IMappingEngine>().Use(() => Mapper.Engine);
For more on this, check out Jimmy's AutoMapper and IOC post.