Load Freemarker Template from External Url - google-app-engine

I have implemeted spring boot app where we need to send email using freemarker.
App is going to deployed on google app engine, where file structure is not available to store the templates. So, I saved templates on google storage with public access. But not able to load in freemarker template engine.
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("/home/dnilesh/Downloads/helloworld-springboot/src/main/resources/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
This above configuration will work on development env. But on Google app engine I dont have directory to store template.
I tried this :
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("https://storage.googleapis.com/nixon-medical/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
But freemarker not loading Template from External URL. How can I load this?

For external URL,you should use URLTemplateLoader:
If your template source accesses the templates through an URL, you needn't implement a TemplateLoader from scratch; you can choose to subclass freemarker.cache.URLTemplateLoader instead and just implement the URL getURL(String templateName) method.
See code sample

Though there is an accepted answer, I did not find the integration with spring boot. So I have done this
I was trying to read Freemarker template from google cloud storage with spring boot application.
So, I have done the following and it worked for me.
Implement URLTemplateLoader and only override getURL method On
FreeMarkerConfigurer bean, set pretemplate as custom template
CloudTemplateLoader - my custom loader
public class CloudTemplateLoader extends URLTemplateLoader {
private URL root;
public CloudTemplateLoader(URL root) {
super();
this.root = root;
}
#Override
protected URL getURL(String template) {
try {
return new URL(root, "/" + template);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
}
FreeMarkerConfigurer Bean to set my custom loader
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() throws MalformedURLException {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
Properties properties = new Properties();
properties.setProperty("localized_lookup", "false");
freeMarkerConfigurer.setFreemarkerSettings(properties);
freeMarkerConfigurer.setPreTemplateLoaders(new CloudTemplateLoader(new URL("https://storage.googleapis.com")));
freeMarkerConfigurer.setDefaultEncoding("UTF-8");
return freeMarkerConfigurer;
}
And my controller is following
#GetMapping
public String index() {
return "<bucket-name>/index.ftl";
}
Don't forget to upload the template on the google cloud or s3. For test purpose, I added public access on my index.ftl file.

You can use a Thymeleaf resolver to load the external files.
https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

Related

Configuring Webpack build for file:// use in CEF

I have to develop a webapp for a CEF-Browser environment. There is no HTTP server available, everything will be served over file:// protocol.
When developing a Webapp nowadays one doesn't get round working with a framework like react/vue for frontend. The standard webpack build scripts of those build a bundle which only works served over HTTP.
Is it possible to configure webpacks build bundle to work on file:// or is there another way to use react or vue via file://?
I'm suggest read CEF wiki more carefully. You are especially interested in https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-request-handling
In short:
You can register custom scheme handler to serve resources over http+custom fake domain.
You can pack resources in zip for example if you like, or leave them at file system as is (but in that case you can expect that some funny users can edit your files, and then report back unexisting errors back to you).
Important helpers already done (but you can write own when need.)
You can... many other things.
Main thing that "file" scheme are more restricted, and for example you can't do XHR requests. But for custom handler you can. Even if dynamic loader for some reason use XHR instead DOM-based loading it will work again same as on http without touching network.
cefclient itself also has usage of custom schemes. Check URL of Tests->Other... in menu. :)
PS: Sorry that my answer doesnt have direct answer for your question. But, custom resource handling in CEF is so common, that i'm just should say about.
fddima is right - you don't need to configure your webpack (although it would be theoretically possible). Instead you can use custom scheme handler in CEF. I made it work with angular at work.
I wrote blog post on how to serve web application via 'file' protocol in CEF.
What you want to add is your scheme handler and its factory:
using System;
using System.IO;
using CefSharp;
namespace MyProject.CustomProtocol
{
public class CustomProtocolSchemeHandler : ResourceHandler
{
// Specifies where you bundled app resides.
// Basically path to your index.html
private string frontendFolderPath;
public CustomProtocolSchemeHandler()
{
frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
}
// Process request and craft response.
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
var requestedFilePath = frontendFolderPath + fileName;
if (File.Exists(requestedFilePath))
{
byte[] bytes = File.ReadAllBytes(requestedFilePath);
Stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
MimeType = GetMimeType(fileExtension);
callback.Continue();
return true;
}
callback.Dispose();
return false;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
}
And then register it before calling Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});

MSF4J: Serving static content

Can MSF4J application serve static content without using the Mustache template engine. I have developed a REST service which will be consumed by an already developed angular web app. Now I need to package the same angular app with the micro service so it will render in the browser and will consume the service via ajax calls.
MSF4J does not directly support serving static content. From your question what I understood is that you want to point the MSF4J server to a directory and serve resources in that directory by their relative path or something similar. In this case what you can do is to write an MSF4J service method with a wildcard path and serve the static content that matches the path of the request.
#Path("/")
public class FileServer {
private static final String BASE_PATH = "/your/www/dir";
#GET
#Path("/**")
public Response serveFiles(#Context Request request) {
String uri = request.getUri();
System.out.println("Requested: " + uri);
File file = Paths.get(BASE_PATH, uri).toFile();
if (file.exists()) {
return Response.ok().entity(file).build();
} else {
return Response.status(404).entity("<h1>Not Found</h1>").build();
}
}
}

(Android Studio) Connecting an app to Google Endpoints Module

I'm having trouble following the second step here.
I really don't understand how this sample does anything other than return a simple toast message. How does it utilize the API to display that message?
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
I'm afraid my this sample is too complex for my limited knowledge. How exactly do I "talk" to the Google Endpoints Module when running an app? Specifically, What is EndpointsAsyncTask();?
Are there any resources listing all the methods available to me? Is there a simpler example of an app communicating with a Google Cloud Endpoint?
The service methods available to you are defined by the backend source in section 1.
In the example you posted, this line: myApiService.sayHi(name).execute()
is an actual invocation call to the backend that you defined by annotating #ApiMethod("sayHi") on the method in the MyEndpoint.java class of your backend module.
The reason your Android app defines an EndpointsAsyncTask is because slow operations such as calls that hit the network need to happen off of the UI thread to avoid locking the UI. The demo simply puts the returned value into a Toast but you could modify onPostExecute() to do whatever you'd like with the result.
For more info on Google Endpoints check out:
https://cloud.google.com/appengine/docs/java/endpoints/
And for info about using an Android AsyncTask look here:
http://developer.android.com/reference/android/os/AsyncTask.html

spring mvc catch all route but only unknown routes

I've got a spring boot app with angular on the frontend.
I'm using ui-router with html5 mode and I would like spring to render the same index.html on all unknown routes.
// Works great, but it also overrides all the resources
#RequestMapping
public String index() {
return "index";
}
// Seems do be the same as above, but still overrides the resources
#RequestMapping("/**")
public String index() {
return "index";
}
// Works well but not for subdirectories. since it doesn't map to those
#RequestMapping("/*")
public String index() {
return "index";
}
So my question is how can i create a fallback mapping but that lets through the resources?
The simplest way I found was implementing a custom 404 page.
#Configuration
public class MvcConfig {
#Bean
public EmbeddedServletContainerCustomizer notFoundCustomizer(){
return new NotFoundIndexTemplate();
}
private static class NotFoundIndexTemplate implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
}
}
}
Neil McGuigan propopes a HandlerInterceptor, but I wasn't able to understand how that would be implemented. I't would be great to see how this would be implemented, as single page applications using html5 history push state will want this behaviour. And I have not really found any best practices to this problem.
Define an entry point for all the urls in your web.xml file like following:
<error-page>
<error-code>404</error-code>
<location>/Error_404</location>
</error-page>
This would catch all 404 i.e page not found errors and throw /Error_404 url, catch it in your controller and push to desired place.
You could handle all of non-matched requests in 404 handler. Take a look at this, there are several options
Another thing you could do is to override DefaultAnnotationHandlerMapping, and to add a sort of catch-all controller by setting the defaultHandler property.
public void setDefaultHandler(Object defaultHandler)
Set the default handler for this handler mapping. This handler will be returned if no specific mapping was found.
Default is null, indicating no default handler.
try to use #ExceptionHandler in your controller, change Exception.class by the class of the exception who you want to handle.
#ExceptionHandler(value = {Exception.class})
public String notFoundErrorHandler() {
return "index";
}

