Render Page Components as JSON

CaaS (Content as a Service) is a common need in today’s multi-channel world. Typically CaaS use cases require that the content is devoid of presentation markup so that the consumer can present the content as it desires. Other times comsumers may wish to pull rendered content. This cookbook provides a simple REST example (a single Groovy based rest controller script) that will give you a way to render all of the components associated to a page.

Prerequisites

  • None

Step 1: Create a REST Controller

  • Under Scripts/rest right click and click create controller
    • Enter get-rendered-components.get as the controller name

  • Add the following code to the controller.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.PrintWriter

import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.FilterConfig
import javax.servlet.ServletException
import javax.servlet.ServletOutputStream
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpServletResponseWrapper
import javax.servlet.WriteListener
import groovy.util.XmlSlurper

def result = [:]
def targetPage = params.pageId

if (targetPage != null) {
        result.page = targetPage

        def pageItem = siteItemService.getSiteItem(targetPage)

        if (pageItem != null) {
                def componentPaths = pageItem.queryValues("//include")
                result.components = []

                if (componentPaths != null) {
                        componentPaths.each { componentPath ->
                                if (componentPath.endsWith(".xml") && !componentPath.startsWith("/site/website") ) {
                                        logger.info("Including component ${componentPath} into JSON response")

                                        def component = [:]
                                        component.id = componentPath

                                        // wrap the response to capture the output
                                        def wrappedResponse = new CapturingResponseWrapper(response)

                                        // "include" the page that does the actual work
                                        request.getRequestDispatcher("/crafter-controller/component?path=" + componentPath).include(request, wrappedResponse)

                                        // get the captured output, parse it and prepare the actual response
                                        def capturedOut = wrappedResponse.getCaptureAsString()

                                        component.markup = capturedOut

                                        result.components.add(component)
                                }
                        }
                } else {
                        result.message = "No components found"
                }
        } else {
                result.message = "Page '${targetPage}` not found"
        }
} else {
        result.message = "Parameter pageId is required."
}

return result

protected class CapturingResponseWrapper extends HttpServletResponseWrapper {

        private final ByteArrayOutputStream capture
        private ServletOutputStream output
        private PrintWriter writer

        public CapturingResponseWrapper(HttpServletResponse response) {
                super(response)
                capture = new ByteArrayOutputStream(response.getBufferSize())
        }

        @Override
        public ServletOutputStream getOutputStream() {
                if (writer != null) {
                        throw new IllegalStateException("getWriter() has already been called on this response.")
                }

                if (output == null) {
                        output = new ServletOutputStream() {

                                @Override
                                public void write(int b) throws IOException {
                                        capture.write(b)
                                }

                                @Override
                                public void flush() throws IOException {
                                        capture.flush()
                                }

                                @Override
                                public void close() throws IOException {
                                        capture.close()
                                }

                                @Override
                                public void setWriteListener(WriteListener writeListener) {
                                }

                                @Override
                                public boolean isReady() {
                                        return true
                                }
                        }
                }

                return output
        }

        @Override
        public PrintWriter getWriter() throws IOException {
                if (output != null) {
                        throw new IllegalStateException("getOutputStream() has already been called on this response.")
                }

                if (writer == null) {
                        writer = new PrintWriter(new OutputStreamWriter(capture, getCharacterEncoding()))
                }

                return writer
        }

        @Override
        public void flushBuffer() throws IOException {
                super.flushBuffer()

                if (writer != null) {
                        writer.flush()
                }
                else if (output != null) {
                        output.flush()
                }
        }

        public byte[] getCaptureAsBytes() throws IOException {
                if (writer != null) {
                        writer.close()
                }
                else if (output != null) {
                        output.close()
                }

                return capture.toByteArray()
        }

        public String getCaptureAsString() throws IOException {
                return new String(getCaptureAsBytes(), getCharacterEncoding())
        }

}

Step 2: Execute the Service