I wrote a private plugin(MyPlugin) that is being used by other applications. It has a controller named MyPluginController and a bunch of REST actions. They all work fine when access through the mainapp. The plugin also comes with a single page app contained in static.html. I could not figure out how to do the UrlMapping for static.html to be accessible like:
http://example.com/mainapp/MyPluginController/static.html
class MyPluginUrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
/* Not sure what do here ???*/
"/MyPluginController/static.html" (redirect: "/static.html", permanent: true)
}
The html is in myplugin's web-app folder. It is easy to rename it to .gsp and make it work but this is grails and I am sure there must be cleaner way of doing it.
Related
Sometimes I see, in a Grails app's Config.groovy, something like:
log4j = {
appenders {
...
}
root {
...
}
...etc.
}
This is clearly a programmatic way of specifying the log4j configs. I'm wondering:
Is it possible to specify the log4j configs in a log4j.xml file, and then tell the Grails app where to look for it?
If so, where should I place the log4j.xml inside the Grails app, and how/where do I "connect" it to the app?
Grails by default won't use log4j.xml. You have a few options.
Wire up Spring to use log4j.xml. This can be somewhat complex if you aren't familiar with Spring and the required beans. Which you and I already discussed.
Use the Log4j XML plugin for Grails.
For simple use cases the second option would be preferred.
Update
Based on your follow up question in the comments you might be better off configuring logging directly in your plugin using the DSL syntax. Burt explains how this is done in another post.
Grails automatically excludes any file in grails-app/conf that matches log4j.*:
From scripts/_GrailsWar.groovy:
ant.copy(todir:"${stagingDir}/WEB-INF/classes", failonerror:false, preservelastmodified:true) {
fileset(dir:"${basedir}/grails-app/conf") {
exclude(name:"*.groovy")
exclude(name:"log4j.*")
exclude(name:"**/hibernate/**")
exclude(name:"**/spring/**")
}
fileset(dir:"${basedir}/grails-app/conf/hibernate", includes:"**/**")
fileset(dir:"${grailsSettings.sourceDir}/java") {
include(name:"**/**")
exclude(name:"**/*.java")
}
fileset(dir:"${grailsSettings.sourceDir}/groovy") {
include(name:"**/**")
exclude(name:"**/*.groovy")
}
fileset(dir:"${resourcesDirPath}", includes:"log4j.properties")
}
As pointed out, there are other ways to achieve what you want without using the log4j.xml file, but if you do want to use it, you can add some logic to your grails-app/conf/BuildConfig.groovy to ensure that it gets packaged in your WAR:
grails.war.resources = { stagingDir, args ->
copy(todir:"${stagingDir}/WEB-INF/classes", failonerror:false, preservelastmodified:true) {
fileset(dir:"${basedir}/grails-app/conf") {
include(name:"log4j.xml")
}
}
}
I have a custom there, where I try to require some of my css and js files via the ResourceManifest.cs file - I keep into running a quite weird issue tough.
I get the following error:
a 'script' named 'FoundationScript' could not be found
This is my ResourceManifest.cs:
using Orchard.UI.Resources;
namespace Themes.TestTheme
{
public class ResourceManifest : IResourceManifestProvider
{
public void BuildManifest(ResourceManifestBuilder builder)
{
var manifest = builder.Add();
manifest.DefineStyle("Foundation").SetUrl("foundation.min.css");
manifest.DefineScript("FoundationScript").SetUrl("foundation.min.js");
}
}
}
In the Layout.cshtml, I have following:
#{
Script.Require("ShapesBase");
Script.Require("FoundationScript");
Style.Include("site.css");
Style.Require("Foundation");
}
What am I missing here?
The issue here is, that the project Themes has a problem with the dynamic compile mechanism of Orchard (i don't know what is wrong exactly) because it resides in folder Themes. Even if you define a class inside the Themes assembly, it will result in an error telling you there is no such class in that assembly.
solution :
Try re-generating your theme with /CreateProject:true and /IncludeInSolution:true parameters as follows:
codegen theme TestTheme /CreateProject:true /IncludeInSolution:true /BasedOn :TheThemeMachine
It will create your theme in a separate project and orchard will pick your registered ResourceManifest.
Hope this helps.
What is the preferred way to remove a default pipeline contributor (OpenRasta 2.0.3)?
I haven't found a lot on that on the net, but one way seems to be writing a custom DependencyRegistrar, i.e. deriving from DefaultDependencyRegistrar and then e.g. overriding AddDefaultContributors(). Apart from that I doubt that it's the best way to remove just a single pipeline contributor, it seems to need additional per-host (ASP vs. InMemory) work, whereas I would consider messing with pipeline handlers to be a host-agnostic affair.
But even if I'd go this route, this guy here seems to have tried it without success: http://groups.google.com/group/openrasta/browse_thread/thread/d72b91e5994f402b
I tried similar things, but so far couldn't make my custom registrar replace the default.
So what's the simplest and best way to remove a default pipeline contributor, preferable in a host agnostic way? Is there a working example somewhere?
No, you just need to derive from the registrar and use the protected members that are available to imperatively remove the types you don't want auto-registered.
The registrar needs to be registered in your container before you provide it to OpenRasta, otherwise the type has been resolved already.
Answering myself with working code snippets as they might be helpful to others.
So it looks like removing default pipeline contributors cannot be done
in a host agnostic way (although I don't see why OpenRasta could not
be modified to allow for easy deletion of handlers in the future).
The 2 classes that need to be written are in fact independent of the
host(s) used:
public class MyDependencyRegistrar : DefaultDependencyRegistrar
{
protected override void AddDefaultContributors()
{
base.AddDefaultContributors();
PipelineContributorTypes.Remove(typeof(HandlerResolverContributor));
// If we remove the only contributor for the 'well-known'
// IHandlerSelection stage, like done above, we need to add
// another one implements IHandlerSelection, otherwise
// we'll run into errors (and what's the point of a pipeline
// without a handler selector anyway?). So let's do that here:
AddPipelineContributor<MyOwnHandlerResolverContributor>();
}
}
In order to make that Registrar available, we need to create an accessor
like the following, which then needs to be set in the various hosts:
public class MyDependencyResolverAccessor : IDependencyResolverAccessor
{
InternalDependencyResolver resolver;
public IDependencyResolver Resolver
{
get
{
if (resolver == null)
{
resolver = new InternalDependencyResolver();
resolver.AddDependency<IDependencyRegistrar, MyDependencyRegistrar>();
}
return resolver;
}
}
}
For Asp.Net, this seems to work for me:
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
OpenRastaModule.Host.DependencyResolverAccessor =
new MyDependencyResolverAccessor();
For InMemoryHost, which I use for integration testing and in-process access
of my handlers, I haven't found a way around copying the whole class
InMemoryHost and modifying it to my needs. In fact, we don't need
MyDependencyResolverAccessor in this case, as InMemoryHost implements
IDependencyResolverAccessor already. So here's how it could look like. Only the
last line was actually added to the existing code in InMemoryHost:
public class TwinMemoryHost : IHost, IDependencyResolverAccessor, IDisposable
{
readonly IConfigurationSource _configuration;
bool _isDisposed;
public TwinMemoryHost(IConfigurationSource configuration)
{
_configuration = configuration;
Resolver = new InternalDependencyResolver();
Resolver.AddDependency<IDependencyRegistrar, MyDependencyRegistrar>();
...
I have a simple asp mvc app which uses MEF, and there is a route which can be accessed by admins to refresh the directory catalog and compose parts, however one thing I am trying to find out how to do is notify some code when a plugin is loaded / unloaded.
The scenario is that when plugins are loaded they register the routes they need, however when they are unloaded I need them to unload their routes, as subsequent refreshes try to re-register the routes and it bombs.
Are there any events which I can hook into from the MEF objects?
The plugin container is something like:
[ImportMany(typeof(ISomePluginInterface))]
IEnumerable<ISomePluginInterface> Plugins {get; private set;}
Each ISomePluginInterface has something like:
public interface ISomePluginInterface
{
public void PluginLoaded();
public void PluginUnloaded();
}
This is similar in theory to this Stackoverflow question and this was my answer. In your case, you have a similar need, you want to fire an event when the plugin is started, and clean up when it is no longer needed.
Using the same concept, you can use the InterceptingCatalog to register routes, but I wouldn't make it an explicit part of the interface definition to do so, instead, you need to look at how your components fit together as a whole, e.g., if the operations for registering routes won't be used for all plugins, what is the purpose of them existing in the interface definition. You could break out the route registration into a separate interface, the IRouteRegistrar, and use intercepting strategies to automatically call the appropriate registration method when the plugin is used for the first time, e.g., I could break out the interface into:
public interface IPlugin
{
void SomeOperation();
}
public interface IRouteRegistrar : IDisposable
{
void RegisterRoutes();
}
The latter interface does the work of registering routes, and we use the Dispose pattern to ensure that it is cleaned up after it is finished with. Therefore, A sample plugin could resemble:
[Export(typeof(IPlugin))]
public class MyPlugin : IPlugin, IRouteRegistrar
{
public void SomeOperation() { }
public void RegisterRoutes()
{
// Register routes here...
}
protected virtual Dispose(bool disposing)
{
if (disposing)
{
// Unregister routes here...
}
}
void IDisposable.Dispose()
{
Dispose(true);
}
}
I only export as an IPlugin, but I ensure my plugin also implements the IRouteRegistrar. The way we use that, is with a strategy:
public class RouteRegistrarStrategy : IExportedValueInteceptor
{
public object Intercept(object value)
{
var registrar = value as IRouteRegistrar;
if (registrar != null)
registrar.RegisterRoutes();
return value;
}
}
Now, only if the plugin supports that interface will it register routes. This also enables you to apply the route registration interface to other plugins which could be used in a different way. You gain a bit more flexibility. To use that strategy in code, you need to add the MefContrib project to your app, and do a little more wire up:
var catalog = new DirectoryCatalog(".\bin");
var config = new InterceptionConfiguration().AddInterceptor(new RouteRegistrarStrategy());
var interceptingCatalog = new InterceptingCatalog(catalog, configuration);
var container = new CompositionContainer(interceptingCatalog);
I am trying to override the g:link tag so that I can prefix an extra string. Here is my code:
import org.codehaus.groovy.grails.plugins.web.taglib.*
class ApplicationTagLib {
static namespace = "g"
def link = { attrs, body ->
if("es".equalsIgnoreCase(request.stLocale.language)) {
attrs['controller'] = "es/" + attrs['controller']
}
def applicationTagLib = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
applicationTagLib.link.call(attrs, body)
}
}
This works fine except for when I add "es/" the resulting path gets translated into es%2F instead of es/ which causes the link to not work.
Is there a way to prevent this from automatically encoding the new slash or a better way to prefix this string to the controller path?
You should be aware that in Grails the controller package (thus it's location in the project's structure path) does not correlate with the default URL mapping - the structure is flattened.
The slash you add to the controller name is thus encoded as it would otherwise form a part of the URL (and thus not map to a controller).
Perhaps the logic for handling different locale be better placed in a controller anyway.
You can add this '/es' prefix in all links generated by grails tags by configuring your UrlMappings.groovy. If you're using the default one, generated by grails create-app command, you can add '/es' in your URL's like this:
class UrlMappings {
static mappings = {
"/es/$controller/$action?/$id?" { // <---------- added '/es' prefix
constraints {
// apply constraints here
}
}
"/"(view: "/index")
"500"(view: '/error')
}
}
To learn more about URL Mappings, see the Grails guide.
Regards