How can I get AngularJS working with the ServiceStack FallbackRoute attribute to support HTML5 pushstate Urls?

I am building a client/server solution, using an AngularJS Single Page App as the client component and a Self-Host ServiceStack RESTful API as the server component. A single Visual Studio Console Application Project holds HTML and JavaScript files for the AngularJS component, along with C# classes for bootstrapping the ServiceStack AppHost (I have devolved Interface and Service responsibilities to separate Visual Studio Projects).
I have set all HTML and JavaScript files to have a 'Build Action' of 'None' and a 'Copy to Output Directory' of 'Copy if newer'.
Everything is working very well as long as I am prepared to put up with having a '#' in my site URLs. I would like to eliminate this by using HTML5 pushstate URLs.
Effectively this means I need to persuade ServiceStack to serve up my default Single Page App HTML shell page whenever a non-existent route is requested. There is now a FallbackRoute attribute available in ServiceStack which appears to have been added exactly for this purpose.
However, I am unsure how to use it. I have found people asking similar questions here, here and here. But the answers given were all before the new FallbackRoute attribute arrived.
Essentially, I am looking for a simple, yet complete example of how to use the FallbackRoute attribute to ensure any requests to non-existent routes are redirected to a single static HTML page.
The RazorRockstars.Web has an implementation. I'll modify it to use a wildcard path and a default view:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
public string PathInfo { get; set; }
}
public class RockstarsService : Service
{
[DefaultView("Index")]
public object Any(Fallback request)
{
request.PathInfo = base.Request.PathInfo;
return request;
}
// ...
}
Since this is a service it requires a View page (details here) rather than a content page.
In the RockStars example, I can't determine what view would be rendered for the FallBackResponse, but setting the view explicitly should be all you need.
The [DefaultView("Index")] attribute I added to the Any method maps the response to a Views/Index.cshtml file. The Index.cshtml file can be empty but for a template declaration, and the complete markup for your single page app can be in your template file (i.e. _Layout.cshtml)
Without Razor
Read the html into a string and return it, while setting the content type to "text/html" with an attribute, see wiki docs on service return types
public class RockstarsService : Service
{
static string readContents;
[AddHeader(ContentType = "text/html")]
public string Any(Fallback request)
{
// check timestamp for changes for production use
if (readContents == '') {
using (StreamReader streamReader = new StreamReader(pathFromConfigFile, Encoding.UTF8))
{
readContents = streamReader.ReadToEnd();
}
}
return readContents;
}
// ...
}
It turns out it is all very simple with the FallbackRoute functionality, once you work out how to use it properly:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
public class FallBackService : Service
{
public object Any(Fallback request)
{
return new HttpResult(new FileInfo("index.html")) {ContentType = "text/html"};
}
}
Once this is in place, I find 'index.html' is indeed getting served up whenever I try to hit a non-existent route.
Any static files, such as JavaScript and CSS resources, get served up as normal (as long as they have a 'Copy to Output Directory' setting of 'Copy if newer', of course).
This works like a charm with the HTML5 Push-state functionality in AngularJS.

Resources