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.LinkedHashMap; 16
import java.util.List; 17
import java.util.Map; 18
import java.util.Properties; 19
import java.util.Set; 20
import java.util.regex.Pattern; 21
22
import javax.faces.context.FacesContext; 23
import javax.persistence.EntityManagerFactory; 24
import javax.persistence.spi.ClassTransformer; 25
import javax.persistence.spi.PersistenceProvider; 26
import javax.persistence.spi.PersistenceUnitInfo; 27
import javax.persistence.spi.PersistenceUnitTransactionType; 28
import javax.servlet.ServletContext; 29
import javax.sql.DataSource; 30
31
import org.slf4j.Logger; 32
import org.slf4j.LoggerFactory; 33
34
import gracelets.api.Gracelets; 35
import gracelets.api.binding.GraceletBinding; 36
import gracelets.api.context.Scope; 37
import gracelets.api.context.VariableContext; 38
import gracelets.api.context.factory.BeanFactory; 39
import gracelets.api.context.factory.BeanGeneratorFactory; 40
import gracelets.api.context.factory.ClosureFactory; 41
import gracelets.api.context.factory.ELFactory; 42
import gracelets.api.extension.GraceletsExtension; 43
import gracelets.api.extension.GraceletsExtensionInitializeContext; 44
import gracelets.api.facelets.GraceletsCompiler; 45
import gracelets.api.resource.ComponentScanner; 46
import gracelets.api.resource.ResourceInfo; 47
import gracelets.util.GeneralUtil; 48
import groovy.lang.Binding; 49
import groovy.lang.Script; 50
51
52
@author 53
@version 54
55
public class JPAExtension implements GraceletsExtension, BeanGeneratorFactory<Object> { 56
57
Logger log = LoggerFactory.getLogger(JPAExtension.class); 58
59
EntityManagerFactory emf; 60
String scope; 61
String sname; 62
boolean autoclose = true; 63
64
public void initialize(GraceletsExtensionInitializeContext ctx) throws Exception { 65
URL gc = null; 66
URL hc = ctx.getServletContext().getResource("/WEB-INF/classes/persistence.properties"); 67
if (hc == null) { 68
gc = ctx.getServletContext().getResource("/WEB-INF/classes/persistence.groovy"); 69
70
if (gc == null) { 71
log.warn("No persistence.properties detected"); 72
return; 73
} 74
} 75
76
Object result = null; 77
Properties props = new Properties(); 78
Map<String, Object> properties = new LinkedHashMap<String, Object>(); 79
80
if (hc != null) { 81
props.load(hc.openStream()); 82
Enumeration names = props.propertyNames(); 83
while (names.hasMoreElements()) { 84
String name = String.valueOf(names.nextElement()); 85
properties.put(name, props.getProperty(name)); 86
} 87
} 88
else { 89
Class<Script> scriptClass = Gracelets.getClassLoader().parseClass(gc.openStream()); 90
Script script = scriptClass.newInstance(); 91
Binding binding = new Binding(); 92
binding.setVariable("log", log); 93
binding.setVariable("properties", properties); 94
script.setBinding(binding); 95
result = script.run(); 96
} 97
98
scope = "session"; 99
String scopes = (String) properties.get("gracelets.scope"); 100
if (scopes != null) scope = scopes.toLowerCase(); 101
102
sname = (String) properties.get("gracelets.name"); 103
if (sname == null) sname = "storage"; 104
105
if (result instanceof EntityManagerFactory) { 106
emf = (EntityManagerFactory) result; 107
this.autoclose = false; 108
} else { 109
DataSource dataSource = null; 110
111
Object datasource = properties.get("connection.datasource"); 112
if (datasource != null && datasource instanceof DataSource) { 113
dataSource = (DataSource) datasource; 114
} else { 115
String driver = (String) properties.get("connection.driver"); 116
if (driver != null) Class.forName(driver.trim(), true, Thread.currentThread().getContextClassLoader()); 117
118
String url = (String) properties.get("connection.url"); 119
if (url == null) throw new IllegalArgumentException("Must specify connection.url for jpa persistence."); 120
121
String username = (String) properties.get("connection.username"); 122
String password = (String) properties.get("connection.password"); 123
properties.remove("connection.driver"); 124
properties.remove("connection.url"); 125
properties.remove("connection.username"); 126
properties.remove("connection.password"); 127
128
dataSource = new LocalDataSource(url, username, password); 129
} 130
131
String providerClass = (String) properties.get("provider.class"); 132
if (providerClass == null) throw new IllegalArgumentException("Must specify provider.class for jpa persistence"); 133
134
Properties unitProperties = new Properties(); 135
for (String key : properties.keySet()) { 136
unitProperties.setProperty(key, String.valueOf( properties.get(key) )); 137
} 138
139
PersistenceProvider provider = (PersistenceProvider) Class.forName(providerClass.trim(), true, Thread.currentThread().getContextClassLoader()).newInstance(); 140
PersistenceUnitInfo info = new UnitInfo(unitProperties, providerClass, sname, 141
dataSource, new File(ctx.getServletContext().getRealPath("/WEB-INF/classes")) 142
); 143
144
emf = provider.createContainerEntityManagerFactory(info, unitProperties); 145
} 146
} 147
148
public void destroy() throws Exception { 149
if (this.autoclose) emf.close(); 150
} 151
152
public VariableContext generateVariableContext(FacesContext ctx) { 153
return null; 154
} 155
156
public void initializeBinding(GraceletBinding binding, Object owner) throws Exception {} 157
158
public void postInitialization(FacesContext ctx, GraceletsCompiler compiler) throws Exception { 159
VariableContext vctx = Gracelets.getGraceletsContext().getVariableContext(); 160
Gracelets.getGraceletsContext().getVariableContext().addFactory(sname, new BeanFactory(scope, this, sname)); 161
} 162
163
public Object generate(String id) { return this.getSession(); } 164
165
public JPAEql getSession () { return new JPAEql(emf.createEntityManager()); } 166
167
public Object resolve(String name) { 168
return "session".equals(name) ? this.getSession() : null; 169
} 170
171
public class LocalDataSource implements DataSource { 172
173
PrintWriter writer; 174
int loginTimeout = 10; 175
String url; 176
String username; 177
String password; 178
179
public LocalDataSource (String url, String user, String pass) { 180
this.url = url; 181
this.username = user; 182
this.password = pass; 183
} 184
185
public boolean isWrapperFor(Class<?> iface) throws SQLException { 186
return false; 187
} 188
189
public <T> T unwrap(Class<T> iface) throws SQLException { 190
return null; 191
} 192
193
public Connection getConnection() throws SQLException { 194
if (username == null || "".equals(username)) return DriverManager.getConnection(url); 195
else return this.getConnection(username, password); 196
} 197
198
public Connection getConnection(String username, String password) throws SQLException { 199
return DriverManager.getConnection(url, username, password); 200
} 201
202
public int getLoginTimeout() throws SQLException { 203
return this.loginTimeout; 204
} 205
206
public PrintWriter getLogWriter() throws SQLException { 207
return writer; 208
} 209
210
public void setLoginTimeout(int seconds) throws SQLException { 211
this.loginTimeout = seconds; 212
} 213
214
public void setLogWriter(PrintWriter out) throws SQLException { 215
this.writer = out; 216
} 217
218
} 219
220
public class UnitInfo implements PersistenceUnitInfo { 221
222
Properties props; 223
String ppCN; 224
String name; 225
URL rootURL; 226
DataSource dataSource; 227
List<URL> urls; 228
229
public UnitInfo (Properties props, String ppClassName, String name, DataSource dataSource, File root) { 230
try { 231
this.props = props; 232
this.name = name; 233
this.rootURL = root.toURL(); 234
this.dataSource = dataSource; 235
this.ppCN = ppClassName; 236
ComponentScanner scanner = new ComponentScanner(); 237
scanner.addPattern(Pattern.compile("^gracelets\\.jpa\\.properties$")); 238
List<gracelets.api.resource.ResourceScanner.ResourceInfo> resources = scanner.scan(); 239
this.urls = new ArrayList<URL>(); 240
for (gracelets.api.resource.ResourceScanner.ResourceInfo info : resources) { 241
this.urls.add(info.getRootResourceUrl()); 242
} 243
} catch (IOException e) { 244
throw new RuntimeException(e); 245
} 246
} 247
248
public void addTransformer(ClassTransformer transformer) { 249
log.warn("Adding class transformer: " + transformer); 250
} 251
252
public boolean excludeUnlistedClasses() { return false; } 253
254
public ClassLoader getClassLoader() { 255
return Thread.currentThread().getContextClassLoader(); 256
} 257
258
public List<URL> getJarFileUrls() { return this.urls; } 259
public DataSource getJtaDataSource() { return null; } 260
public List<String> getManagedClassNames() { return new ArrayList<String>(); } 261
public List<String> getMappingFileNames() { return new ArrayList<String>(); } 262
263
public ClassLoader getNewTempClassLoader() { 264
return new URLClassLoader(new URL[] { rootURL }, Thread.currentThread().getContextClassLoader()); 265
} 266
267
public DataSource getNonJtaDataSource() { return dataSource; } 268
public String getPersistenceProviderClassName() { return ppCN; } 269
public String getPersistenceUnitName() { return name; } 270
public URL getPersistenceUnitRootUrl() { return rootURL; } 271
public Properties getProperties() { return props; } 272
public PersistenceUnitTransactionType getTransactionType() { 273
return PersistenceUnitTransactionType.RESOURCE_LOCAL; 274
} 275
276
} 277
278
}
|