001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc2;
023:
024: import org.jboss.ejb.EntityEnterpriseContext;
025: import org.jboss.ejb.Container;
026: import org.jboss.ejb.EntityContainer;
027: import org.jboss.ejb.EjbModule;
028: import org.jboss.ejb.GenericEntityObjectFactory;
029: import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
030: import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
031: import org.jboss.ejb.plugins.cmp.jdbc.JDBCStartCommand;
032: import org.jboss.ejb.plugins.cmp.jdbc.JDBCStopCommand;
033: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
034: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
035: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCApplicationMetaData;
036: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCXmlFileLoader;
037: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityCommandMetaData;
038: import org.jboss.ejb.plugins.cmp.ejbql.Catalog;
039: import org.jboss.ejb.plugins.cmp.jdbc2.bridge.JDBCEntityBridge2;
040: import org.jboss.ejb.plugins.cmp.jdbc2.schema.Schema;
041: import org.jboss.ejb.plugins.cmp.jdbc2.schema.EntityTable;
042: import org.jboss.logging.Logger;
043: import org.jboss.deployment.DeploymentException;
044: import org.jboss.metadata.ApplicationMetaData;
045: import org.jboss.tm.TransactionLocal;
046:
047: import javax.ejb.DuplicateKeyException;
048: import javax.ejb.FinderException;
049: import javax.ejb.EJBException;
050: import javax.ejb.CreateException;
051: import javax.ejb.RemoveException;
052: import java.lang.reflect.Method;
053: import java.util.Collection;
054: import java.util.HashMap;
055: import java.util.ArrayList;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.Iterator;
059: import java.sql.SQLException;
060:
061: /**
062: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
063: * @version <tt>$Revision: 57209 $</tt>
064: */
065: public class JDBCStoreManager2 implements JDBCEntityPersistenceStore {
066: private static final String CATALOG = "CATALOG";
067: private static final String SCHEMA = "SCHEMA";
068: private static final String CREATED_MANAGERS = "CREATED_JDBCStoreManagers";
069: private static final String CMP_JDBC = "CMP-JDBC";
070:
071: private EntityContainer container;
072: private EjbModule ejbModule;
073: private Logger log;
074: private JDBCEntityMetaData metaData;
075: private JDBCEntityBridge2 entityBridge;
076: private JDBCTypeFactory typeFactory;
077: private Schema schema;
078:
079: private InstanceFactory instanceFactory;
080: private QueryFactory queryFactory;
081: private CreateCommand createCmd;
082: private JDBCStartCommand startCmd;
083: private JDBCStopCommand stop;
084:
085: private final TransactionLocal cascadeDeleteRegistry = new TransactionLocal() {
086: protected Object initialValue() {
087: return new HashMap();
088: }
089: };
090:
091: // Public
092:
093: public Schema getSchema() {
094: schema = (Schema) getApplicationData(SCHEMA);
095: if (schema == null) {
096: schema = new Schema();
097: putApplicationData(SCHEMA, schema);
098: }
099: return schema;
100: }
101:
102: public Catalog getCatalog() {
103: Catalog catalog = (Catalog) getApplicationData(CATALOG);
104: if (catalog == null) {
105: catalog = new Catalog();
106: putApplicationData(CATALOG, catalog);
107: }
108: return catalog;
109: }
110:
111: public QueryFactory getQueryFactory() {
112: return queryFactory;
113: }
114:
115: public boolean registerCascadeDelete(Object key, Object value) {
116: Map map = (Map) cascadeDeleteRegistry.get();
117: return map.put(key, value) == null;
118: }
119:
120: public boolean isCascadeDeleted(Object key) {
121: Map map = (Map) cascadeDeleteRegistry.get();
122: return map.containsKey(key);
123: }
124:
125: public void unregisterCascadeDelete(Object key) {
126: Map map = (Map) cascadeDeleteRegistry.get();
127: map.remove(key);
128: }
129:
130: // ContainerPlugin implementation
131:
132: public void setContainer(Container con) {
133: this .container = (EntityContainer) con;
134: if (container != null) {
135: ejbModule = container.getEjbModule();
136: log = Logger.getLogger(this .getClass().getName() + "."
137: + container.getBeanMetaData().getEjbName());
138: } else {
139: // undeploy
140: ejbModule = null;
141: }
142: }
143:
144: // Service implementation
145:
146: public void create() throws Exception {
147: HashMap managersMap = (HashMap) getApplicationData(CREATED_MANAGERS);
148: if (managersMap == null) {
149: managersMap = new HashMap();
150: putApplicationData(CREATED_MANAGERS, managersMap);
151: }
152: managersMap.put(container.getBeanMetaData().getEjbName(), this );
153: }
154:
155: public void start() throws Exception {
156: initStoreManager();
157:
158: HashMap managersMap = (HashMap) getApplicationData(CREATED_MANAGERS);
159: Catalog catalog = getCatalog();
160: if (catalog.getEntityCount() == managersMap.size()
161: && catalog.getEJBNames().equals(managersMap.keySet())) {
162: // Make a copy of the managers (for safty)
163: List managers = new ArrayList(managersMap.values());
164:
165: // Start Phase 2: resolve relationships
166: for (int i = 0; i < managers.size(); ++i) {
167: JDBCStoreManager2 manager = (JDBCStoreManager2) managers
168: .get(i);
169: manager.resolveRelationships();
170: }
171:
172: // Start Phase 3: init cmr loaders
173: for (int i = 0; i < managers.size(); ++i) {
174: JDBCStoreManager2 manager = (JDBCStoreManager2) managers
175: .get(i);
176: manager.startEntity();
177: }
178:
179: // Start Phase 4: create tables and compile queries
180: for (int i = 0; i < managers.size(); ++i) {
181: JDBCStoreManager2 manager = (JDBCStoreManager2) managers
182: .get(i);
183: manager.startStoreManager();
184: }
185:
186: // add foreign key constraints
187: for (int i = 0; i < managers.size(); ++i) {
188: JDBCStoreManager2 manager = (JDBCStoreManager2) managers
189: .get(i);
190: manager.startCmd.addForeignKeyConstraints();
191: }
192: }
193: }
194:
195: public void stop() {
196: if (stop != null) {
197: Map managersMap = (HashMap) getApplicationData(CREATED_MANAGERS);
198: while (!managersMap.isEmpty()) {
199: int stoppedInIteration = 0;
200: for (Iterator i = managersMap.values().iterator(); i
201: .hasNext();) {
202: JDBCStoreManager2 manager = (JDBCStoreManager2) i
203: .next();
204: if (manager.stop.execute()) {
205: i.remove();
206: try {
207: manager.entityBridge.stop();
208: } catch (Exception e) {
209: log.error("Failed to stop entity bridge.",
210: e);
211: }
212: ++stoppedInIteration;
213: }
214: }
215:
216: if (stoppedInIteration == 0) {
217: break;
218: }
219: }
220: }
221: }
222:
223: public void destroy() {
224: //To change body of implemented methods use File | Settings | File Templates.
225: }
226:
227: // JDBCEntityPersistenceStore implementation
228:
229: public JDBCAbstractEntityBridge getEntityBridge() {
230: return entityBridge;
231: }
232:
233: public JDBCEntityMetaData getMetaData() {
234: return metaData;
235: }
236:
237: public JDBCTypeFactory getJDBCTypeFactory() {
238: return typeFactory;
239: }
240:
241: public EntityContainer getContainer() {
242: return container;
243: }
244:
245: public Object getApplicationData(Object key) {
246: return ejbModule.getModuleData(key);
247: }
248:
249: public void putApplicationData(Object key, Object value) {
250: ejbModule.putModuleData(key, value);
251: }
252:
253: // EntityPersistenceStore implementation
254:
255: public Object createBeanClassInstance() throws Exception {
256: return instanceFactory.newInstance();
257: }
258:
259: public void initEntity(EntityEnterpriseContext ctx) {
260: entityBridge.initPersistenceContext(ctx);
261: entityBridge.initInstance(ctx);
262: }
263:
264: public Object createEntity(Method m, Object[] args,
265: EntityEnterpriseContext ctx) throws CreateException {
266: /*
267: Object pk;
268: PersistentContext pctx = (PersistentContext) ctx.getPersistenceContext();
269: if(ctx.getId() == null)
270: {
271: pk = entityBridge.extractPrimaryKeyFromInstance(ctx);
272:
273: if(pk == null)
274: {
275: throw new CreateException("Primary key for created instance is null.");
276: }
277:
278: pctx.setPk(pk);
279: }
280: else
281: {
282: // insert-after-ejb-post-create
283: try
284: {
285: pctx.flush();
286: }
287: catch(SQLException e)
288: {
289: if("23000".equals(e.getSQLState()))
290: {
291: throw new DuplicateKeyException("Unique key violation or invalid foreign key value: pk=" + ctx.getId());
292: }
293: else
294: {
295: throw new CreateException("Failed to create instance: pk=" + ctx.getId() +
296: ", state=" + e.getSQLState() +
297: ", msg=" + e.getMessage());
298: }
299: }
300: pk = ctx.getId();
301: }
302: return pk;
303: */
304: return createCmd.execute(m, args, ctx);
305: }
306:
307: public Object postCreateEntity(Method m, Object[] args,
308: EntityEnterpriseContext ctx) throws CreateException {
309: return null;
310: }
311:
312: public Object findEntity(Method finderMethod, Object[] args,
313: EntityEnterpriseContext instance,
314: GenericEntityObjectFactory factory) throws FinderException {
315: QueryCommand query = queryFactory.getQueryCommand(finderMethod);
316: return query.fetchOne(schema, factory, args);
317: }
318:
319: public Collection findEntities(Method finderMethod, Object[] args,
320: EntityEnterpriseContext instance,
321: GenericEntityObjectFactory factory) throws FinderException {
322: QueryCommand query = queryFactory.getQueryCommand(finderMethod);
323: return query.fetchCollection(schema, factory, args);
324: }
325:
326: public void activateEntity(EntityEnterpriseContext ctx) {
327: entityBridge.initPersistenceContext(ctx);
328: }
329:
330: public void loadEntity(EntityEnterpriseContext ctx) {
331: try {
332: EntityTable.Row row = entityBridge.getTable().loadRow(
333: ctx.getId());
334: PersistentContext pctx = new PersistentContext(
335: entityBridge, row);
336: ctx.setPersistenceContext(pctx);
337: } catch (EJBException e) {
338: log.error("Failed to load instance of "
339: + entityBridge.getEntityName() + " with pk="
340: + ctx.getId(), e);
341: throw e;
342: } catch (Exception e) {
343: throw new EJBException("Failed to load instance of "
344: + entityBridge.getEntityName() + " with pk="
345: + ctx.getId(), e);
346: }
347: }
348:
349: public boolean isStoreRequired(EntityEnterpriseContext instance) {
350: return entityBridge.isStoreRequired(instance);
351: }
352:
353: public boolean isModified(EntityEnterpriseContext instance)
354: throws Exception {
355: return entityBridge.isModified(instance);
356: }
357:
358: public void storeEntity(EntityEnterpriseContext instance) {
359: // scary?
360: }
361:
362: public void passivateEntity(EntityEnterpriseContext ctx) {
363: JDBCEntityBridge2.destroyPersistenceContext(ctx);
364: }
365:
366: public void removeEntity(EntityEnterpriseContext ctx)
367: throws RemoveException {
368: entityBridge.remove(ctx);
369: PersistentContext pctx = (PersistentContext) ctx
370: .getPersistenceContext();
371: pctx.remove();
372: }
373:
374: // Private
375:
376: protected void initStoreManager() throws Exception {
377: if (log.isDebugEnabled()) {
378: log.debug("Initializing CMP plugin for "
379: + container.getBeanMetaData().getEjbName());
380: }
381:
382: metaData = loadJDBCEntityMetaData();
383:
384: // setup the type factory, which is used to map java types to sql types.
385: typeFactory = new JDBCTypeFactory(metaData.getTypeMapping(),
386: metaData.getJDBCApplication().getValueClasses(),
387: metaData.getJDBCApplication().getUserTypeMappings());
388:
389: entityBridge = new JDBCEntityBridge2(this , metaData);
390: entityBridge.init();
391:
392: Catalog catalog = getCatalog();
393: catalog.addEntity(entityBridge);
394:
395: stop = new JDBCStopCommand(this );
396: }
397:
398: private void resolveRelationships() throws Exception {
399: entityBridge.resolveRelationships();
400: }
401:
402: protected void startStoreManager() throws Exception {
403: entityBridge.start();
404:
405: queryFactory = new QueryFactory(entityBridge);
406: queryFactory.init();
407:
408: instanceFactory = new InstanceFactory(this , entityBridge);
409:
410: startCmd = new JDBCStartCommand(this );
411: startCmd.execute();
412:
413: final JDBCEntityCommandMetaData entityCommand = getMetaData()
414: .getEntityCommand();
415: if (entityCommand == null
416: || "default".equals(entityCommand.getCommandName())) {
417: createCmd = new ApplicationPkCreateCommand();
418: } else {
419: final Class cmdClass = entityCommand.getCommandClass();
420: if (cmdClass == null) {
421: throw new DeploymentException(
422: "entity-command class name is not specified for entity "
423: + entityBridge.getEntityName());
424: }
425:
426: try {
427: createCmd = (CreateCommand) cmdClass.newInstance();
428: } catch (ClassCastException cce) {
429: throw new DeploymentException("Entity command "
430: + cmdClass + " does not implement "
431: + CreateCommand.class);
432: }
433: }
434:
435: createCmd.init(this );
436: }
437:
438: private void startEntity() throws DeploymentException {
439: entityBridge.start();
440: }
441:
442: private JDBCEntityMetaData loadJDBCEntityMetaData()
443: throws DeploymentException {
444: ApplicationMetaData amd = container.getBeanMetaData()
445: .getApplicationMetaData();
446:
447: // Get JDBC MetaData
448: JDBCApplicationMetaData jamd = (JDBCApplicationMetaData) amd
449: .getPluginData(CMP_JDBC);
450:
451: if (jamd == null) {
452: // we are the first cmp entity to need jbosscmp-jdbc.
453: // Load jbosscmp-jdbc.xml for the whole application
454: JDBCXmlFileLoader jfl = new JDBCXmlFileLoader(amd,
455: container.getClassLoader(), container
456: .getLocalClassLoader(), log);
457:
458: jamd = jfl.load();
459: amd.addPluginData(CMP_JDBC, jamd);
460: }
461:
462: // Get JDBC Bean MetaData
463: String ejbName = container.getBeanMetaData().getEjbName();
464: JDBCEntityMetaData metadata = jamd.getBeanByEjbName(ejbName);
465: if (metadata == null) {
466: throw new DeploymentException("No metadata found for bean "
467: + ejbName);
468: }
469: return metadata;
470: }
471: }
|