uPortal IRC Logs-2012-05-17
[11:14:14 CDT(-0500)] <EricDalquist> athena: looks like you're handling pull 44?
[11:15:21 CDT(-0500)] <athena> hey eric
[11:15:22 CDT(-0500)] <athena> yes
[11:15:29 CDT(-0500)] <athena> think they're going to refactor that using the existing library
[11:15:42 CDT(-0500)] <athena> i've also written a factory for those jaxb elements
[11:15:48 CDT(-0500)] <athena> so they're less painful and verbose to create in xml
[11:15:56 CDT(-0500)] <EricDalquist> cool
[11:16:50 CDT(-0500)] <athena> by the way, did you ever experiment with ways to call a portal REST service from a portlet?
[11:16:59 CDT(-0500)] <EricDalquist> yes
[11:17:01 CDT(-0500)] <EricDalquist> it is possible
[11:17:03 CDT(-0500)] <EricDalquist> but gross
[11:17:14 CDT(-0500)] <athena> i seem to remember you writing a portlet that called the portal's portlet list service
[11:17:15 CDT(-0500)] <athena> gotcha
[11:17:19 CDT(-0500)] <EricDalquist> yeah
[11:17:29 CDT(-0500)] <athena> did your portlet re-use the portal session?
[11:17:34 CDT(-0500)] <EricDalquist> yes
[11:17:37 CDT(-0500)] <EricDalquist> so here is the concept"
[11:18:35 CDT(-0500)] <EricDalquist> you create a servlet (spring spring servlet controller). This servlet does a cross-context lookup for the portal context then creates a request dispatcher for that context with the rest API uri
[11:19:36 CDT(-0500)] <EricDalquist> the servlet uses a response wrapper to capture ALL of the output (to prevent mucking with the portlet's response)
[11:20:00 CDT(-0500)] <EricDalquist> the servlet then sets the response data in some sort of request attribute data structure
[11:20:17 CDT(-0500)] <EricDalquist> your portlet code can create a request dispatcher to talk to that custom servlet
[11:20:23 CDT(-0500)] <EricDalquist> so ...
[11:20:26 CDT(-0500)] <athena> that makes sense
[11:20:49 CDT(-0500)] <athena> would it make sense for us to have a reusable library for that logic?
[11:21:11 CDT(-0500)] <EricDalquist> PortletController -> PortletRequestDispatcher.include -> PortalProxyingServlet -> PortalContextLookup -> RequestDisptacher.include ->
[11:21:20 CDT(-0500)] <EricDalquist> if we want to encourage this
[11:21:21 CDT(-0500)] <EricDalquist> yes
[11:21:25 CDT(-0500)] <athena> well
[11:21:26 CDT(-0500)] <EricDalquist> we would have to do a resuable lib
[11:21:30 CDT(-0500)] <EricDalquist> it would be insane otherwise
[11:21:33 CDT(-0500)] <athena> yeah.
[11:21:39 CDT(-0500)] <athena> i guess the question is whether we want to encourage it
[11:21:42 CDT(-0500)] <EricDalquist> we would also need to standardize on the data returned
[11:21:45 CDT(-0500)] <EricDalquist> yeah
[11:21:52 CDT(-0500)] <EricDalquist> I'm not sure what our alternatives are :/
[11:22:00 CDT(-0500)] <EricDalquist> this is good because it never leaves tomcat
[11:22:03 CDT(-0500)] <athena> but it seems like there's a legitimate use case for wanting to query the portal's person directory, permission store, etc.
[11:22:11 CDT(-0500)] <EricDalquist> so you avoid self-call issues
[11:22:19 CDT(-0500)] <athena> and seems like REST services are a reasonable way to do it
[11:22:19 CDT(-0500)] <EricDalquist> the other option
[11:22:24 CDT(-0500)] <EricDalquist> is we create a resuable library
[11:22:28 CDT(-0500)] <EricDalquist> that just does direct API calls
[11:22:43 CDT(-0500)] <EricDalquist> just a sec ...
[11:23:09 CDT(-0500)] <athena> would a library that does direct API calls really work? wouldn't we need to recreate all the XML config?
[11:23:37 CDT(-0500)] <EricDalquist> no
[11:23:50 CDT(-0500)] <EricDalquist> so what we would do is define truly public APIs
[11:24:05 CDT(-0500)] <EricDalquist> this API jar would live in tomcat/shared/lib
[11:24:19 CDT(-0500)] <EricDalquist> when uPortal starts up it would bootstrap this API jar by injecting an implementation
[11:24:32 CDT(-0500)] <EricDalquist> wait
[11:24:36 CDT(-0500)] <EricDalquist> ignore that 2nd line
[11:24:46 CDT(-0500)] <EricDalquist> so we would create an API jar and it would live in shared/lib
[11:25:04 CDT(-0500)] <athena> gotcha
[11:25:09 CDT(-0500)] <athena> yeah, that makes sense
[11:25:11 CDT(-0500)] <EricDalquist> then the portal would provide impls of this API as PortalContext attributes
[11:25:18 CDT(-0500)] <athena> of course, that requires a bunch of planning and design effort
[11:25:27 CDT(-0500)] <athena> that i bet we don't actually have the resources to do right now
[11:25:42 CDT(-0500)] <EricDalquist> so you could do:
[11:25:43 CDT(-0500)] <EricDalquist> PersonDir pd = (PersonDir)portalContext.getAttribute("org.jasig.portal.personDirectory");
[11:25:46 CDT(-0500)] <EricDalquist> right
[11:25:52 CDT(-0500)] <athena> though that does sound like a nice architectural plan
[11:25:53 CDT(-0500)] <EricDalquist> but it would provide isolation from portlets
[11:25:58 CDT(-0500)] <EricDalquist> and be sane to support
[11:25:59 CDT(-0500)] <athena> yeah
[11:26:09 CDT(-0500)] <EricDalquist> the danger with all of this is if we don't commit to doing that work
[11:26:14 CDT(-0500)] <EricDalquist> we WILL hate ourselves in 2 years
[11:26:19 CDT(-0500)] <EricDalquist> and it will be painful
[11:26:53 CDT(-0500)] <athena> yeah
[11:27:02 CDT(-0500)] <athena> and it would be really nice to have person directory and group support for our portlets
[11:27:45 CDT(-0500)] <EricDalquist> so you mean getting attributes for users other than the curent user
[11:27:50 CDT(-0500)] <EricDalquist> and doing more than role checking?
[11:27:56 CDT(-0500)] <EricDalquist> because if we expose that
[11:28:03 CDT(-0500)] <EricDalquist> we really need to get permissioning in place as well
[11:28:11 CDT(-0500)] <EricDalquist> or we're going to have security problems
[11:29:04 CDT(-0500)] <athena> well, we already have a REST feed that does that
[11:29:07 CDT(-0500)] <athena> and it does check user permissions
[11:29:18 CDT(-0500)] <athena> that's how the uMobile native app's directory works
[11:29:19 CDT(-0500)] <EricDalquist> yup
[11:29:31 CDT(-0500)] <athena> it contacts the /people.json service using it's existing portal session
[11:29:53 CDT(-0500)] <athena> and that filters results by VIEW_USER and VIEW_USER_ATTRIBUTE
[11:30:08 CDT(-0500)] <athena> so drew's use case is to do something similar, but from a portlet
[11:30:29 CDT(-0500)] <athena> seems like a reasonable use case for messaging portlets, etc. as well
[11:30:36 CDT(-0500)] <EricDalquist> yup
[11:31:03 CDT(-0500)] <EricDalquist> so our API via request attribute approach could still call the controllers
[11:31:14 CDT(-0500)] <athena> yeah - it sounds like that would work today
[11:31:16 CDT(-0500)] <EricDalquist> especially if the controller doesn't actually need the req/req
[11:31:18 CDT(-0500)] <EricDalquist> req/res
[11:31:35 CDT(-0500)] <athena> i think it does, so that it can check the session?
[11:32:52 CDT(-0500)] <EricDalquist> hrm
[11:33:04 CDT(-0500)] <EricDalquist> what is the controller for that rest feed?
[11:35:17 CDT(-0500)] <athena> PeopleRESTController.java
[11:35:59 CDT(-0500)] <EricDalquist> hrm
[11:36:04 CDT(-0500)] <EricDalquist> yeah that needs req and res
[11:39:52 CDT(-0500)] <athena> yeah - i think most of our services would
[11:40:10 CDT(-0500)] <athena> if they didn't need a session we could just make a normal call and skip all the session handling stuff
[11:44:27 CDT(-0500)] <drewwills> hey folks – sorry, was in a mtg
[11:50:17 CDT(-0500)] <drewwills> I can enhance the rest controllers to optionally permit localhost traffic
[11:50:25 CDT(-0500)] <drewwills> w/o a session
[11:50:37 CDT(-0500)] <EricDalquist> that won't work
[11:50:40 CDT(-0500)] <EricDalquist> at least not portably
[11:50:57 CDT(-0500)] <EricDalquist> for example our portal isntances can't talk to themselves on localhost
[11:50:57 CDT(-0500)] <EricDalquist> or on the portal's host name
[11:51:09 CDT(-0500)] <drewwills> and btw I really like the path of leveraging the REST apis b/c we're already putting so much good investment there
[11:51:10 CDT(-0500)] <EricDalquist> as an artifact of how our hardware load balancer networking gear works
[11:51:29 CDT(-0500)] <EricDalquist> did you read the logs of our chat drewwills?
[11:51:35 CDT(-0500)] <drewwills> i did
[11:52:14 CDT(-0500)] <drewwills> i aslo like the requestDispatcher – as opposed to httpClient – method
[11:52:48 CDT(-0500)] <drewwills> and fwiw our immediate use cases are slightly less ugly b/c I'll be making these calls from spring WEB wvc
[11:53:32 CDT(-0500)] <drewwills> i think... catching up w/ myself here... thinking and typing at the same time
[11:54:20 CDT(-0500)] <drewwills> can't a portllet get a cross-context requestDispatcher anyway?
[11:55:02 CDT(-0500)] <EricDalquist> well remember the httpclient method is NOT portable
[11:55:02 CDT(-0500)] <EricDalquist> and not something we can include/distribute due to that
[11:55:14 CDT(-0500)] <EricDalquist> ok
[11:55:36 CDT(-0500)] <EricDalquist> so a portlet can't directly do cross-context
[11:55:41 CDT(-0500)] <EricDalquist> but it can via a servlet in the same webapp
[11:56:01 CDT(-0500)] <EricDalquist> PortletController -> PortletRequestDispatcher.include -> PortalProxyingServlet -> PortalContextLookup -> RequestDisptacher.include -> RestController
[11:56:21 CDT(-0500)] <drewwills> roger
[11:56:26 CDT(-0500)] <EricDalquist> we could encapsulate "-> PortletRequestDispatcher.include -> PortalProxyingServlet -> PortalContextLookup -> RequestDisptacher.include -> RestController" in a stand alone library
[11:57:17 CDT(-0500)] <EricDalquist> we would also need to figure out what data type would be returned
[11:57:27 CDT(-0500)] <drewwills> so when that request comes in, what does the session look like from the persspective of the rest controller portal-side?
[11:57:37 CDT(-0500)] <drewwills> is it the user's actual httpsession?
[11:57:38 CDT(-0500)] <EricDalquist> it is the user's portal session
[11:57:45 CDT(-0500)] <drewwills> rock & roll
[11:57:52 CDT(-0500)] <EricDalquist> I think we could encapsulate this enough to have it be a plain old API
[11:58:03 CDT(-0500)] <EricDalquist> give me 10 minutes to chat with JimH here in my office
[11:58:03 CDT(-0500)] <drewwills> that would automatically filter appropriately, correct?
[11:58:05 CDT(-0500)] <EricDalquist> and we can talk more about it
[11:58:16 CDT(-0500)] <drewwills> i need to hit a mtg too
[11:58:20 CDT(-0500)] <drewwills> bb in 1 hr or less
[13:07:58 CDT(-0500)] <drewwills> back from my mtg, if there's more ground to cover
[13:08:16 CDT(-0500)] <EricDalquist1> yes
[13:08:20 CDT(-0500)] <EricDalquist1> but I just sat down to eat
[13:08:25 CDT(-0500)] <EricDalquist1> give me 15 minutes or so
[13:08:27 CDT(-0500)] <EricDalquist1> and we cn chat a bit more
[13:08:40 CDT(-0500)] <drewwills> lol, i'd like to give you more than 15 min to eat
[13:08:54 CDT(-0500)] <drewwills> i know i'm demending, but I do have standards
[13:10:43 CDT(-0500)] <EricDalquist1>
[13:26:04 CDT(-0500)] <EricDalquist> ok drewwills
[13:26:16 CDT(-0500)] <EricDalquist> as long as our firewall doesn't decide to block everything again
[13:26:19 CDT(-0500)] <EricDalquist> I should be all set
[13:30:21 CDT(-0500)] <drewwills> great
[13:31:32 CDT(-0500)] <EricDalquist> so a few things that need to get figured out:
[13:31:32 CDT(-0500)] <EricDalquist> what is the result data format
[13:31:32 CDT(-0500)] <EricDalquist> how much API do we encode in the utility library
[13:32:20 CDT(-0500)] <EricDalquist> a very simple util library could look like:
[13:32:20 CDT(-0500)] <EricDalquist> Object callPortalApi(PortletRequest, PortletResponse, String uri, Map<String, String[]> parameters)
[13:32:31 CDT(-0500)] <EricDalquist> and just hide all of the gross stuff
[13:32:32 CDT(-0500)] <drewwills> yep
[13:32:49 CDT(-0500)] <EricDalquist> the portlet would then just have to configure the generic "portal call proxy servlet" in web.xml
[13:32:55 CDT(-0500)] <EricDalquist> and configure the portal's webappname
[13:33:02 CDT(-0500)] <drewwills> could even be "public JsonNode"
[13:33:02 CDT(-0500)] <EricDalquist> though we could add that as a request property
[13:33:10 CDT(-0500)] <EricDalquist> which I would vote for
[13:33:19 CDT(-0500)] <EricDalquist> publicJsonNode?
[13:33:44 CDT(-0500)] <drewwills> instead of Object, we could have a more specific return type... which could be JsonNode
[13:33:51 CDT(-0500)] <EricDalquist> right
[13:33:58 CDT(-0500)] <EricDalquist> though that then forces the choice of JSON library on the portlet
[13:34:08 CDT(-0500)] <EricDalquist> so if we do that we say pick Jackson
[13:34:15 CDT(-0500)] <EricDalquist> then they are forced to use Jackson
[13:34:22 CDT(-0500)] <EricDalquist> not sure if that is an issue or not
[13:34:33 CDT(-0500)] <EricDalquist> maybe reasonable since jackson is what Spring3 uses for all its json stuff
[13:34:43 CDT(-0500)] <EricDalquist> and that is what most of our portlets use or will use for a framework
[13:35:08 CDT(-0500)] <drewwills> or we could add a Class parameter to the method and expose the features of the RestTemplate.getForObject or whatever
[13:43:23 CDT(-0500)] <EricDalquist> sorry .. lost network again
[13:43:25 CDT(-0500)] <EricDalquist> just a minute
[13:43:48 CDT(-0500)] <drewwills> sure
[13:45:27 CDT(-0500)] <drewwills> and maybe i'm missing something... how do we hit a uri through a RequestDispatcher while using a RestTemplate anyway? I don't think I have that peice of the puzzle in my head atm
[13:46:16 CDT(-0500)] <EricDalquist> you can't
[13:46:21 CDT(-0500)] <EricDalquist> we have to write all that handling logic
[13:46:32 CDT(-0500)] <EricDalquist> so the way this works is:
[13:47:03 CDT(-0500)] <drewwills> oh noes
[13:47:20 CDT(-0500)] <drewwills> i was getting quite fond of that RestTemplate interface
[13:47:20 CDT(-0500)] <EricDalquist> 1. PortletController creates a PortletRequestDispatcher to ServletController
[13:47:44 CDT(-0500)] <EricDalquist> 2. PortletController calls prd.include(PortletRequest, PortletResponse)
[13:48:16 CDT(-0500)] <EricDalquist> 3. ServletController uses ServletContext.getContext("/uPortal") to get uPortal's ServletContext object
[13:48:51 CDT(-0500)] <EricDalquist> 4. ServletController creates a RequestDispatcher using uportalsServletContext.createRequestDispatcher("/rest/uri");
[13:49:43 CDT(-0500)] <EricDalquist> 5. ServletController calls rd.include(request, responseWrapper) //That is a HttpServletResponseWrapper that captures EVERYTHING so the response data written doesn't break the portlet's response
[13:50:21 CDT(-0500)] <EricDalquist> 6. ServletController grabs the String or byte[]+encoding data written out by the portal's rest UI
[13:50:51 CDT(-0500)] <EricDalquist> 6. ServletController converts this to some pre-defined data structure (JsonNode?) and stores it in a well named request attribute
[13:51:06 CDT(-0500)] <EricDalquist> 7. PortletController pulls the data out of the well named request attribute
[13:51:07 CDT(-0500)] <EricDalquist> tada
[13:51:13 CDT(-0500)] <drewwills> oh jeez
[13:51:14 CDT(-0500)] <EricDalquist> wonderfully gross
[13:51:46 CDT(-0500)] <drewwills> this is much easier to follow and implement: JsonNode json = restTemplate.getForObject("http://localhost:8080/ssp-platform/api/people.json?searchTerms\[\]= &username= ", JsonNode.class, params);
[13:51:46 CDT(-0500)] <EricDalquist> and remember the portlet is already one request dispatcher deep (that is how uportal/pluto renders portlets, via a cross context request dispatcher)
[13:51:51 CDT(-0500)] <EricDalquist> yes
[13:51:54 CDT(-0500)] <EricDalquist> but that will break
[13:51:59 CDT(-0500)] <drewwills> yeah i remeber localhost
[13:52:05 CDT(-0500)] <EricDalquist> if you try that on our servers they drop off the network
[13:52:32 CDT(-0500)] <EricDalquist> as an artifact of the networking magic that goes on with hardware load balancers
[13:52:54 CDT(-0500)] <EricDalquist> so an alternative is to put work into defining a uPortal specifc API
[13:53:00 CDT(-0500)] <EricDalquist> we create a -api JAR
[13:53:06 CDT(-0500)] <EricDalquist> that gets deployed to shared/lib
[13:53:23 CDT(-0500)] <EricDalquist> then provide impls of those APIs as PortalContext attributes
[13:53:30 CDT(-0500)] <EricDalquist> so you'd do something like:
[13:53:34 CDT(-0500)] <drewwills> yes actually this was the Academus approach
[13:54:02 CDT(-0500)] <EricDalquist> PersonLookupService please = (PersonLookupService)portalContext.getAttribute("org.jasig.portal.api.personLookup")
[13:54:17 CDT(-0500)] <EricDalquist> which isn't unreasonable
[13:54:21 CDT(-0500)] <drewwills> public static void setInternalApiMajic(); // called from portal
[13:54:25 CDT(-0500)] <EricDalquist> no
[13:54:35 CDT(-0500)] <EricDalquist> not the shared/injected static API approach
[13:54:47 CDT(-0500)] <EricDalquist> the portlets get the API reference from the PortalContext interface which is part of the portlet spec
[13:55:01 CDT(-0500)] <EricDalquist> and only hold it for the duration of the portlet's lifecycle
[13:55:12 CDT(-0500)] <EricDalquist> it at least prevents classloader leakage
[13:55:38 CDT(-0500)] <EricDalquist> the shared API may actually be more maintainable
[13:55:46 CDT(-0500)] <EricDalquist> since then we have a concrete class structure
[13:55:54 CDT(-0500)] <drewwills> how much would you have to put in the shared classloader? just the interface(s)?
[13:56:00 CDT(-0500)] <EricDalquist> just the interfaces
[13:56:17 CDT(-0500)] <EricDalquist> the rest api approach could be bad unless we add unit tests for every rest API that asserts the output data format
[13:56:21 CDT(-0500)] <EricDalquist> and commit to never changing it
[13:56:38 CDT(-0500)] <EricDalquist> the shared API approach makes that requirement more apparent
[13:56:46 CDT(-0500)] <EricDalquist> as we would have a specifically defined public APUI
[13:59:00 CDT(-0500)] <drewwills> i suppose under-the-hood we could have the same code do the heavy lifting for both the REST & Java APIs, e.g. PersonLookupHelperImpl
[13:59:05 CDT(-0500)] <EricDalquist> yup
[13:59:10 CDT(-0500)] <EricDalquist> so we get the API impl
[13:59:27 CDT(-0500)] <EricDalquist> then refactor the rest api to use the same service
[14:00:19 CDT(-0500)] <drewwills> i haven't looked, but i bet you will find the REST api already closer to that atm than you would have, say, 2005
[14:00:43 CDT(-0500)] <EricDalquist> oh I'm sure
[14:00:46 CDT(-0500)] <drewwills> i'm looking at PeopleRESTController
[14:00:58 CDT(-0500)] <EricDalquist> this will all also get easier once we get moved completely to spring-security
[14:01:01 CDT(-0500)] <EricDalquist> for authn and authz
[14:01:02 CDT(-0500)] <drewwills> and it seems to hold tolerably to the One Responsibility Rule
[14:01:10 CDT(-0500)] <EricDalquist> then we have less concern over req/res access
[14:01:23 CDT(-0500)] <EricDalquist> as access state will be managed in a ThreadLocal by spring-security
[14:01:28 CDT(-0500)] <drewwills> doing controller-y things, and delegating the deeper things to other classes
[14:01:57 CDT(-0500)] <drewwills> athena?
[14:02:10 CDT(-0500)] <drewwills> anyone else want to comment on direction?
[14:03:03 CDT(-0500)] <peterjhart> no
[14:03:10 CDT(-0500)] <drewwills> i need to head to lunch... bb in a bit
[14:03:14 CDT(-0500)] <EricDalquist> k
[14:03:20 CDT(-0500)] <EricDalquist> I'll be here for ~3 more hours
[14:47:16 CDT(-0500)] <athena> back - was at lunch
[14:52:33 CDT(-0500)] <athena> ok
[14:52:37 CDT(-0500)] <athena> read over the above
[14:52:45 CDT(-0500)] <athena> so first of all, we actually do need the REST services for other things
[14:52:48 CDT(-0500)] <athena> specifically umobile
[14:52:54 CDT(-0500)] <athena> but potentially also useful for other applications
[14:53:09 CDT(-0500)] <athena> so i think regardless of whether we create another API or not we still need to standardize those REST services
[14:53:17 CDT(-0500)] <athena> also, i'd recommend that we don't cast to JsonNode3
[14:53:29 CDT(-0500)] <athena> that takes some freedom away from the client projects
[14:53:35 CDT(-0500)] <athena> since i might want the results back as a map
[14:53:47 CDT(-0500)] <athena> or i might want to create a lightweight java pojo to bind to
[14:54:09 CDT(-0500)] <athena> we should support those kind of choices from the client
[14:54:20 CDT(-0500)] <athena> maybe the solution would be to have the expected type as a parameter
[14:54:29 CDT(-0500)] <athena> and then we can use then when calling objectMapper.readValue
[14:54:39 CDT(-0500)] <athena> makes the API friendlier but doesn't take that flexibility away from the client
[14:58:01 CDT(-0500)] <EricDalquist> athena: so you're saying that no matter what we need to standardize our REST APIs
[14:58:27 CDT(-0500)] <EricDalquist> and because of that we might as well do the dispatcher chaining trick to just use those
[14:59:16 CDT(-0500)] <athena> yes
[14:59:20 CDT(-0500)] <EricDalquist> ok
[14:59:22 CDT(-0500)] <EricDalquist> I'm fine with that
[14:59:26 CDT(-0500)] <athena> unless someone really has time to create those new standalone APIs
[14:59:31 CDT(-0500)] <athena> i mean i think that would be a terrific move
[14:59:37 CDT(-0500)] <EricDalquist> but I think we need to add more tests that assert the output format
[14:59:43 CDT(-0500)] <athena> but from talking to drew, it doesn't sound to me like he has time to do that as part of his project
[14:59:46 CDT(-0500)] <EricDalquist> so that refactoring doesn't break integrations
[14:59:50 CDT(-0500)] <athena> yes, that sounds like a great idea
[15:00:00 CDT(-0500)] <athena> and i think we probably already need to start doing that anyway
[15:00:06 CDT(-0500)] <athena> so that sounds like a great idea to me
[15:00:26 CDT(-0500)] <athena> and since umobile uses these services, i can probably help out
[15:02:28 CDT(-0500)] <athena> so how much of this can we abstract away from the portlet's implementation?
[15:02:45 CDT(-0500)] <EricDalquist> quite a bit
[15:02:46 CDT(-0500)] <athena> can it just be a target url, some parameters, and an expected return type?
[15:03:05 CDT(-0500)] <EricDalquist> so one thing I think we need to do is add a portlet request property that tells the portlet the portal's context path
[15:03:08 CDT(-0500)] <EricDalquist> which would be easy enough to do
[15:03:19 CDT(-0500)] <athena> oh that's be pretty cool
[15:03:26 CDT(-0500)] <EricDalquist> we have to have that really
[15:03:29 CDT(-0500)] <EricDalquist> or people will go nuts
[15:03:33 CDT(-0500)] <EricDalquist> and it is easy to add
[15:03:35 CDT(-0500)] <athena> some of the portlet's already have a config string in them
[15:03:43 CDT(-0500)] <athena> that in umobile gets copied from the filter file
[15:04:02 CDT(-0500)] <athena> so we could go that approach, but using something more automagic would be even better
[15:04:09 CDT(-0500)] <EricDalquist> then we create an API that looks like "public Object callPortalRestApi(PortletRequest, PortletResponse, String uri, Map<String, String[]> params)
[15:04:28 CDT(-0500)] <EricDalquist> not sure what we do for return type stil
[15:04:38 CDT(-0500)] <EricDalquist> that depends on how many external libs we depend on in this utility library
[15:04:52 CDT(-0500)] <athena> ah, good point
[15:05:00 CDT(-0500)] <athena> maybe we could have two methods?
[15:05:10 CDT(-0500)] <athena> one that just returns whatever the raw thing is and lets the portlet handle it itself
[15:05:25 CDT(-0500)] <athena> and one that uses jackson and takes a class as a parameter?
[15:06:01 CDT(-0500)] <EricDalquist> but what is the raw thing?
[15:06:02 CDT(-0500)] <athena> that probably covers the expected use and only pulls in one lib, but lets the portlet have flexibility if it needs it
[15:06:11 CDT(-0500)] <EricDalquist> this approach will have one of two things
[15:06:13 CDT(-0500)] <EricDalquist> a String
[15:06:19 CDT(-0500)] <EricDalquist> or a byte[] and encoding
[15:06:36 CDT(-0500)] <EricDalquist> since we're literally capturing the rest api response data
[15:07:53 CDT(-0500)] <athena> so is the concern that we're not sure which of those to send back?
[15:08:09 CDT(-0500)] <EricDalquist> so we have two options
[15:08:13 CDT(-0500)] <EricDalquist> one is we always return a String
[15:08:37 CDT(-0500)] <EricDalquist> and just do new String(byte[], encoding) if the rest API wrote to the output stream instead of the writer
[15:09:12 CDT(-0500)] <athena> do we have expected use cases where something other than a string would be returned?
[15:09:19 CDT(-0500)] <EricDalquist> I don't know
[15:09:31 CDT(-0500)] <EricDalquist> if you wanted to use the import/export rest APIs then yes
[15:09:38 CDT(-0500)] <EricDalquist> oh
[15:09:39 CDT(-0500)] <athena> ok, that's fair enough
[15:09:46 CDT(-0500)] <EricDalquist> well this is limited in a way to
[15:09:52 CDT(-0500)] <EricDalquist> well no
[15:09:55 CDT(-0500)] <EricDalquist> never mind that last line
[15:09:58 CDT(-0500)] <athena> so could we have two versions?
[15:10:10 CDT(-0500)] <athena> 1. sends back a byte[] and encoding
[15:10:25 CDT(-0500)] <athena> and 2. uses Jackson to parse the response and cast it to the provided type
[15:10:43 CDT(-0500)] <athena> it seems like that'd make the API friendly but still let you connect to something more low-level if you needed to?
[15:11:37 CDT(-0500)] <EricDalquist> well 1 would need to return something like:
[15:11:37 CDT(-0500)] <EricDalquist> RestResponse {
[15:11:37 CDT(-0500)] <EricDalquist> String writerOutput;
[15:11:37 CDT(-0500)] <EricDalquist> byte[] streamOutput;
[15:11:37 CDT(-0500)] <EricDalquist> String streamEncoding;
[15:11:37 CDT(-0500)] <EricDalquist> }
[15:11:51 CDT(-0500)] <EricDalquist> then the client would check both String and byte[]
[15:11:55 CDT(-0500)] <EricDalquist> and see which one isn't null
[15:12:00 CDT(-0500)] <EricDalquist> that would be good
[15:12:07 CDT(-0500)] <EricDalquist> since we could then also add things like status code
[15:12:08 CDT(-0500)] <EricDalquist> and headers
[15:12:12 CDT(-0500)] <EricDalquist> if those might be usful
[15:12:20 CDT(-0500)] <athena> oh i see - the issue is we're not sure if we're going to have a string or a bytestream?
[15:12:26 CDT(-0500)] <EricDalquist> right
[15:12:36 CDT(-0500)] <EricDalquist> you get a string if the rest API writes to response.getWriter()
[15:13:52 CDT(-0500)] <drewwills> no to which?
[15:13:53 CDT(-0500)] <EricDalquist> the only consideration on the Jackson stuff is how many exteral deps do we want?
[15:13:59 CDT(-0500)] <athena> yeah
[15:14:01 CDT(-0500)] <EricDalquist> no it isn't a problem,
[15:14:04 CDT(-0500)] <drewwills> good
[15:14:11 CDT(-0500)] <athena> it seems to me like it's probably not the end of the world to have just jackson as a dependency
[15:14:21 CDT(-0500)] <EricDalquist> well I'd go one step further
[15:14:22 CDT(-0500)] <athena> i guess i'd be more worried about relying heavily on particular spring versions?
[15:14:30 CDT(-0500)] <EricDalquist> I'd have two APIs a plain one
[15:14:35 CDT(-0500)] <EricDalquist> and a Spring 3 one
[15:17:06 CDT(-0500)] <EricDalquist> and that dictated the conversion
[15:17:28 CDT(-0500)] <EricDalquist> the HttpInputMessage interface is really simple
[15:17:39 CDT(-0500)] <EricDalquist> and we could create our own impl to wrap the captured output
[15:17:50 CDT(-0500)] <athena> ok yes, that completely makes sense
[15:17:56 CDT(-0500)] <EricDalquist> so "uportal-rest-api-portlet-client" would have:
[15:19:21 CDT(-0500)] <EricDalquist> RestResponse callPortalRestApi(PortletRequest, PortletResponse, String uri, Map<String, String[]> params)
[15:19:40 CDT(-0500)] <drewwills> btw I can certainly work on this vision in the context of my project... if we do it in a way in which I can also get some help from athena, that's even better
[15:20:19 CDT(-0500)] <EricDalquist> I would add this as a module in the uPortal project itself
[15:20:45 CDT(-0500)] <EricDalquist> then we add a BaseRequestPropertiesManager impl which exposes the portal's request context name as a portlet request property
[15:21:10 CDT(-0500)] <EricDalquist> to use callPortalRestApi in your portlet you would have to add the UPortalRestApiProxyServlet to the web.xml
[15:21:31 CDT(-0500)] <athena> do you mean create this as submodule of uportal or include the module in uportal's dependencies?
[15:22:57 CDT(-0500)] <EricDalquist> hrm
[15:23:05 CDT(-0500)] <drewwills> could we combine this utility with uportal-search-api? (though after maybe renaming it)
[15:23:06 CDT(-0500)] <EricDalquist> I was thinking as a submodule of uportal
[15:23:15 CDT(-0500)] <EricDalquist> though we need to move that search-api out too
[15:23:23 CDT(-0500)] <drewwills> yeah submodule b/c it's uP-specific
[15:23:24 CDT(-0500)] <athena> it might be eaiser to have it as a separate module
[15:23:25 CDT(-0500)] <EricDalquist> so maybe we actually get that jasig-portlet-utils going
[15:23:32 CDT(-0500)] <EricDalquist> and move the search api there
[15:23:35 CDT(-0500)] <EricDalquist> as well as this
[15:25:27 CDT(-0500)] <EricDalquist> then down the road we add another "uportal-rest-api-portlet-spring-client"
[15:25:33 CDT(-0500)] <EricDalquist> which depends on "uportal-rest-api-portlet-client"
[15:25:40 CDT(-0500)] <drewwills> seems like either (1) combine w/ search-api (2) move to jasig-portlet-utils or (3) both
[15:25:40 CDT(-0500)] <athena> sounds great to me
[15:26:09 CDT(-0500)] <EricDalquist> and adds:
[15:26:09 CDT(-0500)] <EricDalquist> T callPortalRestApi(PortletRequest, PortletResponse, String uri, Map<String, String[]> params, HttpMessageConverter<T>)
[15:29:00 CDT(-0500)] <athena> and we could improve it from there
[15:29:05 CDT(-0500)] <drewwills> i would probably leave moving the search api as a roadmap item, for the same of triage
[15:29:15 CDT(-0500)] <athena> i can help with some of this, but certainly not anytime in the next week
[15:29:17 CDT(-0500)] <EricDalquist> we don't
[15:29:24 CDT(-0500)] <EricDalquist> I had this working a while back locally
[15:29:27 CDT(-0500)] <athena> i don't think there's any direct relationship between this and the search module
[15:29:28 CDT(-0500)] <EricDalquist> but it never went anywhere
[15:33:39 CDT(-0500)] <drewwills> if I had this I could hit ANY rest api... not just ones I had made interfaces for
[15:33:40 CDT(-0500)] <EricDalquist> so designing a whole new API, implementation and way to provide it as a portal context attribute?
[15:33:42 CDT(-0500)] <athena> it sort of seems like most of the planning is already there? you'd just need to implement the steps eric already laid out
[15:33:46 CDT(-0500)] <EricDalquist> right
[15:34:00 CDT(-0500)] <athena> yes, you could hit any of the existing rest services
[15:34:08 CDT(-0500)] <athena> plus quickly attach to new ones you created
[15:34:14 CDT(-0500)] <drewwills> yeah I was picturing a "whole new API" of 1 interface, maybe 2 methods
[15:34:14 CDT(-0500)] <EricDalquist> this already exists: https://github.com/Jasig/portlet-utils
[15:34:15 CDT(-0500)] <athena> so if you needed a new service you could jsut add it to uportal and go
[15:34:33 CDT(-0500)] <EricDalquist> yes but it needs to be a well thought out API
[15:34:36 CDT(-0500)] <athena> so we just need a small library that's a submodule of portlet-utils
[15:34:38 CDT(-0500)] <athena> yes, it does
[15:38:06 CDT(-0500)] <drewwills> this is about getting all oars in the same boat, rowing the same way
[15:38:47 CDT(-0500)] <EricDalquist> right
[15:39:00 CDT(-0500)] <EricDalquist> so I thinkt he least effort is the rest api proxy code
[15:44:33 CDT(-0500)] <athena> also i think there's a really interesting use case for leveraging the uportal permissions framework for portlet use
[15:44:46 CDT(-0500)] <drewwills> yes, totally
[15:44:59 CDT(-0500)] <drewwills> need discoverable groups & so forth
[15:45:12 CDT(-0500)] <athena> could even create custom permissions
[15:45:12 CDT(-0500)] <drewwills> isUserInRole only goes so far
[15:45:14 CDT(-0500)] <athena> yes.
[15:45:25 CDT(-0500)] <athena> things like the announcement portlet feed permissions could really use uportal permissions
[15:45:30 CDT(-0500)] <EricDalquist> so one thing we'll have to look into is overhead
[15:45:33 CDT(-0500)] <athena> would take some dev to get to that point, but i's possible
[16:16:53 CDT(-0500)] <drewwills> one more question EricDalquist – calling the rest api from a servlet (not portlet) not a problem, correct?
[16:17:28 CDT(-0500)] <EricDalquist> using the request dispatcher approach?
[16:19:00 CDT(-0500)] <drewwills> yeah
[16:19:13 CDT(-0500)] <EricDalquist> right
[16:19:18 CDT(-0500)] <drewwills> fewer steps even
[16:19:18 CDT(-0500)] <EricDalquist> that isn't a problem
[16:19:21 CDT(-0500)] <EricDalquist> right
[16:19:26 CDT(-0500)] <EricDalquist> it is the same as the portlet
[16:19:29 CDT(-0500)] <EricDalquist> just one less step
[16:19:50 CDT(-0500)] <drewwills> whereas the java api/portal context approach could require extra finessing
[16:21:38 CDT(-0500)] <EricDalquist> right
[16:21:44 CDT(-0500)] <EricDalquist> since the servlet has no context from the portal]
[16:47:09 CDT(-0500)] <drewwills> can you stuff GET/POST/PUT/DELETE into a requestDispatcher?
[16:47:38 CDT(-0500)] <EricDalquist> if you fake ity
[16:47:48 CDT(-0500)] <EricDalquist> with a custom HttpServletRequestWrapper
[16:49:57 CDT(-0500)] <EricDalquist> but doable
[16:55:15 CDT(-0500)] <drewwills> so... any fancy Spring class to turn "/some/url/ ?foo= " into "/some/url/with?foo= " given [ p1:with, p2:bar ]?
[16:55:41 CDT(-0500)] <athena> could take a look at the rest template source code
[16:55:47 CDT(-0500)] <athena> see if that's actually using another library
[16:58:48 CDT(-0500)] <athena> yeah
[16:58:54 CDT(-0500)] <athena> using an internal spring lib - UriTemplate
[17:10:40 CDT(-0500)] <athena> yes, i think we should use normal URL parameter behavior
[17:17:30 CDT(-0500)] <drewwills> sounds lovely... I thought I had seen that the getPeople() method expected differently, but I mis-remembered
[18:36:54 CDT(-0500)] <peterjhart> Here is a mockup of what a Fragment Manager UI could look like: https://wiki.jasig.org/display/UPC/uPortal+4+DLM+Fragment+Manager+Interface