Most technologies written for JSF/Facelets will work fine. However since Gracelets adds Groovy into the mix, you may want other technologies not written
specifically for JSF/Facelets to be interactable with gracelet scripts. You can write Extensions that will accomplish this. Extensions allow you to initialize
a technology when the application is being deployed. Also you can provide variable resolution, even an entire variable context (see below). Extensions also
are able to be notified when the application is being undeployed and thus have a chance to cleanup or to run other house keeping code.
The
gracelets.api.extension
is the main package holding the interfaces/classes for writing a Gracelet Extension.
Since many extensions will rely on the presence of a third part library, to write an extension, you first extend the
gracelets.api.extension.GraceletsExtensionInitializer
This class is defined as follows:
GraceletsExtensionInitializer.javaToggle Line Numbers |
1
package gracelets.api.extension; 2
3
import javax.faces.context.FacesContext; 4
import javax.servlet.ServletContext; 5
6
import org.slf4j.Logger; 7
import org.slf4j.LoggerFactory; 8
9
10
11
@link 12
13
14
15
@link 16
17
@author 18
@author 19
@version 20
21
public abstract class GraceletsExtensionInitializer { 22
23
private final String name; 24
private final String className; 25
protected Logger log = LoggerFactory.getLogger(this.getClass()); 26
27
28
29
30
@param 31
@param@link 32
33
public GraceletsExtensionInitializer(String name, String className) { 34
this.className = className; 35
this.name = name; 36
} 37
38
39
@param 40
@return 41
42
public abstract boolean shouldLoad (ServletContext ctx); 43
44
45
@return 46
47
public String getName() { return name; } 48
49
50
@link 51
52
@return 53
54
public GraceletsExtension createExtension () { 55
try { 56
return (GraceletsExtension) Class.forName(className, true, Thread.currentThread().getContextClassLoader()).newInstance(); 57
} catch (InstantiationException e) { 58
throw new RuntimeException(e); 59
} catch (IllegalAccessException e) { 60
throw new RuntimeException(e); 61
} catch (ClassNotFoundException e) { 62
throw new RuntimeException(e); 63
} catch (Exception e) { 64
throw new RuntimeException(e); 65
} 66
} 67
68
}
|
This class can use class loaders to determine if a particular class or third party library is available. This allows the initializer to run without being
dependant upon the third part library linked classes. The extension in its constructor will pass the class name that refers to the actual extension as mentioned
below. For example, the JBoss Seam extension initializer is defined as follows:
SeamExtensionInitializer.javaToggle Line Numbers |
1
package gracelets.api.extension.seam; 2
3
import javax.faces.context.FacesContext; 4
import javax.servlet.ServletContext; 5
6
import gracelets.api.extension.GraceletsExtensionInitializer; 7
8
9
@author 10
11
12
public class SeamExtensionInitializer extends GraceletsExtensionInitializer { 13
14
public SeamExtensionInitializer() { 15
super("seam", "gracelets.api.extension.seam.SeamExtension"); 16
} 17
18
@Override public boolean shouldLoad(ServletContext ctx) { 19
try { 20
Class.forName("org.jboss.seam.Component", false, Thread.currentThread().getContextClassLoader()); 21
} catch (ClassNotFoundException e) { 22
return false; 23
} 24
25
return true; 26
} 27
28
}
|
Above we use the Thread context class loader to determine if the JBoss Seam libraries are available. If they are we return true and Gracelets will load the class
referred to in the constructor, otherwise the extension will not be loaded.
The class name passed in the constructor must be a class that implements the
gracelets.api.extension.GraceletsExtension
interface. This interface is
defined as follows:
GraceletExtension.javaToggle Line Numbers |
1
package gracelets.api.extension; 2
3
import gracelets.api.Gracelets; 4
import gracelets.api.binding.GraceletBinding; 5
import gracelets.api.context.VariableContext; 6
import gracelets.api.facelets.GraceletsCompiler; 7
import javax.faces.context.FacesContext; 8
import javax.servlet.ServletContext; 9
10
11
@link 12
13
14
@author 15
@author 16
@version 17
18
public interface GraceletsExtension { 19
20
21
22
23
24
@param 25
@throws 26
27
void initialize (GraceletsExtensionInitializeContext ctx) throws Exception; 28
29
30
31
32
33
@throws 34
35
void destroy () throws Exception; 36
37
38
39
40
41
@param 42
@param 43
@throws 44
45
void postInitialization (FacesContext ctx, GraceletsCompiler compiler) throws Exception; 46
47
48
49
50
51
@param 52
@param 53
@throws 54
55
@see@link 56
57
void initializeBinding (GraceletBinding binding, Object owner) throws Exception; 58
59
60
61
62
@param 63
@return 64
65
VariableContext generateVariableContext (FacesContext ctx); 66
67
68
69
70
71
@param 72
@return 73
74
75
Object resolve (String name); 76
77
}
|
The initialize() method will be called when Gracelets itself is being initialized, during the deployment of the web application.
You can register servlet Filters and JSF PhaseListeners here so that you can plugin to the system without requiring the user
to setup these artifacts in their web.xml or faces-config.xml.
The destroy() method will be called when the application is being undeployed().
The postInitialization() method will be called on the first request when Facelets/Gracelets finishes initialization. You can use this to
add a Factory or outject variables to the variable context that you want available to controllers/views from the start.
The initializeBinding() method will be called whenever a GraceletBinding instance is created giving you a chance to bind() certain variables
from the beginning, you can use the bind() method on the GraceletBinding instance to do this.
The generateVariableContext() method will be called once every request. You can simply return null if your extension does not provide a variable context.
The resolve() method will allow you to do lazy initialization of certain variables related to your extension. See the
bindings section
to know when this will actually be called.
A concrete example
The JPA extension is shown below and will give you a concrete example of how an extension can be implemented:
JPAExtension.javaToggle Line Numbers |
1
package gracelets.api.extension.jpa; 2
3
import java.io.File; 4
import java.io.IOException; 5
import java.io.PrintWriter; 6
import java.net.MalformedURLException; 7
import java.net.URL; 8
import java.net.URLClassLoader; 9
import java.sql.Connection; 10
import java.sql.DriverManager; 11
import java.sql.SQLException; 12
import java.util.ArrayList; 13
import java.util.Collections; 14
import java.util.Enumeration; 15
import java.util.List; 16
import java.util.Properties; 17
import java.util.Set; 18
import java.util.regex.Pattern; 19
20
import javax.faces.context.FacesContext; 21
import javax.persistence.EntityManagerFactory; 22
import javax.persistence.spi.ClassTransformer; 23
import javax.persistence.spi.PersistenceProvider; 24
import javax.persistence.spi.PersistenceUnitInfo; 25
import javax.persistence.spi.PersistenceUnitTransactionType; 26
import javax.servlet.ServletContext; 27
import javax.sql.DataSource; 28
29
import org.slf4j.Logger; 30
import org.slf4j.LoggerFactory; 31
32
import gracelets.api.Gracelets; 33
import gracelets.api.binding.GraceletBinding; 34
import gracelets.api.context.Scope; 35
import gracelets.api.context.VariableContext; 36
import gracelets.api.context.factory.BeanFactory; 37
import gracelets.api.context.factory.BeanGeneratorFactory; 38
import gracelets.api.context.factory.ClosureFactory; 39
import gracelets.api.context.factory.ELFactory; 40
import gracelets.api.extension.GraceletsExtension; 41
import gracelets.api.extension.GraceletsExtensionInitializeContext; 42
import gracelets.api.facelets.GraceletsCompiler; 43
import gracelets.api.resource.ComponentScanner; 44
import gracelets.api.resource.ResourceInfo; 45
import gracelets.util.GeneralUtil; 46
import groovy.lang.Binding; 47
import groovy.lang.Script; 48
49
50
@author 51
@version 52
53
public class JPAExtension implements GraceletsExtension, BeanGeneratorFactory<Object> { 54
55
Logger log = LoggerFactory.getLogger(JPAExtension.class); 56
57
EntityManagerFactory emf; 58
String scope; 59
String sname; 60
boolean autoclose = true; 61
62
public void initialize(GraceletsExtensionInitializeContext ctx) throws Exception { 63
URL gc = null; 64
URL hc = ctx.getServletContext().getResource("/WEB-INF/classes/persistence.properties"); 65
if (hc == null) { 66
gc = ctx.getServletContext().getResource("/WEB-INF/classes/persistence.groovy"); 67
68
if (gc == null) { 69
log.warn("No persistence.properties detected"); 70
return; 71
} 72
} 73
74
Object result = null; 75
Properties props = new Properties(); 76
77
if (hc != null) props.load(hc.openStream()); 78
else { 79
Class<Script> scriptClass = Gracelets.getClassLoader().parseClass(gc.openStream()); 80
Script script = scriptClass.newInstance(); 81
Binding binding = new Binding(); 82
binding.setVariable("log", log); 83
binding.setVariable("properties", props); 84
script.setBinding(binding); 85
result = script.run(); 86
} 87
88
scope = "session"; 89
String scopes = props.getProperty("gracelets.scope"); 90
if (scopes != null) scope = scopes.toLowerCase(); 91
92
sname = props.getProperty("gracelets.name"); 93
if (sname == null) sname = "storage"; 94
95
if (result instanceof EntityManagerFactory) { 96
emf = (EntityManagerFactory) result; 97
this.autoclose = false; 98
} else { 99
String driver = props.getProperty("connection.driver"); 100
if (driver != null) Class.forName(driver.trim(), true, Thread.currentThread().getContextClassLoader()); 101
102
String url = props.getProperty("connection.url"); 103
if (url == null) throw new IllegalArgumentException("Must specify connection.url for jpa persistence."); 104
105
String username = props.getProperty("connection.username"); 106
String password = props.getProperty("connection.password"); 107
props.remove("connection.driver"); 108
props.remove("connection.url"); 109
props.remove("connection.username"); 110
props.remove("connection.password"); 111
112
String providerClass = props.getProperty("provider.class"); 113
if (providerClass == null) throw new IllegalArgumentException("Must specify provider.class for jpa persistence"); 114
115
PersistenceProvider provider = (PersistenceProvider) Class.forName(providerClass.trim(), true, Thread.currentThread().getContextClassLoader()).newInstance(); 116
PersistenceUnitInfo info = new UnitInfo(props, providerClass, sname, 117
new LocalDataSource(url, username, password), 118
new File(ctx.getServletContext().getRealPath("/WEB-INF/classes")) 119
); 120
121
emf = provider.createContainerEntityManagerFactory(info, props); 122
} 123
} 124
125
public void destroy() throws Exception { 126
if (this.autoclose) emf.close(); 127
} 128
129
public VariableContext generateVariableContext(FacesContext ctx) { 130
return null; 131
} 132
133
public void initializeBinding(GraceletBinding binding, Object owner) throws Exception {} 134
135
public void postInitialization(FacesContext ctx, GraceletsCompiler compiler) throws Exception { 136
VariableContext vctx = Gracelets.getGraceletsContext().getVariableContext(); 137
Gracelets.getGraceletsContext().getVariableContext().addFactory(sname, new BeanFactory(scope, this, sname)); 138
} 139
140
public Object generate(String id) { return this.getSession(); } 141
142
public JPAEql getSession () { return new JPAEql(emf.createEntityManager()); } 143
144
public Object resolve(String name) { 145
return "session".equals(name) ? this.getSession() : null; 146
} 147
148
public class LocalDataSource implements DataSource { 149
150
PrintWriter writer; 151
int loginTimeout = 10; 152
String url; 153
String username; 154
String password; 155
156
public LocalDataSource (String url, String user, String pass) { 157
this.url = url; 158
this.username = user; 159
this.password = pass; 160
} 161
162
public boolean isWrapperFor(Class<?> iface) throws SQLException { 163
return false; 164
} 165
166
public <T> T unwrap(Class<T> iface) throws SQLException { 167
return null; 168
} 169
170
public Connection getConnection() throws SQLException { 171
if (username == null || "".equals(username)) return DriverManager.getConnection(url); 172
else return this.getConnection(username, password); 173
} 174
175
public Connection getConnection(String username, String password) throws SQLException { 176
return DriverManager.getConnection(url, username, password); 177
} 178
179
public int getLoginTimeout() throws SQLException { 180
return this.loginTimeout; 181
} 182
183
public PrintWriter getLogWriter() throws SQLException { 184
return writer; 185
} 186
187
public void setLoginTimeout(int seconds) throws SQLException { 188
this.loginTimeout = seconds; 189
} 190
191
public void setLogWriter(PrintWriter out) throws SQLException { 192
this.writer = out; 193
} 194
195
} 196
197
public class UnitInfo implements PersistenceUnitInfo { 198
199
Properties props; 200
String ppCN; 201
String name; 202
URL rootURL; 203
DataSource dataSource; 204
List<URL> urls; 205
206
public UnitInfo (Properties props, String ppClassName, String name, DataSource dataSource, File root) { 207
try { 208
this.props = props; 209
this.name = name; 210
this.rootURL = root.toURL(); 211
this.dataSource = dataSource; 212
this.ppCN = ppClassName; 213
ComponentScanner scanner = new ComponentScanner(); 214
scanner.addPattern(Pattern.compile("^gracelets\\.jpa\\.properties$")); 215
List<gracelets.api.resource.ResourceScanner.ResourceInfo> resources = scanner.scan(); 216
this.urls = new ArrayList<URL>(); 217
for (gracelets.api.resource.ResourceScanner.ResourceInfo info : resources) { 218
this.urls.add(info.getRootResourceUrl()); 219
} 220
} catch (IOException e) { 221
throw new RuntimeException(e); 222
} 223
} 224
225
public void addTransformer(ClassTransformer transformer) { 226
log.warn("Adding class transformer: " + transformer); 227
} 228
229
public boolean excludeUnlistedClasses() { return false; } 230
231
public ClassLoader getClassLoader() { 232
return Thread.currentThread().getContextClassLoader(); 233
} 234
235
public List<URL> getJarFileUrls() { return this.urls; } 236
public DataSource getJtaDataSource() { return null; } 237
public List<String> getManagedClassNames() { return new ArrayList<String>(); } 238
public List<String> getMappingFileNames() { return new ArrayList<String>(); } 239
240
public ClassLoader getNewTempClassLoader() { 241
return new URLClassLoader(new URL[] { rootURL }, Thread.currentThread().getContextClassLoader()); 242
} 243
244
public DataSource getNonJtaDataSource() { return dataSource; } 245
public String getPersistenceProviderClassName() { return ppCN; } 246
public String getPersistenceUnitName() { return name; } 247
public URL getPersistenceUnitRootUrl() { return rootURL; } 248
public Properties getProperties() { return props; } 249
public PersistenceUnitTransactionType getTransactionType() { 250
return PersistenceUnitTransactionType.RESOURCE_LOCAL; 251
} 252
253
} 254
255
}
|