2
0
Fork 0

Refactored internal api

This commit is contained in:
Willem Cazander 2012-06-04 22:49:12 +02:00
parent 3f31bb8a3a
commit 6ccd763d1f
361 changed files with 23049 additions and 4498 deletions

View file

@ -61,9 +61,6 @@ public class JdbcConnectionProviderJdniImpl implements JdbcConnectionProvider {
try {
Context initialContext = new InitialContext();
if ( initialContext == null ) {
throw new SQLException("Cannot get Initial Context");
}
DataSource datasource = (DataSource)initialContext.lookup(dataSourceContext+name);
if ( datasource == null ) {
throw new SQLException("Cannot lookup datasource: "+name);

View file

@ -51,10 +51,19 @@ public class JdbcVascBackend extends AbstractVascBackend {
private JdbcConnectionProvider jdbcConnectionProvider = null;
private String sqlList = null;
private String idColumn = null;
private String sqlDelete = null;
private String sqlUpdate = null;
private String sqlInsert = null;
//private String idColumn = null;
//private String sqlDelete = null;
//private String sqlUpdate = null;
//private String sqlInsert = null;
/**
* @see net.forwardfire.vasc.backend.AbstractVascBackend#isReadOnly()
*/
@Override
public boolean isReadOnly() {
return true;
}
/**
* @return the JdbcConnectionProvider

View file

@ -54,6 +54,14 @@ public class JdbcVascBackendXpql extends AbstractVascBackend {
private net.forwardfire.vasc.xpql.query.Query query = null;
/**
* @see net.forwardfire.vasc.backend.AbstractVascBackend#isReadOnly()
*/
@Override
public boolean isReadOnly() {
return true;
}
/**
* @return the JdbcConnectionProvider
*/

View file

@ -19,13 +19,13 @@
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<version>${persistence-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.4.0.GA</version>
<version>${hibernate-annotations.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

View file

@ -18,7 +18,19 @@
<dependency>
<groupId>org.eobjects.metamodel</groupId>
<artifactId>MetaModel-full</artifactId>
<version>3.0-SNAPSHOT</version>
<version>${metamodel.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,70 @@
/*
* Copyright 2007-2012 forwardfire.net All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.forwardfire.vasc.backend.metamodel;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.eobjects.metamodel.DataContext;
/**
* MetaModelDataContextJndiDataContext provides a data context from jndi.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class MetaModelDataContextJndiDataContext implements MetaModelDataContextProvider {
private String jndiName = null;
public DataContext getDataContext() {
return getDataContextJndi();
}
private DataContext getDataContextJndi() {
try {
InitialContext context = new InitialContext();
DataContext dataContext = (DataContext)context.lookup(jndiName);
if (dataContext == null) {
throw new NullPointerException("Cannot lookup dataContext: "+jndiName);
}
return dataContext;
} catch (NamingException e) {
throw new IllegalStateException("Naming error:"+e.getMessage(),e);
}
}
/**
* @return the jndiName
*/
public String getJndiName() {
return jndiName;
}
/**
* @param jndiName the jndiName to set
*/
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2007-2012 forwardfire.net All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.forwardfire.vasc.backend.metamodel;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.jdbc.JdbcDataContext;
/**
* MetaModelDataContextJndiDataSource provides a connection from datasource for meta model data context.
*
* @author Willem Cazander
* @version 1.0 May 15, 2012
*/
public class MetaModelDataContextJndiDataSource implements MetaModelDataContextProvider {
private String jndiName = null;
public DataContext getDataContext() {
JdbcDataContext dataContext = new JdbcDataContext(getDataSource());
return dataContext;
}
private DataSource getDataSource() {
try {
InitialContext context = new InitialContext();
DataSource datasource = (DataSource)context.lookup(jndiName);
if (datasource == null) {
throw new NullPointerException("Cannot lookup datasource: "+jndiName);
}
return datasource;
} catch (NamingException e) {
throw new IllegalStateException("Naming error:"+e.getMessage(),e);
}
}
/**
* @return the jndiName
*/
public String getJndiName() {
return jndiName;
}
/**
* @param jndiName the jndiName to set
*/
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}

View file

@ -26,6 +26,7 @@ import java.net.UnknownHostException;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.mongodb.MongoDbDataContext;
import org.eobjects.metamodel.mongodb.MongoDbDataContextBean;
import com.mongodb.DB;
import com.mongodb.Mongo;
@ -48,10 +49,15 @@ public class MetaModelDataContextMongodb implements MetaModelDataContextProvider
private boolean readonly = false;
protected Mongo mongo = null;
//protected DB db = null;
public DataContext getDataContext() {
MongoDbDataContext dataContext = new MongoDbDataContext(getMongodbConnection());
MongoDbDataContextBean dataContext = new MongoDbDataContextBean(getMongodbConnection());
dataContext.setRegisterMBean(true);
dataContext.start();
//MongoDbDataContextSchemaDetector detector = new MongoDbDataContextSchemaDetector();
//detector.setDataCheckSize(10);
//detector.setSearchReference(true);
//DataContext dataContext = detector.new MongoDbDataContextExtended(getMongodbConnection(),detector);
return dataContext;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2007-2012 forwardfire.net All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.forwardfire.vasc.backend.metamodel;
import java.io.File;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.xml.XmlDomDataContext;
/**
* MetaModelDataContextXmlDom provides basic auto xml editing support.
*
* @author Willem Cazander
* @version 1.0 May 20, 2012
*/
public class MetaModelDataContextXmlDom implements MetaModelDataContextProvider {
private String file = null;
private boolean autoFlattenTables = true;
public DataContext getDataContext() {
if (file==null) {
throw new NullPointerException("Can's provided data context with null file.");
}
XmlDomDataContext dataContext = new XmlDomDataContext(new File(file),autoFlattenTables);
return dataContext;
}
/**
* @return the file
*/
public String getFile() {
return file;
}
/**
* @param file the file to set
*/
public void setFile(String file) {
this.file = file;
}
/**
* @return the autoFlattenTables
*/
public boolean isAutoFlattenTables() {
return autoFlattenTables;
}
/**
* @param autoFlattenTables the autoFlattenTables to set
*/
public void setAutoFlattenTables(boolean autoFlattenTables) {
this.autoFlattenTables = autoFlattenTables;
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright 2007-2012 forwardfire.net All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.forwardfire.vasc.backend.metamodel;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.xml.XmlSaxDataContext;
import org.eobjects.metamodel.xml.XmlSaxTableDef;
/**
* MetaModelDataContextXml provides basic xml editing support.
*
* @author Willem Cazander
* @version 1.0 May 3, 2012
*/
public class MetaModelDataContextXmlSax implements MetaModelDataContextProvider {
private String file = null;
private List<XmlSaxTableDef> tableSchemas = null;
public MetaModelDataContextXmlSax() {
tableSchemas = new ArrayList<XmlSaxTableDef>(5);
}
public DataContext getDataContext() {
if (file==null) {
throw new NullPointerException("Can's provided data context with null file.");
}
if (tableSchemas.isEmpty()) {
throw new IllegalStateException("Can't provided data context with zero table schemas.");
}
XmlSaxTableDef[] args = new XmlSaxTableDef[tableSchemas.size()];
XmlSaxDataContext dataContext = new XmlSaxDataContext(new File(file),tableSchemas.toArray(args));
return dataContext;
}
public void addTableSchema(XmlSaxTableDef tableSchema) {
tableSchemas.add(tableSchema);
}
public void removeTableSchema(XmlSaxTableDef tableSchema) {
tableSchemas.remove(tableSchema);
}
public List<XmlSaxTableDef> getTableSchemas() {
return tableSchemas;
}
public void setFile(String file) {
this.file=file;
}
public String getFile() {
return file;
}
}

View file

@ -33,14 +33,13 @@ import net.forwardfire.vasc.backend.VascBackendControllerLocal;
import net.forwardfire.vasc.core.VascController;
import net.forwardfire.vasc.core.VascEntry;
import net.forwardfire.vasc.core.VascEntryControllerLocal;
import net.forwardfire.vasc.core.VascEntryField;
import net.forwardfire.vasc.core.VascLinkEntry;
import net.forwardfire.vasc.core.VascLinkEntryType;
import net.forwardfire.vasc.core.VascEntryFieldLocal;
import net.forwardfire.vasc.core.VascEntryLinkType;
import net.forwardfire.vasc.core.VascEntryLocal;
import net.forwardfire.vasc.impl.DefaultVascEntry;
import net.forwardfire.vasc.impl.DefaultVascEntryField;
import net.forwardfire.vasc.impl.DefaultVascFactory;
import net.forwardfire.vasc.impl.DefaultVascLinkEntry;
import net.forwardfire.vasc.impl.DefaultVascEntryLink;
import net.forwardfire.vasc.impl.ui.VascSelectItemModelEntry;
/**
@ -74,11 +73,11 @@ public class MetaModelSchemaAutoEntry {
DataContext ds = getDataContextProvider().getDataContext();
for (String table:ds.getDefaultSchema().getTableNames()) {
if (getTableInclude()!=null && table.matches(getTableInclude())==false) {
logger.fine("Excluding table: "+table+" from include rule.");
logger.finer("Excluding table: "+table+" from include rule.");
continue;
}
if (getTableExclude()!=null && table.matches(getTableExclude())) {
logger.fine("Excluding table: "+table+" from exclude rule.");
logger.finer("Excluding table: "+table+" from exclude rule.");
continue;
}
createMetaEntry(ds,getEntryPrefix()+"_"+table,table);
@ -90,7 +89,7 @@ public class MetaModelSchemaAutoEntry {
}
private void createMetaEntry(DataContext ds,String id,String tableName) {
logger.info("Creating entry id: "+id+" of table: "+tableName);
logger.fine("Creating entry id: "+id+" of table: "+tableName);
Table metaTable = null;
if (tableName==null) {
metaTable = ds.getDefaultSchema().getTable(0);
@ -114,7 +113,7 @@ public class MetaModelSchemaAutoEntry {
//TODO backend.setRequestReadOnly(true);
}
VascEntry ve = new DefaultVascEntry();
DefaultVascEntry ve = new DefaultVascEntry();
ve.setId(id);
ve.setBackendId(id+"_backend");
ve.setPrimaryKeyFieldId(backend.getTableId());
@ -123,10 +122,10 @@ public class MetaModelSchemaAutoEntry {
for (Relationship rs:metaTable.getRelationships()) {
logger.finer("Found relation FT: "+rs.getForeignTable().getName()+" PT: "+rs.getPrimaryTable().getName());
if (tableName.equals(rs.getForeignTable().getName())==false) {
logger.info("Creating Link entry for: "+rs.getForeignColumns()[0].getName()+" of table: "+rs.getForeignTable().getName());
logger.finer("Creating Link entry for: "+rs.getForeignColumns()[0].getName()+" of table: "+rs.getForeignTable().getName());
createLinkEntry(rs,ve,metaTable,id+"_"+rs.getForeignTable().getName()+"_"+rs.getForeignColumns()[0].getName()+"_link");
} else {
logger.info("Creating List entry for: "+rs.getPrimaryColumns()[0].getName()+" of table: "+rs.getPrimaryTable().getName());
logger.finer("Creating List entry for: "+rs.getPrimaryColumns()[0].getName()+" of table: "+rs.getPrimaryTable().getName());
createListEntry(rs,ve,metaTable,id+"_"+rs.getPrimaryTable().getName()+"_"+rs.getPrimaryColumns()[0].getName()+"_list");
}
}
@ -134,15 +133,12 @@ public class MetaModelSchemaAutoEntry {
try {
((VascBackendControllerLocal)getVascController().getVascBackendController()).addVascBackend(backend);
((VascEntryControllerLocal)getVascController().getVascEntryController()).addVascEntry(ve);
// mm TODO rm this fill which adds the global actions ... and show updated tree in editor
DefaultVascFactory.fillVascControllerLocalEntries((VascEntryControllerLocal) getVascController().getVascEntryController(), getVascController());
} catch (Exception ee) {
ee.printStackTrace();
}
}
private void createLinkEntry(Relationship rs2,VascEntry ve,Table metaTable,String id) {
private void createLinkEntry(Relationship rs2,VascEntryLocal ve,Table metaTable,String id) {
MetaModelVascBackend backendLink = new MetaModelVascBackend();
backendLink.setId(id+"_backend");
backendLink.setDataContextProvider(getDataContextProvider());
@ -159,7 +155,7 @@ public class MetaModelSchemaAutoEntry {
backendLink.setTableId(cols[0].getName());
}
VascEntry veLink = new DefaultVascEntry();
DefaultVascEntry veLink = new DefaultVascEntry();
veLink.setId(id);
veLink.setBackendId(id+"_backend");
veLink.setPrimaryKeyFieldId(backendLink.getTableId());
@ -171,7 +167,7 @@ public class MetaModelSchemaAutoEntry {
//logger.info("Creating Link entry for: "+rs.getForeignColumns()[0].getName()+" of table: "+rs.getForeignTable().getName());
//createLinkEntry(rs,ve,rs2.getForeignTable(),id+"_"+rs.getForeignTable().getName()+"_"+rs.getForeignColumns()[0].getName()+"_link");
} else {
logger.info("Creating List entry for: "+rs.getPrimaryColumns()[0].getName()+" of table: "+rs.getPrimaryTable().getName());
logger.fine("Creating List entry for: "+rs.getPrimaryColumns()[0].getName()+" of table: "+rs.getPrimaryTable().getName());
createListEntry(rs,veLink,rs2.getForeignTable(),id+"_"+rs.getPrimaryTable().getName()+"_"+rs.getPrimaryColumns()[0].getName()+"_list");
}
}
@ -180,13 +176,13 @@ public class MetaModelSchemaAutoEntry {
((VascBackendControllerLocal)getVascController().getVascBackendController()).addVascBackend(backendLink);
((VascEntryControllerLocal)getVascController().getVascEntryController()).addVascEntry(veLink);
VascLinkEntry vle = new DefaultVascLinkEntry();
DefaultVascEntryLink vle = new DefaultVascEntryLink();
vle.setId(id+"Link");
vle.setVascLinkEntryType(VascLinkEntryType.DEFAULT_TYPE);
vle.setVascLinkEntryType(VascEntryLinkType.DEFAULT_TYPE);
vle.setVascEntryId(id);
vle.addEntryParameterFieldId(rs2.getForeignColumns()[0].getName(), rs2.getPrimaryColumns()[0].getName());
ve.addVascLinkEntry(vle);
ve.addVascEntryLink(vle);
} catch (Exception e) {
e.printStackTrace();
@ -209,13 +205,13 @@ public class MetaModelSchemaAutoEntry {
} else {
backendLink.setTableId(cols[0].getName());
}
VascEntry veLink = new DefaultVascEntry();
DefaultVascEntry veLink = new DefaultVascEntry();
veLink.setId(id);
veLink.setBackendId(id+"_backend");
veLink.setPrimaryKeyFieldId(backendLink.getTableId());
createFields(veLink,cols);
try {
VascEntryField vef = ve.getVascEntryFieldById(rs.getForeignColumns()[0].getName());
VascEntryFieldLocal vef = (VascEntryFieldLocal)ve.getVascEntryFieldById(rs.getForeignColumns()[0].getName());
if (vef==null) {
logger.warning("Could not find: "+rs.getForeignColumns()[0].getName()+" in ve: "+ve.getId());
return;
@ -234,9 +230,10 @@ public class MetaModelSchemaAutoEntry {
}
}
private void createFields(VascEntry ve,Column[] cols) {
private void createFields(VascEntryLocal ve,Column[] cols) {
for (Column c:cols) {
VascEntryField vef = new DefaultVascEntryField(c.getName());
DefaultVascEntryField vef = new DefaultVascEntryField();
vef.setId(c.getName());
vef.setVascEntryFieldType(getVascController().getVascEntryFieldTypeController().getVascEntryFieldTypeByClass(c.getType().getJavaEquivalentClass()));
if (vef.getId().equals(ve.getPrimaryKeyFieldId())) {

View file

@ -24,29 +24,27 @@ package net.forwardfire.vasc.backend.metamodel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.eobjects.metamodel.UpdateCallback;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.insert.RowInsertionBuilder;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.query.builder.SatisfiedQueryBuilder;
import org.eobjects.metamodel.query.builder.SatisfiedWhereBuilder;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.update.RowUpdationBuilder;
import net.forwardfire.vasc.backend.AbstractVascBackend;
import net.forwardfire.vasc.backend.VascBackendState;
import net.forwardfire.vasc.backend.data.MapVascEntryFieldValue;
import net.forwardfire.vasc.backend.data.MapVascEntryRecordCreator;
import net.forwardfire.vasc.backend.metamodel.crud.CrudDataContext;
import net.forwardfire.vasc.backend.metamodel.crud.UpdateableRow;
import net.forwardfire.vasc.backend.metamodel.crud.CrudDataContextImpl;
import net.forwardfire.vasc.backend.metamodel.crud.UpdateableRowMapImpl;
import net.forwardfire.vasc.core.VascEntry;
import net.forwardfire.vasc.core.VascEntryField;
import net.forwardfire.vasc.core.VascException;
@ -61,19 +59,55 @@ import net.forwardfire.vasc.core.entry.VascEntryRecordCreator;
*/
public class MetaModelVascBackend extends AbstractVascBackend {
private Logger logger = Logger.getLogger(MetaModelVascBackend.class.getName());
private MetaModelDataContextProvider dataContextProvider = null;
private UpdateableDataContext dataContext = null;
private DataContext dataContext = null;
private CrudDataContext crudDataContext = null;
private String table = null;
private String tableId = null;
/**
* Returns casted version of data context.
*/
private UpdateableDataContext getUpdateableDataContext() {
return (UpdateableDataContext)dataContext;
}
/**
* Returns true when data context in updateable.
*/
private boolean isUpdateableDataContext() {
return dataContext instanceof UpdateableDataContext;
}
/**
* @see net.forwardfire.vasc.backend.AbstractVascBackend#isReadOnly()
*/
@Override
public boolean isReadOnly() {
return isUpdateableDataContext()==false;
}
/**
* @see net.forwardfire.vasc.backend.AbstractVascBackend#startBackend()
*/
@Override
public void startBackend() {
dataContext = (UpdateableDataContext)dataContextProvider.getDataContext();
if (table==null) {
throw new NullPointerException("Can't start with null table.");
}
if (table.isEmpty()) {
throw new NullPointerException("Can't start with empty table.");
}
long startTime = System.currentTimeMillis();
dataContext = dataContextProvider.getDataContext();
if (isUpdateableDataContext()) {
crudDataContext = new CrudDataContextImpl(getUpdateableDataContext());
}
long stopTime = System.currentTimeMillis();
logger.info(dataContext.getClass().getSimpleName()+" created for: "+table+" in: "+(stopTime-startTime)+" ms.");
}
/**
* @see net.forwardfire.vasc.backend.AbstractVascBackend#stopBackend()
*/
@ -163,89 +197,68 @@ public class MetaModelVascBackend extends AbstractVascBackend {
public List<Object> execute(VascBackendState state) throws VascException {
Schema schema = dataContext.getDefaultSchema();
Table t = schema.getTableByName(table);
if (t==null) {
throw new VascException("Could not get meta table for: '"+table+"'.");
}
Query q = createFilterQuery(state,t,false);
if (isPageable() && state.getPageSize()>0) {
q.setFirstRow(state.getPageIndex());
q.setMaxRows(state.getPageSize());
}
if (crudDataContext!=null) {
DataSet ds = crudDataContext.executeQuery(q);
List<Object> result = new ArrayList<Object>(50);
result.addAll(ds.toRows());
ds.close();
return result;
}
DataSet ds = dataContext.executeQuery(q);
List<Object> result = new ArrayList<Object>(50);
while (ds.next()) {
Row row = ds.getRow();
SelectItem[] cols = row.getSelectItems();
Map<String,Object> map = new HashMap<String,Object>(cols.length);
List<String> keys = new ArrayList<String>(1);
keys.add(cols[0].getColumn().getName());
UpdateableRowMapImpl rowMM = new UpdateableRowMapImpl(dataContext.getDefaultSchema().getTableByName(table),keys);
for (SelectItem col:cols) {
Object value = row.getValue(col);
map.put(col.getColumn().getName(), value);
rowMM.setValue(col, value);
}
result.add(map);
result.add(rowMM);
}
ds.close();
return result;
}
@SuppressWarnings("unchecked")
public void persist(Object object) throws VascException {
final Map<String,Object> map = (Map<String,Object>)object;
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
RowInsertionBuilder query = backendImpl.insertInto(table);
for (String key:map.keySet()) {
Object value = map.get(key);
query.value(key, value);
}
query.execute();
}
});
if (crudDataContext==null) {
return;
}
crudDataContext.persist((UpdateableRow) object);
}
@SuppressWarnings("unchecked")
public Object merge(Object object) throws VascException {
final Map<String,Object> map = (Map<String,Object>)object;
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
RowUpdationBuilder query = null;
if (map.get(tableId) instanceof Number) {
query = backendImpl.update(table).where(tableId).equals((Number)map.get(tableId));
} else {
query = backendImpl.update(table).where(tableId).equals(map.get(tableId).toString());
}
for (String key:map.keySet()) {
if (key.equals(tableId)) {
continue; // skip id;
}
Object value = map.get(key);
query.value(key, value);
}
query.execute();
}
});
return object;
if (crudDataContext==null) {
return object;
}
return crudDataContext.merge((UpdateableRow) object);
}
@SuppressWarnings("unchecked")
public void delete(Object object) throws VascException {
final Map<String,Object> map = (Map<String,Object>)object;
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
if (map.get(tableId) instanceof Number) {
backendImpl.deleteFrom(table).where(tableId).equals((Number)map.get(tableId)).execute();
} else {
backendImpl.deleteFrom(table).where(tableId).equals(map.get(tableId).toString()).execute();
}
}
});
if (crudDataContext==null) {
return;
}
crudDataContext.delete((UpdateableRow) object);
}
public VascEntryFieldValue provideVascEntryFieldValue(VascEntryField field) {
return new MapVascEntryFieldValue();
return new RowVascEntryFieldValue();
}
public VascEntryRecordCreator provideVascEntryRecordCreator(VascEntry vascEntry) {
return new MapVascEntryRecordCreator();
return new RowVascEntryRecordCreator(crudDataContext,crudDataContext.getDefaultSchema().getTableByName(table));
}
/**

View file

@ -0,0 +1,479 @@
package net.forwardfire.vasc.backend.metamodel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.bson.types.ObjectId;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.QueryPostprocessDataContext;
import org.eobjects.metamodel.data.AbstractDataSet;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.DefaultRow;
import org.eobjects.metamodel.data.FirstRowDataSet;
import org.eobjects.metamodel.data.MaxRowsDataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.FilterItem;
import org.eobjects.metamodel.query.FromItem;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.ColumnType;
import org.eobjects.metamodel.schema.MutableColumn;
import org.eobjects.metamodel.schema.MutableRelationship;
import org.eobjects.metamodel.schema.MutableSchema;
import org.eobjects.metamodel.schema.MutableTable;
import org.eobjects.metamodel.schema.Relationship;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.schema.TableType;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
public class MongoDbDataContextSchemaDetector /* implements MetaModelSchemaDetector */ {
private int dataCheckSize = 100;
private boolean searchReference = true;
public MongoDbDataContextSchemaDetector() {
}
public class MongoDbDataContextExtended extends QueryPostprocessDataContext /* MongoDbDataContext */ {
protected DB mongoDb;
protected Schema schema;
protected MongoDbDataContextSchemaDetector walker;
public MongoDbDataContextExtended(DB mongoDb,MongoDbDataContextSchemaDetector walker) {
this.mongoDb=mongoDb;
this.walker=walker;
}
@Override
protected Schema getMainSchema() throws MetaModelException {
if (schema!=null) {
return schema;
}
schema = walker.detectSchema(mongoDb, getMainSchemaName());
return schema;
}
protected String getMainSchemaName() throws MetaModelException {
return mongoDb.getName();
}
protected DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {
return materializeMainSchemaTableInternal(table, columns, null, maxRows, true);
}
// Overidded these method because they do not use the getMainSchema() method
public DataSet executeQuery(Query query) {
// Check for queries containing only simple selects and where clauses,
// or if it is a COUNT(*) query.
// if from clause only contains a main schema table
List<FromItem> fromItems = query.getFromClause().getItems();
if (fromItems.size() == 1 && fromItems.get(0).getTable() != null
&& fromItems.get(0).getTable().getSchema() == getMainSchema()) {
final Table table = fromItems.get(0).getTable();
// if GROUP BY, HAVING and ORDER BY clauses are not specified
if (query.getGroupByClause().isEmpty() && query.getHavingClause().isEmpty()
&& query.getOrderByClause().isEmpty()) {
final List<FilterItem> whereItems = query.getWhereClause().getItems();
// if all of the select items are "pure" column selection
boolean allSelectItemsAreColumns = true;
List<SelectItem> selectItems = query.getSelectClause().getItems();
// if it is a
// "SELECT [columns] FROM [table] WHERE [conditions]"
// query.
for (SelectItem selectItem : selectItems) {
if (selectItem.getFunction() != null || selectItem.getColumn() == null) {
allSelectItemsAreColumns = false;
break;
}
}
if (allSelectItemsAreColumns) {
//logger.debug("Query can be expressed in full MongoDB, no post processing needed.");
// prepare for a non-post-processed query
Column[] columns = new Column[selectItems.size()];
for (int i = 0; i < columns.length; i++) {
columns[i] = selectItems.get(i).getColumn();
}
DataSet dataSet = materializeMainSchemaTableInternal(table, columns, whereItems, -1, false);
if (query.getFirstRow() != null) {
dataSet = new FirstRowDataSet(dataSet, query.getFirstRow());
}
if (query.getMaxRows() != null) {
dataSet = new MaxRowsDataSet(dataSet, query.getMaxRows());
}
return dataSet;
}
}
}
//logger.debug("Query will be simplified for MongoDB and post processed.");
return super.executeQuery(query);
}
private DataSet materializeMainSchemaTableInternal(Table table, Column[] columns, List<FilterItem> whereItems,
int maxRows, boolean queryPostProcessed) {
final DBCollection collection = mongoDb.getCollection(table.getName());
final DBObject query = createMongoDbQuery(table, whereItems);
//logger.info("Executing MongoDB 'find' query: {}", query);
DBCursor cursor = collection.find(query);
if (maxRows > 0) {
cursor = cursor.limit(maxRows);
}
return new MongoDbDataSet(cursor, columns, queryPostProcessed);
}
final class MongoDbDataSet extends AbstractDataSet {
//private static final Logger logger = LoggerFactory
// .getLogger(MongoDbDataSet.class);
private final DBCursor _cursor;
private final SelectItem[] _selectItems;
private final boolean _queryPostProcessed;
private boolean _closed;
private volatile DBObject _dbObject;
public MongoDbDataSet(DBCursor cursor, Column[] columns,
boolean queryPostProcessed) {
_cursor = cursor;
_selectItems = new SelectItem[columns.length];
for (int i = 0; i < columns.length; i++) {
_selectItems[i] = new SelectItem(columns[i]);
}
_queryPostProcessed = queryPostProcessed;
_closed = false;
}
public boolean isQueryPostProcessed() {
return _queryPostProcessed;
}
public SelectItem[] getSelectItems() {
return _selectItems;
}
@Override
public void close() {
super.close();
_cursor.close();
_closed = true;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
if (!_closed) {
// logger.warn(
// "finalize() invoked, but DataSet is not closed. Invoking close() on {}",
// this);
close();
}
}
public boolean next() {
if (_cursor.hasNext()) {
_dbObject = _cursor.next();
return true;
} else {
_dbObject = null;
return false;
}
}
public Row getRow() {
if (_dbObject == null) {
return null;
}
final Object[] values = new Object[_selectItems.length];
for (int i = 0; i < values.length; i++) {
String key = _selectItems[i].getColumn().getName();
Object value = _dbObject.get(key);
values[i] = toValue(_selectItems[i].getColumn(), value);
}
return new DefaultRow(_selectItems, values);
}
private Object toValue(Column column, Object value) {
if (value instanceof List) {
return value;
}
if (value instanceof DBObject) {
DBObject basicDBObject = (DBObject) value;
return basicDBObject.toMap();
}
return value;
}
}
protected BasicDBObject createMongoDbQuery(Table table, List<FilterItem> whereItems) {
assert getMainSchema() == table.getSchema();
final BasicDBObject query = new BasicDBObject();
if (whereItems != null && !whereItems.isEmpty()) {
for (FilterItem item : whereItems) {
convertToCursorObject(query, item);
}
}
return query;
}
private void convertToCursorObject(BasicDBObject query, FilterItem item) {
if (item.isCompoundFilter()) {
BasicDBList orList = new BasicDBList();
final FilterItem[] childItems = item.getChildItems();
for (FilterItem childItem : childItems) {
BasicDBObject childObject = new BasicDBObject();
convertToCursorObject(childObject, childItem);
orList.add(childObject);
}
query.put("$or", orList);
} else {
final Column column = item.getSelectItem().getColumn();
final String columnName = column.getName();
final Object operand = item.getOperand();
final String operatorName = getOperatorName(item);
final BasicDBObject existingFilterObject = (BasicDBObject) query.get(columnName);
if (existingFilterObject == null) {
if (operatorName == null) {
query.put(columnName, operand);
} else {
query.put(columnName, new BasicDBObject(operatorName, operand));
}
} else {
if (operatorName == null) {
throw new IllegalStateException("Cannot retrieve records for a column with two EQUALS_TO operators");
} else {
existingFilterObject.append(operatorName, operand);
}
}
}
}
@SuppressWarnings("deprecation")
private String getOperatorName(FilterItem item) {
final String operatorName;
switch (item.getOperator()) {
case EQUALS_TO:
operatorName = null;
break;
case LESS_THAN:
case LOWER_THAN:
operatorName = "$lt";
break;
case GREATER_THAN:
case HIGHER_THAN:
operatorName = "$gt";
break;
case DIFFERENT_FROM:
operatorName = "$ne";
break;
case IN:
operatorName = "$in";
break;
default:
throw new IllegalStateException("Unsupported operator type: " + item.getOperator());
}
return operatorName;
}
protected void addTable(MutableTable table) {
if (getMainSchema() instanceof MutableSchema) {
MutableSchema mutableSchema = (MutableSchema) getMainSchema();
mutableSchema.addTable(table);
} else {
throw new UnsupportedOperationException("Schema is not mutable");
}
}
}
public Schema detectSchema(Object nativeBackendConnection,String schemaName) {
if ((nativeBackendConnection instanceof DB)==false) {
throw new RuntimeException("Can only work with DB instance not with: "+nativeBackendConnection);
}
DB db = (DB)nativeBackendConnection;
MutableSchema schema = new MutableSchema(schemaName);
for (MutableTable tableDef : detectSchemaTables(db)) {
tableDef.setSchema(schema);
schema.addTable(tableDef);
}
return schema;
}
protected MutableTable[] detectSchemaTables(DB db) {
Set<String> collectionNames = db.getCollectionNames();
Map<String,PublicTable> tables = new HashMap<String,PublicTable>(collectionNames.size());
for (String collectionName : collectionNames) {
PublicTable table = detectTable(db, collectionName);
tables.put(collectionName, table);
}
// Check for object id
for (String collectionName:objectIdRef.keySet()) {
Map<String,ObjectId> refMap = objectIdRef.get(collectionName);
if (refMap==null) {
continue;
}
for (String columnName:refMap.keySet()) {
ObjectId objectId = refMap.get(columnName);
for (String colName : collectionNames) {
Column primaryColumn = findColumnRef(db,colName,tables,objectId,columnName);
if (primaryColumn==null) {
continue;
}
PublicTable t = tables.get(collectionName);
Column foreignColumn = t.getColumnByName(columnName);
t.addRelationship(MutableRelationship.createRelationship(primaryColumn, foreignColumn));
}
}
}
MutableTable[] result = new MutableTable[collectionNames.size()];
return new ArrayList<MutableTable>(tables.values()).toArray(result);
}
Map<String,Map<String,ObjectId>> objectIdRef = new HashMap<String,Map<String,ObjectId>>(20);
protected Column findColumnRef(DB db, String collectionName,Map<String,PublicTable> tables,ObjectId objectId,String colName) {
DBCollection collection = db.getCollection(collectionName);
DBObject query = new BasicDBObject();
query.put("_id", objectId);
DBCursor cursor = collection.find(query).limit(dataCheckSize);
try {
if (cursor.hasNext()) {
PublicTable t = tables.get(collectionName);
return t.getColumnByName("_id");
}
} finally {
if (cursor!=null) {
cursor.close();
}
}
return null;
}
protected PublicTable detectTable(DB db, String collectionName) {
final DBCollection collection = db.getCollection(collectionName);
final DBCursor cursor = collection.find().limit(dataCheckSize);
final SortedMap<String, Set<Class<?>>> columnsAndTypes = new TreeMap<String, Set<Class<?>>>();
while (cursor.hasNext()) {
DBObject row = cursor.next();
Set<String> keysInObject = row.keySet();
for (String key : keysInObject) {
Set<Class<?>> types = columnsAndTypes.get(key);
if (types == null) {
types = new HashSet<Class<?>>();
columnsAndTypes.put(key, types);
}
Object value = row.get(key);
if (value != null) {
types.add(value.getClass());
}
if ("_id".equals(key)==false && value instanceof ObjectId) {
Map<String,ObjectId> map = objectIdRef.get(collectionName);
if (map==null) {
map = new HashMap<String,ObjectId>(10);
objectIdRef.put(collectionName,map);
}
map.put(key, (ObjectId)value);
}
}
}
cursor.close();
final String[] columnNames = new String[columnsAndTypes.size()];
final ColumnType[] columnTypes = new ColumnType[columnsAndTypes.size()];
int i = 0;
for (Entry<String, Set<Class<?>>> columnAndTypes : columnsAndTypes.entrySet()) {
final String columnName = columnAndTypes.getKey();
final Set<Class<?>> columnTypeSet = columnAndTypes.getValue();
final Class<?> columnType;
if (columnTypeSet.size() == 1) {
columnType = columnTypeSet.iterator().next();
} else {
columnType = Object.class;
}
columnNames[i] = columnName;
if (columnType == ObjectId.class) {
columnTypes[i] = ColumnType.ROWID;
} else {
columnTypes[i] = ColumnType.convertColumnType(columnType);
}
i++;
}
PublicTable tableSchema = new PublicTable(collectionName,TableType.TABLE);
for (int y = 0; y < columnNames.length; y++) {
MutableColumn col = new MutableColumn(columnNames[y], columnTypes[y], tableSchema, i, true);
tableSchema.addColumn(col);
}
return tableSchema; // new SimpleTableDef(collectionName, columnNames, columnTypes);
}
// Override to make public
class PublicTable extends MutableTable {
public PublicTable(String name,TableType type) {
super(name,type);
}
@Override
public void addRelationship(Relationship relation) {
super.addRelationship(relation);
}
}
/**
* @return the dataCheckSize
*/
public int getDataCheckSize() {
return dataCheckSize;
}
/**
* @param dataCheckSize the dataCheckSize to set
*/
public void setDataCheckSize(int dataCheckSize) {
this.dataCheckSize = dataCheckSize;
}
/**
* @return the searchReference
*/
public boolean isSearchReference() {
return searchReference;
}
/**
* @param searchReference the searchReference to set
*/
public void setSearchReference(boolean searchReference) {
this.searchReference = searchReference;
}
}

View file

@ -0,0 +1,71 @@
package net.forwardfire.vasc.backend.metamodel;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.SelectItem;
import net.forwardfire.vasc.backend.metamodel.crud.UpdateableRow;
import net.forwardfire.vasc.core.VascEntryField;
import net.forwardfire.vasc.core.VascException;
import net.forwardfire.vasc.core.entry.VascEntryFieldValue;
public class RowVascEntryFieldValue implements VascEntryFieldValue {
private static final long serialVersionUID = -806674640688182132L;
/**
* @see net.forwardfire.vasc.core.entry.VascEntryFieldValue#getValue(net.forwardfire.vasc.core.VascEntryField, java.lang.Object)
*/
public Object getValue(VascEntryField field, Object record) throws VascException {
if (field==null) {
throw new NullPointerException("Can't get value of null field.");
}
if (field.getBackendName()==null) {
throw new NullPointerException("Can't get value of null backendName field.");
}
if (record==null) {
throw new NullPointerException("Can't get value of null object.");
}
if (record instanceof UpdateableRow) {
UpdateableRow row = (UpdateableRow)record;
return row.getValue(field.getBackendName());
}
Row row = (Row)record;
Object fieldValue = row.getValue(indexOf(field.getBackendName(),row));
return fieldValue;
}
private int indexOf(String columnName,Row row) { // RM after MM Row update
if (columnName==null) {
return UpdateableRow.INDEX_NOT_FOUND;
}
int index = 0;
for (SelectItem si:row.getSelectItems()) {
if (si.getColumn().getName().equals(columnName)) {
return index;
}
index++;
}
return UpdateableRow.INDEX_NOT_FOUND;
}
/**
* @see net.forwardfire.vasc.core.entry.VascEntryFieldValue#getDisplayValue(net.forwardfire.vasc.core.VascEntryField, java.lang.Object)
*/
public String getDisplayValue(VascEntryField field, Object record) throws VascException {
Object fieldValue = getValue(field,record);
if (fieldValue==null) {
fieldValue = "";
}
return fieldValue.toString();
}
/**
* @see net.forwardfire.vasc.core.entry.VascEntryFieldValue#setValue(net.forwardfire.vasc.core.VascEntryField, java.lang.Object, java.lang.Object)
*/
public void setValue(VascEntryField field, Object record,Object value) throws VascException {
if (record instanceof UpdateableRow) {
UpdateableRow row = (UpdateableRow)record;
row.setValue(field.getBackendName(), value);
}
}
}

View file

@ -0,0 +1,28 @@
package net.forwardfire.vasc.backend.metamodel;
import org.eobjects.metamodel.schema.Table;
import net.forwardfire.vasc.backend.metamodel.crud.CrudDataContext;
import net.forwardfire.vasc.backend.metamodel.crud.UpdateableRow;
import net.forwardfire.vasc.core.VascEntry;
import net.forwardfire.vasc.core.entry.VascEntryRecordCreator;
public class RowVascEntryRecordCreator implements VascEntryRecordCreator {
private static final long serialVersionUID = -1182678362367989090L;
private CrudDataContext dataContext = null;
private Table table = null;
public RowVascEntryRecordCreator(CrudDataContext dataContext,Table table) {
this.dataContext=dataContext;
this.table=table;
}
public Class<?> getObjectClass() {
return UpdateableRow.class;
}
public Object newRecord(VascEntry entry) throws Exception {
return dataContext.createRow(table);
}
}

View file

@ -0,0 +1,240 @@
package net.forwardfire.vasc.backend.metamodel.bundle;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.QueryPostprocessDataContext;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.AbstractDataSet;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.DefaultRow;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.FilterItem;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.MutableColumn;
import org.eobjects.metamodel.schema.MutableSchema;
import org.eobjects.metamodel.schema.MutableTable;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.schema.TableType;
/**
* ResourceBundleDataContext is updatable context for file based data source.
* Will try to load all supported locals by jvm as tables. (except for URL)
*
* @author Willem Cazander
* @version 1.0 May 26, 2012
*/
public class ResourceBundleDataContext extends QueryPostprocessDataContext implements UpdateableDataContext {
protected static final String DEFAULT_CHAR_SET = "UTF-8";
protected ResourceBundleUpdateExecutor resourceBundleUpdateExecutor = null;
protected ResourceBundleDataControl bundleDataControl = null;
protected String mainSchemaName = null;
protected Charset charSet = null;
private ResourceBundleDataContext(Charset charSet) {
if (charSet==null) {
throw new NullPointerException("Can't start with null charSet");
}
this.charSet = charSet;
}
public ResourceBundleDataContext(String resourceBundle) {
this(resourceBundle,Charset.forName(DEFAULT_CHAR_SET));
}
public ResourceBundleDataContext(String resourceBundle,Charset charSet) {
this(charSet);
if (resourceBundle==null) {
throw new NullPointerException("Can't start with null resourceBundle");
}
init(resourceBundle);
}
public ResourceBundleDataContext(File resourceBundle) {
this(resourceBundle,Charset.forName(DEFAULT_CHAR_SET));
}
public ResourceBundleDataContext(File resourceBundle,Charset charSet) {
this(charSet);
if (resourceBundle==null) {
throw new NullPointerException("Can't start with null resourceBundle");
}
init(resourceBundle);
}
public ResourceBundleDataContext(URL resourceBundle) {
this(resourceBundle,Charset.forName(DEFAULT_CHAR_SET));
}
public ResourceBundleDataContext(URL resourceBundle,Charset charSet) {
this(charSet);
if (resourceBundle==null) {
throw new NullPointerException("Can't start with null resourceBundle");
}
init(resourceBundle);
}
/**
* Init ResourceBundleDataContext to use diffect source backends.
*/
protected void init(Object source) {
if (source==null) {
throw new NullPointerException("Can't init with null source");
}
if (bundleDataControl!=null) {
throw new NullPointerException("Only one init allowed."); // can't override refreshSchema to reinit again.
}
// Setup data source
String bundleBaseName = null;
if (source instanceof File) {
File baseFile = (File)source;
bundleBaseName = baseFile.getName();
bundleDataControl = new ResourceBundleDataControl(charSet,baseFile);
} else if (source instanceof URL) {
URL baseUrl = (URL)source;
bundleBaseName = baseUrl.getFile();
bundleDataControl = new ResourceBundleDataControl(charSet,baseUrl);
} else {
bundleBaseName = source.toString();
bundleDataControl = new ResourceBundleDataControl(charSet);
}
mainSchemaName = cleanMainSchemaName(bundleBaseName);
// Load all data
//ResourceBundle.getBundle(bundleBaseName,bundleDataControl);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl==null) { cl=getClass().getClassLoader(); }
try {
bundleDataControl.newBundle(bundleBaseName, Locale.getDefault(), null, cl, false);
} catch (Exception e) {
e.printStackTrace();
}
// Start Query executor
resourceBundleUpdateExecutor = new ResourceBundleUpdateExecutor(this,bundleDataControl);
}
// TODO check where static verion is.
protected String cleanMainSchemaName(String name) {
if (name.endsWith(ResourceBundleDataControl.BUNDLE_EXTENSION)) {
name = name.substring(0,name.indexOf(ResourceBundleDataControl.BUNDLE_EXTENSION)-1);
}
StringBuffer buffer = new StringBuffer(name.length());
for (char c:name.toCharArray()) {
if (Character.isDigit(c)) {
buffer.append(c);
} else if (Character.isLetter(c)) {
buffer.append(c);
} else {
buffer.append('_');
}
}
return buffer.toString();
}
public boolean isDataContextWritable() {
return bundleDataControl.isWritable();
}
protected MutableTable createTable(Schema schema,String name) {
MutableTable table = new MutableTable(name, TableType.TABLE, schema);
MutableColumn idColumn = new MutableColumn("id");
//idColumn.setColumnNumber(0);
idColumn.setIndexed(true);
idColumn.setNullable(false);
//idColumn.setPrimaryKey(true);
MutableColumn keyColumn = new MutableColumn("key");
keyColumn.setColumnNumber(1);
keyColumn.setNullable(false);
MutableColumn valueColumn = new MutableColumn("value");
valueColumn.setColumnNumber(2);
valueColumn.setNullable(false);
table.addColumn(idColumn);
table.addColumn(keyColumn);
table.addColumn(valueColumn);
return table;
}
// ======== Interface
@Override
protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) {
return null;
}
@Override
protected Schema getMainSchema() throws MetaModelException {
String schemaName = getDefaultSchemaName();
MutableSchema schema = new MutableSchema(schemaName);
for (String bundleName:bundleDataControl.getBundleNames()) {
schema.addTable(createTable(schema,bundleName));
}
return schema;
}
// TODO: move to SelectItem
static public SelectItem[] convertColumns(Column[] cols) {
SelectItem[] selectItems = new SelectItem[cols.length];
for (int i=0;i<cols.length;i++) {
selectItems[i] = new SelectItem(cols[i]);
}
return selectItems;
}
@Override
protected DataSet materializeMainSchemaTable(Table table, Column[] cols,int maxRows) {
return materializeTable(table.getName(),convertColumns(table.getColumns()),maxRows);
}
protected DataSet materializeTable(String table,final SelectItem[] selectItems,final int maxRows) {
Properties data = bundleDataControl.getBundleData(table);
List<Row> result = new ArrayList<Row>(200);
for (Object keyO:data.keySet()) {
String keyId = Integer.toString(keyO.hashCode());
String key = (String)keyO;
String value = data.getProperty(key);
result.add(new DefaultRow(selectItems, new Object[] {keyId,key,value}));
}
final Iterator<Row> resultData = result.iterator();
return new AbstractDataSet() {
int resultCount = 0;
public SelectItem[] getSelectItems() { return selectItems; }
public Row getRow() { return resultData.next(); }
public boolean next() {
if (maxRows > 0 && resultCount++ > maxRows) {
return false;
}
return resultData.hasNext();
}
};
}
public void executeUpdate(UpdateScript update) {
update.run(resourceBundleUpdateExecutor);
}
@Override
protected String getMainSchemaName() throws MetaModelException {
return mainSchemaName;
}
}

View file

@ -0,0 +1,254 @@
package net.forwardfire.vasc.backend.metamodel.bundle;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ResourceBundleControl load ResourceBundles with correct CharSet from file and class path.
*
* @author Willem Cazander
* @version 1.0 Mar 26, 2012
*/
public class ResourceBundleDataControl extends Control {
/** The default extension for bundles. */
protected static final String BUNDLE_EXTENSION = "properties";
protected static final String BUNDLE_COMMENT = "Saved by MetaModel; ";
protected Logger logger = null;
protected Charset charSet = null;
/** The charSet to load with **/
protected File baseBundleFile = null;
protected URL baseBundleUrl = null;
protected Map<String,Properties> bundleData = null;
protected Map<String,File> bundleFiles = null;
private ResourceBundleDataControl() {
logger = LoggerFactory.getLogger(ResourceBundleDataControl.class);
bundleFiles = new HashMap<String,File>(10);
bundleData = Collections.synchronizedMap(new HashMap<String,Properties>(10));
}
/**
* Creates ResourceBundleControl to read ResourceBundle with charSet.
*/
public ResourceBundleDataControl(Charset charSet) {
this();
if (charSet==null) {
throw new NullPointerException("Can't load bundle with null Charset.");
}
this.charSet=charSet;
}
public ResourceBundleDataControl(Charset charSet,File baseBundleFile) {
this(charSet);
if (baseBundleFile==null) {
throw new NullPointerException("Can't load bundle with null baseBundleFile.");
}
this.baseBundleFile=baseBundleFile;
}
public ResourceBundleDataControl(Charset charSet,URL baseBundleUrl) {
this(charSet);
if (baseBundleUrl==null) {
throw new NullPointerException("Can't load bundle with null baseBundleUrl.");
}
this.baseBundleUrl=baseBundleUrl;
}
public boolean isWritable() {
return baseBundleFile!=null;
}
public Set<String> getBundleNames() {
return bundleData.keySet();
}
public Properties getBundleData(String bundleName) {
if (bundleName==null) {
throw new NullPointerException("Can't get data for null bundleName.");
}
return bundleData.get(bundleName);
}
public void removeBundle(String bundleName) {
Properties data = getBundleData(bundleName);
if (data==null) {
throw new IllegalStateException("Can't remove bundle data of unknown bundle: "+bundleName);
}
bundleData.remove(bundleName);
bundleFiles.remove(bundleName);
}
public void saveBundle(String bundleName) throws IOException {
Properties data = getBundleData(bundleName);
if (data==null) {
throw new IllegalStateException("Can't save bundle data of unknown bundle: "+bundleName);
}
File bundleFile = bundleFiles.get(bundleName);
if (bundleFile==null) {
throw new IllegalStateException("Could not find loaded bundle file: "+bundleName);
}
long startTime = System.currentTimeMillis();
saveBundleFile(data,bundleFile);
long stopTime = System.currentTimeMillis();
logger.info("Saved bundle "+bundleName+" with "+data.size()+" keys in "+(stopTime-startTime)+" ms.");
}
public int putBundleData(ResourceBundle bundle,String bundleName,File file) {
Properties p = new Properties();
for (String key:bundle.keySet()) {
p.put(key, bundle.getString(key));
}
bundleData.put(bundleName, p);
if (file!=null) {
bundleFiles.put(bundleName, file);
}
return p.size();
}
/**
* Does the loading of the ResourceBundle
*/
public ResourceBundle newBundle(String baseName, Locale localeRequested, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException {
long startTime = System.currentTimeMillis();
// On urls only load the requested one.
if (baseBundleUrl!=null) {
ResourceBundle bundle = loadBundleUrl(baseBundleUrl);
putBundleData(bundle, baseBundleUrl.getFile(), null);
return bundle;
}
if (baseName.endsWith(BUNDLE_EXTENSION)) {
baseName = baseName.substring(0,baseName.indexOf(BUNDLE_EXTENSION)-1);
}
// Try to load all bundles.
ResourceBundle bundleLast = null;
int totalBundleDataSize = 0;
for (Locale locale:Locale.getAvailableLocales()) {
String bundleName = toBundleName(baseName, locale);
ResourceBundle bundle = null;
File bundleFile = null;
try {
if (baseBundleFile==null) {
String resourceName = toResourceName(bundleName, BUNDLE_EXTENSION);
logger.info("Loading: "+resourceName);
bundle = loadBundleClassPath(resourceName,loader,reload);
} else {
String resourceName = baseBundleFile.getParent()+File.separatorChar+bundleName+"."+BUNDLE_EXTENSION;
logger.info("Loading: "+resourceName);
bundleFile = new File(resourceName);
bundle = loadBundleFile(bundleFile);
}
} catch (FileNotFoundException fnfe) {
} catch (IOException e) {
e.printStackTrace();
continue; // next
}
if (bundle==null) {
continue;
}
bundleLast = bundle;
String tableName = toBundleName("", locale).substring(1);
totalBundleDataSize += putBundleData(bundle,tableName,bundleFile);
}
long stopTime = System.currentTimeMillis();
logger.info("Loaded total "+bundleData.size()+" bundles with "+totalBundleDataSize+" keys in "+(stopTime-startTime)+" ms.");
return bundleLast; // just return last one, not requested one.
}
protected ResourceBundle loadBundleClassPath(String resourceName,ClassLoader loader,boolean reload) throws IOException {
ResourceBundle bundle = null;
InputStream stream = null;
if (reload) {
URL url = loader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
} else {
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null) {
bundle = loadBundleStream(stream);
}
return bundle;
}
protected ResourceBundle loadBundleUrl(URL url) throws IOException {
ResourceBundle bundle = null;
InputStream stream = null;
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
stream = connection.getInputStream();
}
if (stream != null) {
bundle = loadBundleStream(stream);
}
return bundle;
}
protected ResourceBundle loadBundleFile(File file) throws IOException {
ResourceBundle bundle = null;
InputStream stream = new FileInputStream(file);
if (stream != null) {
bundle = loadBundleStream(stream);
}
return bundle;
}
protected ResourceBundle loadBundleStream(InputStream stream) throws IOException {
if (stream==null) {
return null;
}
try {
return new PropertyResourceBundle(new InputStreamReader(stream, charSet));
} finally {
stream.close();
}
}
protected void saveBundleFile(Properties properties,File file) throws IOException {
OutputStream out = new FileOutputStream(file);
try {
properties.store(new OutputStreamWriter(out,charSet), BUNDLE_COMMENT+new Date());
} finally {
if (out!=null) {
out.close();
}
}
}
}

View file

@ -0,0 +1,137 @@
package net.forwardfire.vasc.backend.metamodel.bundle;
import java.io.File;
import java.util.Properties;
import java.util.ResourceBundle;
import org.eobjects.metamodel.AbstractUpdateCallback;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.UpdateCallback;
import org.eobjects.metamodel.create.ColumnCreationBuilder;
import org.eobjects.metamodel.create.TableCreationBuilder;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.delete.AbstractRowDeletionBuilder;
import org.eobjects.metamodel.delete.RowDeletionBuilder;
import org.eobjects.metamodel.drop.AbstractTableDropBuilder;
import org.eobjects.metamodel.drop.TableDropBuilder;
import org.eobjects.metamodel.insert.AbstractRowInsertionBuilder;
import org.eobjects.metamodel.insert.RowInsertionBuilder;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.ColumnType;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
/**
* ResourceBundleUpdateExecutor handles update to ResourceBundleDataContext
*
* @author Willem Cazander
* @version 1.0 May 26, 2012
*/
public class ResourceBundleUpdateExecutor extends AbstractUpdateCallback {
protected ResourceBundleDataContext resourceBundleDataContext = null;
protected ResourceBundleDataControl resourceBundleDataControl = null;
public ResourceBundleUpdateExecutor(ResourceBundleDataContext resourceBundleDataContext,ResourceBundleDataControl resourceBundleDataControl) {
super(resourceBundleDataContext);
this.resourceBundleDataControl=resourceBundleDataControl;
}
public boolean isDataContextWritable() {
return resourceBundleDataControl.isWritable();
}
@Override
public boolean isCreateTableSupported() {
return isDataContextWritable();
}
@Override
public boolean isInsertSupported() {
return isDataContextWritable();
}
public boolean isDeleteSupported() {
return isDataContextWritable();
}
public boolean isDropTableSupported() {
return isDataContextWritable();
}
public TableCreationBuilder createTable(final Schema schema, final String name) throws IllegalArgumentException, IllegalStateException {
TableCreationBuilder result = new TableCreationBuilder() {
public Table toTable() { return execute(); }
public String toSql() { return "nosql"; }
public TableCreationBuilder like(Table table) { return this; }
public ColumnCreationBuilder withColumn(String name) {
//return new ColumnCreationBuilderImpl(this,new MutableColumn(name));
final TableCreationBuilder parent = this;
return new ColumnCreationBuilder() {
public ColumnCreationBuilder withColumn(String name) { return this; }
public ColumnCreationBuilder ofType(ColumnType type) { return this; }
public ColumnCreationBuilder ofSize(int size) { return this; }
public ColumnCreationBuilder ofNativeType(String nativeType) { return this; }
public ColumnCreationBuilder nullable(boolean nullable) { return this; }
public ColumnCreationBuilder asPrimaryKey() { return this; }
public ColumnCreationBuilder like(Column column) { return this; }
public TableCreationBuilder like(Table table) { return this; }
public String toSql() { return "nosql"; }
public Table toTable() { return execute(); }
public Table execute() throws MetaModelException { return parent.execute();}
};
}
public Table execute() throws MetaModelException {
File file = new File(name);
Properties p = new Properties();
try {
resourceBundleDataControl.saveBundleFile(p, file);
ResourceBundle bundle = resourceBundleDataControl.loadBundleFile(file);
resourceBundleDataControl.putBundleData(bundle, name, file);
} catch (Exception e) {
throw new MetaModelException(e);
}
return resourceBundleDataContext.createTable(schema, name);
}
};
return result;
}
public TableDropBuilder dropTable(Table table) throws IllegalArgumentException, IllegalStateException,UnsupportedOperationException {
TableDropBuilder result = new AbstractTableDropBuilder(table) {
public void execute() throws MetaModelException {
resourceBundleDataControl.removeBundle(getTable().getName());
}
};
return result;
}
public RowInsertionBuilder insertInto(final Table table) throws IllegalArgumentException, IllegalStateException,UnsupportedOperationException {
RowInsertionBuilder result = new AbstractRowInsertionBuilder<UpdateCallback>(this, table) {
public void execute() throws MetaModelException {
Properties data = resourceBundleDataControl.getBundleData(table.getName());
Object[] values = getValues();
data.put(values[1], values[2]);
}
};
return result;
}
public RowDeletionBuilder deleteFrom(final Table table) throws IllegalArgumentException, IllegalStateException,UnsupportedOperationException {
RowDeletionBuilder result = new AbstractRowDeletionBuilder(table) {
public void execute() throws MetaModelException {
DataSet ds = resourceBundleDataContext.materializeMainSchemaTable(getTable(),getTable().getColumns(),0);
Properties data = resourceBundleDataControl.getBundleData(table.getName());
while (ds.next()) {
Row row = ds.getRow();
if (deleteRow(row)) {
data.remove(row.getValue(1)); // delete by key
}
}
}
};
return result;
}
}

View file

@ -0,0 +1,294 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.UpdateCallback;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.EmptyDataSet;
import org.eobjects.metamodel.data.InMemoryDataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.delete.RowDeletionBuilder;
import org.eobjects.metamodel.insert.RowInsertionBuilder;
import org.eobjects.metamodel.query.FromItem;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.query.builder.SatisfiedQueryBuilder;
import org.eobjects.metamodel.query.builder.SatisfiedWhereBuilder;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.update.RowUpdationBuilder;
/**
* AbstractCrudDataContext implements the generic abstract crud actions.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
abstract public class AbstractCrudDataContext implements CrudDataContext {
abstract public UpdateableRow wrapOrCreateToUpdateableRow(Row row,Table table);
protected UpdateableDataContext abstractProviderUpdateableDataContext() {
return this;
}
public UpdateableRow createRow(Table table) {
return wrapOrCreateToUpdateableRow(null,table);
}
public void persist(final UpdateableRow row) {
UpdateableDataContext dataContext = abstractProviderUpdateableDataContext();
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
RowInsertionBuilder query = backendImpl.insertInto(row.getTable());
for (int i=0;i<row.size();i++) {
SelectItem si = row.getSelectItem(i);
Object value = row.getValue(i);
query.value(si.getColumn(), value);
}
query.execute();
}
});
}
public Object merge(final UpdateableRow row) {
for (String column:row.getPrimaryKeys()) {
if (row.getValue(column)==null) {
throw new IllegalStateException("Can't update row where primary key: "+column+" value is null.");
}
}
UpdateableDataContext dataContext = abstractProviderUpdateableDataContext();
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
Object qWhere = backendImpl.update(row.getTable());
boolean first = true;
List<String> primaryKeyColumns = new ArrayList<String>(30);
Collections.addAll(primaryKeyColumns, row.getPrimaryKeys());
for (String column:primaryKeyColumns) {
Object value = row.getValue(column);
queryWhereBuilderUpdate(qWhere,column,value,first);
first = false;
}
List<String> columns = new ArrayList<String>(30);
Collections.addAll(columns, row.getTable().getColumnNames());
columns.removeAll(primaryKeyColumns);
for (String column:columns) {
Object value = row.getValue(column);
((RowUpdationBuilder)qWhere).value(column, value);
}
if (first==false) {
((RowUpdationBuilder)qWhere).execute();
} else {
// loggger.warning no where clause
}
}
});
Object qWhere = dataContext.query().from(row.getTable()).select(row.getTable().getColumns());
boolean first = true;
for (String column:row.getPrimaryKeys()) {
Object value = row.getValue(column);
queryWhereBuilder(qWhere,column,value,first);
first = false;
}
Query mergeSelectQuery = ((SatisfiedQueryBuilder<?>)qWhere).toQuery();
DataSet ds = dataContext.executeQuery(mergeSelectQuery);
if (ds.next()==false) {
ds.close();
throw new IllegalStateException("Could not fetch row after merging.");
}
UpdateableRow rowResult = wrapOrCreateToUpdateableRow(ds.getRow(),row.getTable());
ds.close();
return rowResult;
}
public void delete(final UpdateableRow row) {
UpdateableDataContext dataContext = abstractProviderUpdateableDataContext();
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback backendImpl) {
Object qWhere = backendImpl.deleteFrom(row.getTable());
boolean first = true;
for (String column:row.getPrimaryKeys()) {
Object value = row.getValue(column);
queryWhereBuilderDelete(qWhere,column,value,first);
first = false;
}
if (first==false) {
((RowDeletionBuilder)qWhere).execute();
} else {
///logger.warning no where clause
}
}
});
}
// TODO: redo these queryWhereBuilder methods to cleaner code
protected Object queryWhereBuilder(Object qWhere,String key,Object value,boolean first) {
if (value instanceof Number) {
if (first) {
qWhere = ((SatisfiedQueryBuilder<?>)qWhere).where(key).equals((Number)value);
} else {
qWhere = ((SatisfiedWhereBuilder<?>)qWhere).and(key).equals((Number)value);
}
} else if (value instanceof Date) {
if (first) {
qWhere = ((SatisfiedQueryBuilder<?>)qWhere).where(key).equals((Date)value);
} else {
qWhere = ((SatisfiedWhereBuilder<?>)qWhere).and(key).equals((Date)value);
}
} else if (value instanceof Boolean) {
if (first) {
qWhere = ((SatisfiedQueryBuilder<?>)qWhere).where(key).equals((Boolean)value);
} else {
qWhere = ((SatisfiedWhereBuilder<?>)qWhere).and(key).equals((Boolean)value);
}
} else {
if (value==null) {
if (first) {
qWhere = ((SatisfiedQueryBuilder<?>)qWhere).where(key).isNull();
} else {
qWhere = ((SatisfiedWhereBuilder<?>)qWhere).and(key).isNull();
}
} else {
if (first) {
qWhere = ((SatisfiedQueryBuilder<?>)qWhere).where(key).equals(value.toString());
} else {
qWhere = ((SatisfiedWhereBuilder<?>)qWhere).and(key).equals(value.toString());
}
}
}
return qWhere;
}
protected Object queryWhereBuilderUpdate(Object qWhere,String key,Object value,boolean first) {
if (value instanceof Number) {
if (first) {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Number)value);
} else {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Number)value);
}
} else if (value instanceof Date) {
if (first) {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Date)value);
} else {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Date)value);
}
} else if (value instanceof Boolean) {
if (first) {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Boolean)value);
} else {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals((Boolean)value);
}
} else {
if (value==null) {
if (first) {
qWhere = ((RowUpdationBuilder)qWhere).where(key).isNull();
} else {
qWhere = ((RowUpdationBuilder)qWhere).where(key).isNull();
}
} else {
if (first) {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals(value.toString());
} else {
qWhere = ((RowUpdationBuilder)qWhere).where(key).equals(value.toString());
}
}
}
return qWhere;
}
protected Object queryWhereBuilderDelete(Object qWhere,String key,Object value,boolean first) {
if (value instanceof Number) {
if (first) {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Number)value);
} else {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Number)value);
}
} else if (value instanceof Date) {
if (first) {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Date)value);
} else {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Date)value);
}
} else if (value instanceof Boolean) {
if (first) {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Boolean)value);
} else {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals((Boolean)value);
}
} else {
if (value==null) {
if (first) {
qWhere = ((RowDeletionBuilder)qWhere).where(key).isNull();
} else {
qWhere = ((RowDeletionBuilder)qWhere).where(key).isNull();
}
} else {
if (first) {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals(value.toString());
} else {
qWhere = ((RowDeletionBuilder)qWhere).where(key).equals(value.toString());
}
}
}
return qWhere;
}
/**
* executeQueryWrapDataSet interface so when implemented on backend then executeQueryWrapDataSet is not used.
* There for no need to expose this primary key handler method in this abstract class.
*/
protected interface QueryWrapDataSetPrimaryKeyHandler {
public List<String> getPrimaryKeysForTable(Table table);
}
protected DataSet executeQueryWrapDataSet(DataContext dataContext,Query query,boolean checkPrimaryKeysAllRows,QueryWrapDataSetPrimaryKeyHandler keyHandler) throws MetaModelException {
DataSet data = dataContext.executeQuery(query);
if (query.getFromClause().getItems().size() > 1) {
return data; // Only supports crud on single table data.
}
FromItem fromItem = query.getFromClause().getItems().get(0);
Table table = fromItem.getTable();
List<String> keys = keyHandler.getPrimaryKeysForTable(table);
if (keys.isEmpty()) {
return data;
}
if (query.getSelectClause().getItemCount()==1 && query.getSelectClause().getItem(0).getColumn()==null) {
return data; // TODO: use correct api to detect selectCount query
}
List<Row> dataWrap = new ArrayList<Row>(2000);
boolean keysOke = false; // NOTE: could add option checkPrimaryKeys to override if user/impl is really sure keys are there.
boolean keysCheck = true; // always check first row
List<String> keysCheckList = new ArrayList<String>(keys); // copy keys
while (data.next()) {
Row row = data.getRow();
if (keysCheck) {
for (SelectItem si:row.getSelectItems()) {
keysCheckList.remove(si.getColumn().getName());
}
keysCheck = checkPrimaryKeysAllRows; // default is check first row only.
keysOke = keysCheckList.isEmpty(); // Found all primary keys in result so we can wrap data.
if (keysCheck) {
keysCheckList.addAll(keys); // Reload keys to check all data.
}
}
if (keysOke) {
dataWrap.add(wrapOrCreateToUpdateableRow(data.getRow(),table));
} else {
dataWrap.add(row);
}
}
data.close();
if (dataWrap.isEmpty()) {
return new EmptyDataSet(table.getColumns());
}
return new InMemoryDataSet(dataWrap);
}
}

View file

@ -0,0 +1,163 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.data.Style;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
/**
* AbstractRow implements most Row features onto of itself.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
@SuppressWarnings("serial")
abstract public class AbstractRow implements Row,RowLocal {
protected Map<Integer,MetaRowData> metaData = null;
public AbstractRow() {
metaData = new HashMap<Integer,MetaRowData>(30);
}
public SelectItem getSelectItem(int index) {
MetaRowData metaRow = getMetaRowData(index);
return metaRow.selectItem;
}
public void addSelectItems(Column[] columns) {
for (Column column:columns) {
addSelectItem(new SelectItem(column));
}
}
public void addSelectItems(SelectItem[] selectItems) {
for (SelectItem selectItem:selectItems) {
addSelectItem(selectItem);
}
}
public void addSelectItems(List<SelectItem> selectItems) {
for (SelectItem selectItem:selectItems) {
addSelectItem(selectItem);
}
}
public void addSelectItem(SelectItem selectItem) {
MetaRowData metaRow = new MetaRowData();
metaRow.selectItem=selectItem;
metaData.put(metaData.size(), metaRow);
}
public MetaRowData getMetaRowData(int index) {
MetaRowData metaRow = metaData.get(index);
return metaRow;
}
public List<MetaRowData> getIndexedMetaRowDataList() {
List<MetaRowData> result = new ArrayList<MetaRowData>(size());
for (int i=0;i<metaData.size();i++) {
MetaRowData metaRow = metaData.get(i);
result.add(metaRow);
}
return result;
}
public List<SelectItem> getIndexedSelectItemList() {
List<SelectItem> result = new ArrayList<SelectItem>(size());
for (MetaRowData metaRow:getIndexedMetaRowDataList()) {
result.add(metaRow.selectItem);
}
return result;
}
public void setStyle(int index,Style style) {
MetaRowData metaRow = metaData.get(index);
metaRow.style=style;
}
class MetaRowData /* implements DataSetRowMetaData */ {
SelectItem selectItem;
Style style;
// name and backendIndex,etc so DataSetApi does not need SchemaApi, when using schema aware code like an DataContext
// the DC api does not change because it fill/config/wires the DS api with meta data from schema.
// When using DataSet api in non-schema aware code then caller has to provided meta data for api to work.
// ObjectConverter objectConverter
// List<ObjectValidator>
// etc/etc
}
// ==== Start interface
public Object[] getValues() {
Object[] result = new Object[size()];
for (int i=0;i<size();i++) {
result[i] = getValue(i);
}
return result;
}
public Object getValue(SelectItem item) {
return getValue(indexOf(item));
}
public Object getValue(Column column) {
return getValue(indexOf(column));
}
public int indexOf(SelectItem item) {
if (item == null) {
return UpdateableRow.INDEX_NOT_FOUND;
}
int i = 0;
for (SelectItem selectItem : getIndexedSelectItemList()) {
if (item.equalsIgnoreAlias(selectItem)) {
return i;
}
i++;
}
return UpdateableRow.INDEX_NOT_FOUND;
}
public int indexOf(Column column) {
if (column == null) {
return UpdateableRow.INDEX_NOT_FOUND;
}
SelectItem selectItem = new SelectItem(column);
return indexOf(selectItem);
}
public SelectItem[] getSelectItems() {
List<SelectItem> list = getIndexedSelectItemList();
SelectItem[] result = new SelectItem[list.size()];
return list.toArray(result);
}
public Style getStyle(SelectItem item) {
return getStyle(indexOf(item));
}
public Style getStyle(Column column) {
return getStyle(indexOf(column));
}
public Style getStyle(int index) throws IndexOutOfBoundsException {
MetaRowData metaRow = getMetaRowData(index);
if (metaRow==null) {
return Style.NO_STYLE;
}
if (metaRow.style==null) {
return Style.NO_STYLE;
}
return metaRow.style;
}
public int size() {
return metaData.size();
}
}

View file

@ -0,0 +1,80 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.List;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.data.Style;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
/**
* AbstractUpdateableRow implements some setters of UpdateableRow interface.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
@SuppressWarnings("serial")
abstract public class AbstractUpdateableRow extends AbstractRow implements UpdateableRow {
// TODO: rm / move getValue and indexOf to AbstractRow after UpdateableRow interface change
public Object getValue(String columnName) {
return getValue(indexOf(columnName));
}
public int indexOf(String columnName) {
if (columnName==null) {
return UpdateableRow.INDEX_NOT_FOUND;
}
int index = 0;
for (SelectItem si:getIndexedSelectItemList()) {
if (si.getColumn().getName().equals(columnName)) {
return index;
}
index++;
}
return UpdateableRow.INDEX_NOT_FOUND;
}
public void setValue(Row row) {
for (SelectItem selectItem:row.getSelectItems()) {
Object value = row.getValue(selectItem);
setValue(indexOf(selectItem),value);
}
}
public void setValue(String columnName,Object object) {
setValue(indexOf(columnName), object);
}
public void setValue(SelectItem selectItem,Object object) {
setValue(indexOf(selectItem),object);
}
public void setValue(Column column,Object object) {
setValue(indexOf(column),object);
}
public String[] getPrimaryKeys() {
List<String> primaryKeys = getPrimaryKeysList();
return primaryKeys.toArray(new String[primaryKeys.size()]);
}
public Row getSubSelectionFromImpl(AbstractUpdateableRow rowImpl,SelectItem[] selectItems) {
for (SelectItem selectItem:selectItems) {
int index = rowImpl.indexOf(selectItem);
Object value = null;
Style style = null;
if (selectItem.getSubQuerySelectItem() != null) {
value = getValue(selectItem.getSubQuerySelectItem());
style = getStyle(selectItem.getSubQuerySelectItem());
}
if (value == null) {
value = getValue(selectItem);
style = getStyle(selectItem);
}
rowImpl.setValue(index, value);
rowImpl.setStyle(index, style);
}
return rowImpl;
}
}

View file

@ -0,0 +1,35 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.schema.Table;
/**
* CrudDataContext provides Create/Update/Delete operations on row level of table.
*
* note: UpdateableDataContext can implement UpdateableRowDataContext interface to provide native support for UpdateableRow in DataSet.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
public interface CrudDataContext extends UpdateableDataContext {
/**
* Creates empty row to fill and persist.
*/
public UpdateableRow createRow(Table table);
/**
* Inserts row into table.
*/
public void persist(UpdateableRow row);
/**
* Merges row with table.
*/
public Object merge(UpdateableRow row);
/**
* Deletes row from table.
*/
public void delete(UpdateableRow row);
}

View file

@ -0,0 +1,194 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.builder.InitFromBuilder;
import org.eobjects.metamodel.query.builder.InitFromBuilderImpl;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.ColumnType;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
/**
* CrudDataContextImpl is implementation of CrudDataContext interface which provides crud operations on UpdateableDataContext
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
public class CrudDataContextImpl extends AbstractCrudDataContext implements AbstractCrudDataContext.QueryWrapDataSetPrimaryKeyHandler {
protected UpdateableDataContext dataContextDelegate = null;
protected Map<String,List<String>> tableKeys = null;
private boolean checkPrimaryKeysAllRows = false;
public CrudDataContextImpl(UpdateableDataContext dataContext) {
dataContextDelegate = dataContext;
tableKeys = new HashMap<String,List<String>>(100);
}
public void overridePrimaryKeysForTable(String table,List<String> keys) {
tableKeys.put(table, keys);
}
public List<String> getPrimaryKeysForTable(Table table) {
List<String> keys = tableKeys.get(table.getName());
if (keys!=null) {
return keys;
}
keys = new ArrayList<String>(5);
for (Column k:table.getPrimaryKeys()) {
keys.add(k.getName()); // strange getPrimaryKeys is mostly empty, check SimpleTableDef.toTable or MutableColumn.setType to fix
}
// start test hack code
if (keys.isEmpty()) {
for (Column k:table.getColumnsOfType(ColumnType.ROWID)) { // mmm still empty
keys.add(k.getName());
}
if (keys.isEmpty()) {
for (Column k:table.getColumns()) {
String colName = k.getName();
if (colName.equals("_id")) {
keys.add(colName);
} else if (colName.equalsIgnoreCase("id")) { // TODO: rm this quick hard code ID search
keys.add(colName);
}
}
}
}
// stop test hack code
if (keys.isEmpty()) {
throw new IllegalStateException("Can't work with table: "+table+" without primary key.");
}
return keys;
}
/**
* Does the actual automatic Row to UpdateableRow wrapping.
*/
@Override
public UpdateableRow wrapOrCreateToUpdateableRow(Row row,Table table) {
UpdateableRowMapImpl result = new UpdateableRowMapImpl(table,getPrimaryKeysForTable(table));
if (row==null) {
result.addSelectItems(table.getColumns()); // create new when row is null
} else {
result.addSelectItems(row.getSelectItems());
result.setValue(row); // copy all values to impl
}
return result;
}
/**
* Checks if backend dataContextDelegate has native support for UpdateableRowDataContext.
* If not then automatic wrap full DataSet data to UpdateableRow
*/
public DataSet executeQuery(Query query) throws MetaModelException {
if (dataContextDelegate instanceof UpdateableRowDataContext) {
return ((UpdateableRowDataContext) dataContextDelegate).crudExecuteQuery(this,query);
} else {
return executeQueryWrapDataSet(dataContextDelegate,query,isCheckPrimaryKeysAllRows(),this);
}
}
/**
* Checks if backend dataContextDelegate has native support for UpdateableRowDataContext.
* If so then use api to start query builder with this datacontext.
* Else use default InitFromBuilderImpl query builder.
*/
public InitFromBuilder query() {
if (dataContextDelegate instanceof UpdateableRowDataContext) {
return ((UpdateableRowDataContext) dataContextDelegate).crudCreateQuery(this);
} else {
return new InitFromBuilderImpl(this);
}
}
// === Bean properties
/**
* @return the checkPrimaryKeysAllRows
*/
public boolean isCheckPrimaryKeysAllRows() {
return checkPrimaryKeysAllRows;
}
/**
* @param checkPrimaryKeysAllRows the checkPrimaryKeysAllRows to set
*/
public void setCheckPrimaryKeysAllRows(boolean checkPrimaryKeysAllRows) {
this.checkPrimaryKeysAllRows = checkPrimaryKeysAllRows;
}
// === Start wrapper to dataContextDelegate
/**
* @see org.eobjects.metamodel.UpdateableDataContext#executeUpdate(org.eobjects.metamodel.UpdateScript)
*/
public void executeUpdate(UpdateScript arg0) {
dataContextDelegate.executeUpdate(arg0);
}
/**
* @see org.eobjects.metamodel.DataContext#getColumnByQualifiedLabel(java.lang.String)
*/
public Column getColumnByQualifiedLabel(String arg0) {
return dataContextDelegate.getColumnByQualifiedLabel(arg0);
}
/**
* @see org.eobjects.metamodel.DataContext#getDefaultSchema()
*/
public Schema getDefaultSchema() throws MetaModelException {
return dataContextDelegate.getDefaultSchema();
}
/**
* @see org.eobjects.metamodel.DataContext#getSchemaByName(java.lang.String)
*/
public Schema getSchemaByName(String arg0) throws MetaModelException {
return dataContextDelegate.getSchemaByName(arg0);
}
/**
* @see org.eobjects.metamodel.DataContext#getSchemaNames()
*/
public String[] getSchemaNames() throws MetaModelException {
return dataContextDelegate.getSchemaNames();
}
/**
* @see org.eobjects.metamodel.DataContext#getSchemas()
*/
public Schema[] getSchemas() throws MetaModelException {
return dataContextDelegate.getSchemas();
}
/**
* @see org.eobjects.metamodel.DataContext#getTableByQualifiedLabel(java.lang.String)
*/
public Table getTableByQualifiedLabel(String arg0) {
return dataContextDelegate.getTableByQualifiedLabel(arg0);
}
/**
* @see org.eobjects.metamodel.DataContext#refreshSchemas()
*/
public DataContext refreshSchemas() {
return dataContextDelegate.refreshSchemas();
}
}

View file

@ -0,0 +1,19 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.data.Style;
/**
* RowLocal is the set version of Row interface.
*
* Currently only needed setStyle to make duplicate code in Row.getSubSelection(SelectItem[] selectItems) abstract so impl can do one liner.
* other option is move all get/setStyle to one RowStyle interface as that data is almost always locally implemented.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
public interface RowLocal extends Row {
public void setStyle(int index,Style style);
}

View file

@ -0,0 +1,75 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.List;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.Table;
/**
* UpdateableRow is the setValue(...) version of Row interface.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
public interface UpdateableRow extends Row {
// TODO: move these 3 to Row interface
public SelectItem getSelectItem(int index);
public Object getValue(String columnName);
public static final int INDEX_NOT_FOUND = -1;
/**
* Returns the table
*/
public Table getTable();
/**
* Returns primary keys of table.
*/
public List<String> getPrimaryKeysList();
/**
* Returns primary keys of table.
*/
public String[] getPrimaryKeys();
/**
* Sets the value by the column name.
*
* @param columnName
* @param object
*/
public void setValue(String columnName,Object object);
/**
* Sets the value of the provided SelectItem.
*
* @param item
* @return the value that corresponds to the provided SelectItem. Can be
* null if either the value <i>is</i> null or if no value exists
* that matches the SelectItem.
*/
public void setValue(SelectItem item,Object object);
/**
* Shorthand method for setting the value of a SelectItem based on the
* provided column. Invoking this method is equivalent to invoking
* setValue(new SelectItem(column)).
*
* @param column
* @return the value of the specified column
*/
public void setValue(Column column,Object object);
/**
* Sets the value of the row at a given index
*
* @param index
* @return the value at the specified index
* @throws IndexOutOfBoundsException
* if the provided index is out of range
*/
public void setValue(int index,Object object) throws IndexOutOfBoundsException;
}

View file

@ -0,0 +1,24 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.builder.InitFromBuilder;
/**
* UpdateableRowDataContext to let DataContexts integrate CrudDataContext without wrapping data.
*
* @author Willem Cazander
* @version 1.0 May 24, 2012
*/
public interface UpdateableRowDataContext {
/**
* Gets called by executeQuery from crud to this impl which knows how to return UpdateableRow DataSet.
*/
public DataSet crudExecuteQuery(CrudDataContext crudDataContext,Query query);
/**
* Start the query builder with correct data context for execute call back.
*/
public InitFromBuilder crudCreateQuery(CrudDataContext crudDataContext);
}

View file

@ -0,0 +1,60 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Table;
/**
* UpdateableRowMapImpl is basic implementation of UpdateableRow interface.
*
* @author Willem Cazander
* @version 1.0 May 23, 2012
*/
public class UpdateableRowMapImpl extends AbstractUpdateableRow {
private static final long serialVersionUID = 7660759661023194759L;
private Map<Integer,Object> dataMap = null;
private Table table = null;
private List<String> primaryKeys = null;
public UpdateableRowMapImpl(Table table,List<String> primaryKeys) {
this(table,primaryKeys,null);
}
public UpdateableRowMapImpl(Table table,List<String> primaryKeys,SelectItem[] selectItems) {
if (table==null) { throw new NullPointerException("Can't work with null table."); }
if (primaryKeys==null) { throw new NullPointerException("Can't work with null primaryKeys."); }
this.dataMap = new HashMap<Integer,Object>(30);
this.table=table;
this.primaryKeys = primaryKeys;
if (selectItems!=null) {
addSelectItems(selectItems);
}
}
// interface
public Table getTable() {
return table;
}
public List<String> getPrimaryKeysList() {
return primaryKeys;
}
public void setValue(int index, Object value) throws IndexOutOfBoundsException {
dataMap.put(index, value);
}
public Object getValue(int index) throws IndexOutOfBoundsException {
return dataMap.get(index);
}
public Row getSubSelection(SelectItem[] selectItems) {
return getSubSelectionFromImpl(new UpdateableRowMapImpl(table,primaryKeys,selectItems),selectItems);
}
}

View file

@ -0,0 +1,14 @@
package net.forwardfire.vasc.backend.metamodel.jndi;
import org.eobjects.metamodel.DataContext;
/**
* DataContextProvider will create of fetch an DataContext for use.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public interface DataContextProvider {
public DataContext getDataContext();
}

View file

@ -0,0 +1,118 @@
package net.forwardfire.vasc.backend.metamodel.jndi;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiCsvDataContextLoader;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiDataContextLoader;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiDataContextLoaderConfig;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiJdbcDataContextLoader;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiMongodbDataContextLoader;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiXmlDomDataContextLoader;
import net.forwardfire.vasc.backend.metamodel.jndi.loader.JndiXmlSaxDataContextLoader;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.UpdateableDataContext;
/**
* JndiDataContextObjectFactory provides data contexts on application scope level for
* all (EE) servers which provides java naming support.
*
* Tomcat context.xml config example;
* <pre>
* <Resource name="metamodel/databaseExampleDC" auth="Container" type="org.eobjects.metamodel.DataContext"
* factory="net.forwardfire.vasc.backend.metamodel.jndi.JndiDataContextObjectFactory"
* backendType="jdbcJndi" backendUrl="java:jdbc/databaseExampleDS"
* />
* </pre>
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiDataContextObjectFactory implements ObjectFactory {
protected DataContext dataContext = null;
public enum BackendType {
jdbcJndi,jdbc,mongodb,mongodbJndi,csvFile,csvUrl,xmlDomFile,xmlDomUrl,xmlSaxFile,xmlSaxUrl,providerClass
}
/**
* @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable)
*/
synchronized public Object getObjectInstance(Object obj, Name namePath, Context nameCtx,Hashtable<?, ?> env) throws Exception {
// Create only once.
if (dataContext!=null) {
return dataContext;
}
// Create objectName to print with errors
StringBuffer buf = new StringBuffer(40);
for (int i=0;i<namePath.size();i++) {
buf.append(namePath.get(i));
}
String objectName = buf.toString(); // note: print only last part of full name path
// Wrap to loader and config bean
JndiDataContextLoaderConfig config = new JndiDataContextLoaderConfig(obj,namePath,nameCtx,env,objectName);
BackendType type = BackendType.valueOf(config.getBackendType());
JndiDataContextLoader loader = null;
// Config loader
switch (type) {
default: throw new IllegalStateException("Can't handle unimplemented backendType: "+config.getBackendType());
case jdbcJndi: loader = new JndiJdbcDataContextLoader(); break;
case jdbc: loader = new JndiJdbcDataContextLoader(false); break;
case mongodb: loader = new JndiMongodbDataContextLoader(); break;
case mongodbJndi: loader = new JndiMongodbDataContextLoader(true); break;
case csvFile: loader = new JndiCsvDataContextLoader(); break;
case csvUrl: loader = new JndiCsvDataContextLoader(true); break;
case xmlDomFile: loader = new JndiXmlDomDataContextLoader(); break;
case xmlDomUrl: loader = new JndiXmlDomDataContextLoader(true); break;
case xmlSaxFile: loader = new JndiXmlSaxDataContextLoader(); break;
case xmlSaxUrl: loader = new JndiXmlSaxDataContextLoader(true); break;
case providerClass:
dataContext = createDataContextProvider(objectName,config.getBackendClass()).getDataContext();
break;
}
// Load data context except when using the providerClass type.
if (loader!=null) {
dataContext = loader.loadDataContext(config);
}
// Make data context read only is requested by config of factory.
if ("true".equalsIgnoreCase(config.getBackendReadOnly()) && dataContext instanceof UpdateableDataContext) {
dataContext = new JndiReadOnlyDataContext(dataContext);
}
// We are done lets return
return dataContext;
}
/**
* Load DataContextProvider from class path to provide data context.
*/
protected DataContextProvider createDataContextProvider(String objectName,String className) {
if (className==null) {
throw new IllegalArgumentException("Can't work with null backendClass attribute for: "+objectName);
}
if (className.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty backendClass attribute for: "+objectName);
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl==null) {
cl = getClass().getClassLoader();
}
try {
Class<?> providerClass = cl.loadClass(className);
DataContextProvider dataContextProvider = (DataContextProvider)providerClass.newInstance();
return dataContextProvider;
} catch (Exception e) {
throw new IllegalStateException("Could not create provider of: "+className,e);
}
}
}

View file

@ -0,0 +1,45 @@
package net.forwardfire.vasc.backend.metamodel.jndi;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.eobjects.metamodel.DataContext;
/**
* JndiDataContextProvider retreives a DataContext from the jndi tree by the jndiName.
*
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiDataContextProvider implements DataContextProvider {
private String jndiName = null;
/**
* Returns an DataContext object from the jndi tree.
*/
public DataContext getDataContext() {
try {
InitialContext context = new InitialContext();
DataContext dataContext = (DataContext)context.lookup(jndiName);
return dataContext;
} catch (NamingException e) {
throw new IllegalStateException("Jndi naming error on: "+jndiName,e);
}
}
/**
* @return the jndiName
*/
public String getJndiName() {
return jndiName;
}
/**
* @param jndiName the jndiName to set
*/
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}

View file

@ -0,0 +1,50 @@
package net.forwardfire.vasc.backend.metamodel.jndi;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.query.Query;
import org.eobjects.metamodel.query.builder.InitFromBuilder;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
/**
* JndiReadOnlyDataContext makes DataContext read only by removing the UpdateableDataContext interface.
*
* @author Willem Cazanders
* @version 1.0 May 22, 2012
*/
public class JndiReadOnlyDataContext /* extends DataContextProxy */ implements DataContext {
private DataContext proxyDataContext = null;
public JndiReadOnlyDataContext(DataContext proxyDataContext) {
this.proxyDataContext=proxyDataContext;
}
public DataSet executeQuery(Query arg0) throws MetaModelException {
return proxyDataContext.executeQuery(arg0);
}
public Column getColumnByQualifiedLabel(String arg0) {
return proxyDataContext.getColumnByQualifiedLabel(arg0);
}
public Schema getDefaultSchema() throws MetaModelException {
return proxyDataContext.getDefaultSchema();
}
public Schema getSchemaByName(String arg0) throws MetaModelException {
return proxyDataContext.getSchemaByName(arg0);
}
public String[] getSchemaNames() throws MetaModelException {
return proxyDataContext.getSchemaNames();
}
public Schema[] getSchemas() throws MetaModelException {
return proxyDataContext.getSchemas();
}
public Table getTableByQualifiedLabel(String arg0) {
return proxyDataContext.getTableByQualifiedLabel(arg0);
}
public InitFromBuilder query() {
return proxyDataContext.query();
}
public DataContext refreshSchemas() {
return proxyDataContext.refreshSchemas();
}
}

View file

@ -0,0 +1,71 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import java.util.Map;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.csv.CsvConfiguration;
import org.eobjects.metamodel.csv.CsvDataContext;
/**
* JndiCsvDataContextLoader Loads and confis csv DataContext from jndi loader config.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiCsvDataContextLoader implements JndiDataContextLoader {
private boolean useUrlSource = false;
public JndiCsvDataContextLoader() {
}
public JndiCsvDataContextLoader(boolean useUrlSource) {
setUseUrlSource(useUrlSource);
}
public DataContext loadDataContext(JndiDataContextLoaderConfig config) {
if (useUrlSource) {
return new CsvDataContext(config.checkBackendUrl(),createCsvConfig(config.getBackendParameters()));
} else {
return new CsvDataContext(config.checkFile(),createCsvConfig(config.getBackendParameters()));
}
}
protected CsvConfiguration createCsvConfig(Map<String,String> param) {
int columnNameLineNumber = 0;
String encoding = "UTF-8";
char separatorChar = CsvConfiguration.DEFAULT_SEPARATOR_CHAR;
char quoteChar = CsvConfiguration.DEFAULT_QUOTE_CHAR;
char escapeChar = CsvConfiguration.DEFAULT_ESCAPE_CHAR;
boolean failOnInconsistentRowLength = false;
if (param.containsKey("fileEnconding")) {
encoding = param.get("fileEnconding");
}
if (param.containsKey("separatorChar")) {
separatorChar = param.get("separatorChar").charAt(0);
}
if (param.containsKey("quoteChar")) {
quoteChar = param.get("quoteChar").charAt(0);
}
if (param.containsKey("escapeChar")) {
escapeChar = param.get("escapeChar").charAt(0);
}
if (param.containsKey("failOnInconsistentRowLength")) {
failOnInconsistentRowLength = true;
}
return new CsvConfiguration(columnNameLineNumber,encoding,separatorChar,quoteChar,escapeChar,failOnInconsistentRowLength);
}
/**
* @return the useUrlSource
*/
public boolean isUseUrlSource() {
return useUrlSource;
}
/**
* @param useUrlSource the useUrlSource to set
*/
public void setUseUrlSource(boolean useUrlSource) {
this.useUrlSource = useUrlSource;
}
}

View file

@ -0,0 +1,17 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import org.eobjects.metamodel.DataContext;
/**
* JndiDataContextLoader loads and config the DataContext(DC) from the loader config.
* This makes the jndi factory class independent from all MetaModel DC classes.
* So we can have one factory for all DC without needing all DC libs in classpath when
* most are not needed if using only one or two different DC's.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public interface JndiDataContextLoader {
public DataContext loadDataContext(JndiDataContextLoaderConfig config);
}

View file

@ -0,0 +1,238 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
/**
* JndiDataContextLoaderConfig hold generic parameters to config the loader and DataContext.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiDataContextLoaderConfig {
public final static String BACKEND_TYPE = "backendType";
public final static String BACKEND_FILE = "backendFile";
public final static String BACKEND_URL = "backendUrl";
public final static String BACKEND_HOST = "backendHost";
public final static String BACKEND_PORT = "backendPort";
public final static String BACKEND_DB = "backendDatabase";
public final static String BACKEND_USER = "backendUsername";
public final static String BACKEND_PASS = "backendPassword";
public final static String BACKEND_CLASS= "backendClass";
public final static String BACKEND_READ_ONLY = "backendReadOnly";
private Object jndiObject = null;
private Name jndiNamePath = null;
private Context jndiContext = null;
private Hashtable<?, ?> jndiEnv = null;
private String jndiObjectName = null;
private String backendType = null;
private String backendFile = null;
private String backendUrl = null;
private String backendHost = null;
private String backendPort = null;
private String backendDatabase = null;
private String backendUsername = null;
private String backendPassword = null;
private String backendClass = null;
private String backendReadOnly = null;
private Map<String,String> backendParameters = null;
public JndiDataContextLoaderConfig(Object jndiObject,Name jndiNamePath,Context jndiContext,Hashtable<?, ?> jndiEnv,String jndiObjectName) {
this.jndiObject=jndiObject;
this.jndiNamePath=jndiNamePath;
this.jndiContext=jndiContext;
this.jndiEnv=jndiEnv;
this.jndiObjectName=jndiObjectName;
initBackendParameters();
initBeanParameters();
}
protected void initBackendParameters() {
// Copy config parameters of this factory instance to map
backendParameters = new HashMap<String,String>(30);
Reference ref = (Reference) getJndiObject();
Enumeration<?> addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = (RefAddr) addrs.nextElement();
String name = addr.getType();
String value = (String) addr.getContent();
backendParameters.put(name,value);
}
}
protected void initBeanParameters() {
// Check out most generic parameters
backendType = backendParameters.get(BACKEND_TYPE);
backendFile = backendParameters.get(BACKEND_FILE);
backendUrl = backendParameters.get(BACKEND_URL);
backendHost = backendParameters.get(BACKEND_HOST);
backendPort = backendParameters.get(BACKEND_PORT);
backendDatabase = backendParameters.get(BACKEND_DB);
backendUsername = backendParameters.get(BACKEND_USER);
backendPassword = backendParameters.get(BACKEND_PASS);
backendClass = backendParameters.get(BACKEND_CLASS);
backendReadOnly = backendParameters.get(BACKEND_READ_ONLY);
if (backendHost==null) {
backendHost = "localhost";
}
if (backendHost.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty "+BACKEND_HOST+" attribute.");
}
}
public URL checkBackendUrl() {
if (backendUrl==null) {
throw new IllegalArgumentException("Can't work with null "+BACKEND_URL+" attribute for: "+jndiObjectName);
}
if (backendUrl.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty "+BACKEND_URL+" attribute for: "+jndiObjectName);
}
try {
return new URL(backendUrl);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Could not convert "+backendUrl+" to url.",e);
}
}
public File checkFile() {
if (backendFile==null) {
throw new IllegalArgumentException("Can't work with null "+BACKEND_FILE+" attribute for: "+jndiObjectName);
}
if (backendFile.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty "+BACKEND_FILE+" attribute for: "+jndiObjectName);
}
File file = new File(backendFile);
if (file.exists()==false) {
throw new IllegalStateException("File not found: "+file.getName()+" for: "+jndiObjectName);
}
if (file.canRead()==false) {
throw new IllegalStateException("Can't read file: "+file+" for: "+jndiObjectName);
}
return file;
}
/**
* @return the jndiObject
*/
public Object getJndiObject() {
return jndiObject;
}
/**
* @return the jndiNamePath
*/
public Name getJndiNamePath() {
return jndiNamePath;
}
/**
* @return the jndiContext
*/
public Context getJndiContext() {
return jndiContext;
}
/**
* @return the jndiEnv
*/
public Hashtable<?, ?> getJndiEnv() {
return jndiEnv;
}
/**
* @return the jndiObjectName
*/
public String getJndiObjectName() {
return jndiObjectName;
}
/**
* @return the backendType
*/
public String getBackendType() {
return backendType;
}
/**
* @return the backendFile
*/
public String getBackendFile() {
return backendFile;
}
/**
* @return the backendUrl
*/
public String getBackendUrl() {
return backendUrl;
}
/**
* @return the backendHost
*/
public String getBackendHost() {
return backendHost;
}
/**
* @return the backendPort
*/
public String getBackendPort() {
return backendPort;
}
/**
* @return the backendDatabase
*/
public String getBackendDatabase() {
return backendDatabase;
}
/**
* @return the backendUsername
*/
public String getBackendUsername() {
return backendUsername;
}
/**
* @return the backendPassword
*/
public String getBackendPassword() {
return backendPassword;
}
/**
* @return the backendClass
*/
public String getBackendClass() {
return backendClass;
}
/**
* @return the backendReadOnly
*/
public String getBackendReadOnly() {
return backendReadOnly;
}
/**
* @return the backendParameters
*/
public Map<String, String> getBackendParameters() {
return backendParameters;
}
}

View file

@ -0,0 +1,90 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import java.sql.Connection;
import java.sql.DriverManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.jdbc.JdbcDataContext;
/**
* JndiCsvDataContextLoader Loads and config jdbc DataContext from jndi loader config.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiJdbcDataContextLoader implements JndiDataContextLoader {
private boolean useDataSource = true;
public JndiJdbcDataContextLoader() {
}
public JndiJdbcDataContextLoader(boolean useDataSource) {
setUseDataSource(useDataSource);
}
public DataContext loadDataContext(JndiDataContextLoaderConfig config) {
if (useDataSource) {
if (config.getBackendUrl()==null) {
throw new IllegalArgumentException("Can't work with null "+JndiDataContextLoaderConfig.BACKEND_URL+" attribute for: "+config.getJndiObjectName());
}
if (config.getBackendUrl().isEmpty()) {
throw new IllegalArgumentException("Can't work with empty "+JndiDataContextLoaderConfig.BACKEND_URL+" attribute for: "+config.getJndiObjectName());
}
return new JdbcDataContext(getDataSource(config.getJndiObjectName(),config.getBackendUrl()));
} else {
config.checkBackendUrl();
return new JdbcDataContext(getConnection(
config.getJndiObjectName(),
config.getBackendClass(),
config.getBackendUrl(),
config.getBackendUsername(),
config.getBackendPassword()
));
}
}
protected Connection getConnection(String objectName,String driverClass,String url,String user,String pass) {
try {
if (driverClass!=null && driverClass.isEmpty()==false) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl==null) {
cl = this.getClass().getClassLoader();
}
cl.loadClass(driverClass);
}
Connection connection = DriverManager.getConnection(url,user,pass);
return connection;
} catch (Exception e) {
throw new RuntimeException("Could not get jdbc connection to: "+url+" for: "+objectName,e);
}
}
protected DataSource getDataSource(String objectName,String jndiName) {
try {
InitialContext context = new InitialContext();
DataSource datasource = (DataSource)context.lookup(jndiName);
return datasource;
} catch (NamingException e) {
throw new IllegalStateException("Jndi naming error on: "+jndiName+" for: "+objectName,e);
}
}
/**
* @return the useDataSource
*/
public boolean isUseDataSource() {
return useDataSource;
}
/**
* @param useDataSource the useDataSource to set
*/
public void setUseDataSource(boolean useDataSource) {
this.useDataSource = useDataSource;
}
}

View file

@ -0,0 +1,106 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import java.net.UnknownHostException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.mongodb.MongoDbDataContext;
import org.eobjects.metamodel.mongodb.MongoDbDataContextBean;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
/**
* JndiCsvDataContextLoader Loads and config mongodb DataContext from jndi loader config.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiMongodbDataContextLoader implements JndiDataContextLoader {
private boolean useJndiResource = false;
public JndiMongodbDataContextLoader() {
}
public JndiMongodbDataContextLoader(boolean useJndiResource) {
setUseJndiResource(useJndiResource);
}
public DataContext loadDataContext(JndiDataContextLoaderConfig config) {
MongoDbDataContextBean result = null;
if (useJndiResource) {
config.checkBackendUrl();
result = new MongoDbDataContextBean(getMongodbJdni(config.getJndiObjectName(),config.getBackendUrl()));
} else {
int mongoPort = 27017;
if (config.getBackendPort()!=null && config.getBackendPort().isEmpty()==false) {
mongoPort = new Integer(config.getBackendPort());
}
result = new MongoDbDataContextBean(getMongodbConnection(
config.getJndiObjectName(),
config.getBackendHost(),
mongoPort,
config.getBackendDatabase(),
config.getBackendUsername(),
config.getBackendPassword()
));
}
result.setRegisterMBean(true); // TODO: make flag
result.start();
return result;
}
protected DB getMongodbJdni(String objectName,String jndiName) {
try {
InitialContext context = new InitialContext();
DB db = (DB)context.lookup(jndiName);
return db;
} catch (NamingException e) {
throw new IllegalStateException("Jndi naming error on: "+jndiName+" for: "+objectName,e);
}
}
protected DB getMongodbConnection(String objectName,String hostname,int port,String database,String username,String password) {
if (database==null) {
throw new IllegalArgumentException("Can't work with null "+JndiDataContextLoaderConfig.BACKEND_DB+" attribute for: "+objectName);
}
if (database.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty "+JndiDataContextLoaderConfig.BACKEND_DB+" attribute for: "+objectName);
}
ServerAddress server;
try {
server = new ServerAddress(hostname,port);
} catch (UnknownHostException e) {
throw new IllegalStateException("Could not create hostname from: "+hostname+" for: "+objectName,e);
}
MongoOptions options = new MongoOptions();
Mongo mongo = new Mongo(server,options);
DB db = mongo.getDB(database);
if (username!=null && password!=null) {
boolean auth = db.authenticate(username, password.toCharArray());
if (auth==false) {
throw new IllegalStateException("Could not auth to db: "+database+" with username: "+username+" for: "+objectName);
}
}
return db;
}
/**
* @return the useJndiResource
*/
public boolean isUseJndiResource() {
return useJndiResource;
}
/**
* @param useJndiResource the useJndiResource to set
*/
public void setUseJndiResource(boolean useJndiResource) {
this.useJndiResource = useJndiResource;
}
}

View file

@ -0,0 +1,47 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.xml.XmlDomDataContext;
/**
* JndiCsvDataContextLoader Loads and config xml dom DataContext from jndi loader config.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiXmlDomDataContextLoader implements JndiDataContextLoader {
private boolean useUrlSource = false;
public JndiXmlDomDataContextLoader() {
}
public JndiXmlDomDataContextLoader(boolean useUrlSource) {
setUseUrlSource(useUrlSource);
}
public DataContext loadDataContext(JndiDataContextLoaderConfig config) {
boolean autoFlattenTables = true;
if ("false".equalsIgnoreCase(config.getBackendParameters().get("autoFlattenTables"))) {
autoFlattenTables = false;
}
if (useUrlSource) {
return new XmlDomDataContext(config.checkBackendUrl(),autoFlattenTables);
} else {
return new XmlDomDataContext(config.checkFile(),autoFlattenTables);
}
}
/**
* @return the useUrlSource
*/
public boolean isUseUrlSource() {
return useUrlSource;
}
/**
* @param useUrlSource the useUrlSource to set
*/
public void setUseUrlSource(boolean useUrlSource) {
this.useUrlSource = useUrlSource;
}
}

View file

@ -0,0 +1,96 @@
package net.forwardfire.vasc.backend.metamodel.jndi.loader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.util.Ref;
import org.eobjects.metamodel.xml.XmlSaxDataContext;
import org.eobjects.metamodel.xml.XmlSaxTableDef;
import org.xml.sax.InputSource;
/**
* JndiXmlSaxDataContextLoader Loads and config xml sax DataContext from jndi loader config.
*
* @author Willem Cazander
* @version 1.0 May 22, 2012
*/
public class JndiXmlSaxDataContextLoader implements JndiDataContextLoader {
private boolean useUrlSource = false;
public JndiXmlSaxDataContextLoader() {
}
public JndiXmlSaxDataContextLoader(boolean useUrlSource) {
setUseUrlSource(useUrlSource);
}
public DataContext loadDataContext(JndiDataContextLoaderConfig config) {
List<XmlSaxTableDef> tableSchemas = createXmlSaxConfig(config.getJndiObjectName(),config.getBackendParameters());
XmlSaxTableDef[] args = new XmlSaxTableDef[tableSchemas.size()];
if (useUrlSource) {
final URL url = config.checkBackendUrl();
return new XmlSaxDataContext(new Ref<InputSource>() { // TODO: missing constructor in XmlSaxDataContext
public InputSource get() {
InputStream in = null;
try {
in = url.openStream();
return new InputSource(in);
} catch (IOException e) {
throw new IllegalStateException("Could not read url; "+url,e);
} finally {
if (in!=null) {
try {
in.close();
} catch (IOException e) {
}
}
}
}
},tableSchemas.toArray(args));
} else {
return new XmlSaxDataContext(config.checkFile(),tableSchemas.toArray(args)); // copy List to array
}
}
protected List<XmlSaxTableDef> createXmlSaxConfig(String objectName,Map<String,String> param) {
String rowXPath = param.get("rowXPath");
String dataXPaths = param.get("dataXPaths");
if (rowXPath==null) {
throw new IllegalArgumentException("Can't work with null rowXPath attribute for: "+objectName);
}
if (rowXPath.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty rowXPath attribute for: "+objectName);
}
if (dataXPaths==null) {
throw new IllegalArgumentException("Can't work with null dataXPaths attribute for: "+objectName);
}
if (dataXPaths.isEmpty()) {
throw new IllegalArgumentException("Can't work with empty dataXPaths attribute for: "+objectName);
}
String[] args = dataXPaths.split(",");
List<XmlSaxTableDef> tableSchemas = new ArrayList<XmlSaxTableDef>(5);
XmlSaxTableDef tableSchema = new XmlSaxTableDef(rowXPath,args);
tableSchemas.add(tableSchema);
return tableSchemas;
}
/**
* @return the useUrlSource
*/
public boolean isUseUrlSource() {
return useUrlSource;
}
/**
* @param useUrlSource the useUrlSource to set
*/
public void setUseUrlSource(boolean useUrlSource) {
this.useUrlSource = useUrlSource;
}
}

View file

@ -0,0 +1,55 @@
package net.forwardfire.vasc.backend.metamodel.x4o;
import java.util.ArrayList;
import java.util.List;
import net.forwardfire.vasc.backend.metamodel.MetaModelDataContextXmlSax;
import org.eobjects.metamodel.xml.XmlSaxTableDef;
import org.x4o.xml.element.AbstractElement;
import org.x4o.xml.element.Element;
import org.x4o.xml.element.ElementException;
public class XmlSaxSchemaElement extends AbstractElement {
/**
* @see org.x4o.xml.element.AbstractElement#doElementRun()
*/
@Override
public void doElementRun() throws ElementException {
if (getElementClass().getTag().endsWith("Column")) {
return; // save one object to code.
}
if (getParent()==null) {
throw new ElementException("Parent element is null.");
}
if (getParent().getElementObject()==null) {
throw new ElementException("Parent object is null.");
}
if ((getParent().getElementObject() instanceof MetaModelDataContextXmlSax)==false) {
throw new ElementException("Parent object is not MetaModelDataContextXmlSax class.");
}
String rowXPath = getAttributes().get("rowXPath");
if (rowXPath==null) {
throw new ElementException("Attribute rowXPath is null.");
}
if (rowXPath.isEmpty()) {
throw new ElementException("Attribute rowXPath is empty.");
}
List<String> columns = new ArrayList<String>(15);
for (Element e:getChilderen()) {
String col = e.getAttributes().get("dataXPath");
if (col==null) {
throw new ElementException("Attribute dataXPath is null.");
}
if (col.isEmpty()) {
throw new ElementException("Attribute dataXPath is empty.");
}
columns.add(col);
}
MetaModelDataContextXmlSax saxDataContext = (MetaModelDataContextXmlSax)getParent().getElementObject();
String[] args = new String[columns.size()];
XmlSaxTableDef tableSchema = new XmlSaxTableDef(rowXPath,columns.toArray(args));
saxDataContext.addTableSchema(tableSchema);
}
}

View file

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<eld:root xmlns:eld="http://eld.x4o.org/eld/eld-lang.eld">
<eld:elementClass tag="metaModelBackend" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelVascBackend"/>
<eld:elementClass tag="mongodbDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextMongodb"/>
<eld:elementClass tag="jdbcDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextJdbc"/>
<eld:elementClass tag="csvDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextCsv"/>
<eld:elementClass tag="schemaAutoEntry" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelSchemaAutoEntry">
<eld:elementClass tag="metaModelBackend" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelVascBackend"/>
<eld:elementClass tag="mongodbDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextMongodb"/>
<eld:elementClass tag="jdbcDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextJdbc"/>
<eld:elementClass tag="jndiDataSourceDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextJndiDataSource"/>
<eld:elementClass tag="csvDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextCsv"/>
<eld:elementClass tag="xmlSaxDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextXmlSax"/>
<eld:elementClass tag="xmlSaxSchema" elementClassName="net.forwardfire.vasc.backend.metamodel.x4o.XmlSaxSchemaElement"/>
<eld:elementClass tag="xmlSaxSchemaColumn" elementClassName="net.forwardfire.vasc.backend.metamodel.x4o.XmlSaxSchemaElement"/>
<eld:elementClass tag="xmlDomDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextXmlDom"/>
<eld:elementClass tag="jndiDataContext" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelDataContextJndiDataContext"/>
<eld:elementClass tag="schemaAutoEntry" objectClassName="net.forwardfire.vasc.backend.metamodel.MetaModelSchemaAutoEntry">
<eld:elementConfigurator bean.class="net.forwardfire.vasc.backend.metamodel.x4o.SchemaAutoEntryElementConfigurator" configAction="true"/>
</eld:elementClass>

View file

@ -0,0 +1,67 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import junit.framework.TestCase;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.jdbc.JdbcDataContext;
import org.eobjects.metamodel.schema.Table;
public class BravoTest extends TestCase {
private Connection conn;
@Override
protected void setUp() throws Exception {
super.setUp();
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection("jdbc:postgresql://localhost/openbravo","postgres","postgresql");
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
conn.close();
}
public void testDataTypeBoolean() throws Exception {
/*
Statement s = conn.createStatement();
s.executeUpdate("DROP TABLE test_type_bool IF EXISTS;");
s.executeUpdate("CREATE TABLE test_type_bool (id IDENTITY not null primary key,name varchar not null,active boolean not null);");
s.executeUpdate("INSERT INTO test_type_bool VALUES(1,\'name\',true);");
// Test working data first
JdbcDataContext dc = new JdbcDataContext(conn);
Table table = dc.getDefaultSchema().getTableByName("TEST_TYPE_BOOL");
assertNotNull(table);
DataSet ds = dc.query().from(table).select(table.getColumns()).execute();
assertTrue(ds.next());
ds = dc.query().from(table).selectCount().execute();
assertTrue(ds.next());
// Now let it fail
s.executeUpdate("DROP TABLE test_type_bool IF EXISTS;");
s.executeUpdate("CREATE TABLE test_type_bool (id IDENTITY not null primary key,name varchar not null,active boolean not null);");
s.executeUpdate("INSERT INTO test_type_bool VALUES(1,\'\',true);");
// Make sure it works from jdbc driver
Boolean active = null;
s.execute("SELECT * FROM TEST_TYPE_BOOL");
ResultSet rs = s.getResultSet();
while (rs.next()) {
active = rs.getBoolean(3); // this should be same method as exception
}
assertTrue(active);
*/
// Fails on execute.
long startTime = System.currentTimeMillis();
JdbcDataContext dc = new JdbcDataContext(conn);
long stopTime = System.currentTimeMillis();
System.out.println("DC init took: "+(stopTime-startTime)+" ms. for total tables: "+dc.getDefaultSchema().getTableCount());
}
}

View file

@ -0,0 +1,70 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import junit.framework.TestCase;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.jdbc.JdbcDataContext;
import org.eobjects.metamodel.schema.Table;
public class H2Test extends TestCase {
private Connection conn;
@Override
protected void setUp() throws Exception {
super.setUp();
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection("jdbc:h2:mem:");
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
conn.close();
}
public void testDataTypeBoolean() throws Exception {
// This works see diff is empty value in column before boolean column ...
Statement s = conn.createStatement();
s.executeUpdate("DROP TABLE test_type_bool IF EXISTS;");
s.executeUpdate("CREATE TABLE test_type_bool (id IDENTITY not null primary key,name varchar not null,active boolean not null);");
s.executeUpdate("INSERT INTO test_type_bool VALUES(1,\'name\',true);");
// Test working data first
JdbcDataContext dc = new JdbcDataContext(conn);
Table table = dc.getDefaultSchema().getTableByName("TEST_TYPE_BOOL");
assertNotNull(table);
DataSet ds = dc.query().from(table).select(table.getColumns()).execute();
assertTrue(ds.next());
ds = dc.query().from(table).selectCount().execute();
assertTrue(ds.next());
// Now let it fail
s.executeUpdate("DROP TABLE test_type_bool IF EXISTS;");
s.executeUpdate("CREATE TABLE test_type_bool (id IDENTITY not null primary key,name varchar not null,active boolean not null);");
s.executeUpdate("INSERT INTO test_type_bool VALUES(1,\'\',true);");
// Make sure it works from jdbc driver
Boolean active = null;
s.execute("SELECT * FROM TEST_TYPE_BOOL");
ResultSet rs = s.getResultSet();
while (rs.next()) {
active = rs.getBoolean(3); // this should be same method as exception
}
assertTrue(active);
// Fails on execute.
dc = new JdbcDataContext(conn);
table = dc.getDefaultSchema().getTableByName("TEST_TYPE_BOOL");
assertNotNull(table);
ds = dc.query().from(table).select(table.getColumns()).execute();
assertTrue(ds.next());
ds = dc.query().from(table).selectCount().execute();
assertTrue(ds.next());
}
}

View file

@ -0,0 +1,38 @@
import java.io.File;
import org.eobjects.metamodel.UpdateCallback;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.csv.CsvDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.util.MutableRef;
public class SwingLoadTest {
private Table table = null;
public static void main(String[] argu) {
try {SwingLoadTest test = new SwingLoadTest();test.run(argu);} catch (Exception e) {e.printStackTrace();}
}
public void run(String[] argu) throws Exception {
File tmpFile = File.createTempFile("test-class-loading", "csv");
tmpFile.deleteOnExit();
CsvDataContext dataContext = new CsvDataContext(tmpFile);
final Schema schema = dataContext.getDefaultSchema();
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback cb) {
MutableRef<Table> tableRef = new MutableRef<Table>();
table = cb.createTable(schema, "table").withColumn("column").execute();
tableRef.set(table);
cb.insertInto(table).value(0,"data").execute();
}
});
DataSet ds = dataContext.query().from(table).select(table.getColumns()).execute();
while (ds.next()) {
Row row = ds.getRow();
System.out.println("Row value contains "+row.getValue(0));
}
ds.close();
}
}

View file

@ -0,0 +1,36 @@
package net.forwardfire.vasc.backend.metamodel.bundle;
import java.io.File;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.csv.CsvDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.schema.Table;
import junit.framework.TestCase;
public class ResouceBundleTest extends TestCase {
public void testBundle() {
final String tableName = "mm-bundle-test"; // Setup some small test data in tmp
File testFile = new File(System.getProperty("java.io.tmpdir")+File.separatorChar+tableName+".properties");
final boolean createTable = testFile.exists()==false;
DataContext dataContext = new ResourceBundleDataContext(testFile);
assertTrue(dataContext instanceof UpdateableDataContext);
for (String t:dataContext.getDefaultSchema().getTableNames()) {
System.out.println("table: '"+t+"'");
}
Table table = dataContext.getDefaultSchema().getTableByName("en");
assertNotNull(table);
DataSet ds = dataContext.query().from(table).select(table.getColumns()).execute();
while (ds.next()) {
Row row = ds.getRow();
System.out.println("row: "+row.getValue(0)+" "+row.getValue(1)+" "+row.getValue(2));
}
}
}

View file

@ -0,0 +1,97 @@
package net.forwardfire.vasc.backend.metamodel.crud;
import java.io.File;
import org.eobjects.metamodel.DataContext;
import org.eobjects.metamodel.UpdateCallback;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.csv.CsvDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.data.Row;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.util.MutableRef;
import junit.framework.TestCase;
public class CrudContextTest extends TestCase {
public void testCrud() {
final String tableName = "test-crud"; // Setup some small test data in tmp
File testFile = new File(System.getProperty("java.io.tmpdir")+File.separatorChar+tableName+".csv");
final boolean createTable = testFile.exists()==false;
CsvDataContext dataContext = new CsvDataContext(testFile);
assertTrue(dataContext instanceof UpdateableDataContext);
final Schema schema = dataContext.getDefaultSchema();
final MutableRef<Table> tableRef = new MutableRef<Table>();
dataContext.executeUpdate(new UpdateScript() {
public void run(UpdateCallback cb) {
Table table = null;
int idx = 0;
if (createTable) {
table = cb.createTable(schema, tableName).withColumn("id").withColumn("name").withColumn("active").execute();
tableRef.set(table);
} else {
table = cb.getDataContext().getDefaultSchema().getTableByName(tableName);
idx = generateNextId(cb.getDataContext(),table);
}
cb.insertInto(table).value(0,idx+1).value(1, "foo").value(2, true).execute();
cb.insertInto(table).value(0,idx+2).value(1, "foo").value(2, false).execute();
cb.insertInto(table).value(0,idx+3).value(1, "bar").value(2, false).execute();
cb.insertInto(table).value(0,idx+4).value(1, "bar").value(2, true).execute();
cb.insertInto(table).value(0,idx+5).value(1, "object").value(2, true).execute();
}
});
// Wrap to CrudDataContext and get table to test with.
CrudDataContext crudDataContext = new CrudDataContextImpl((UpdateableDataContext)dataContext);
Table table = crudDataContext.getDefaultSchema().getTableByName(tableName);
assertNotNull(table);
// Fetch and update data
DataSet ds = crudDataContext.query().from(table).select(table.getColumns()).execute();
while (ds.next()) {
Row row = ds.getRow();
if (row instanceof UpdateableRow) {
UpdateableRow rowCrud = (UpdateableRow)row;
if (rowCrud.getValue("name")!=null && rowCrud.getValue("name").toString().startsWith("test")==false) {
rowCrud.setValue("active", false);
crudDataContext.merge(rowCrud);
}
}
}
ds.close();
// Lets add an record.
UpdateableRow newRow = crudDataContext.createRow(table);
newRow.setValue("id", generateNextId(crudDataContext,table)); // Leave key out on jdbc/mongo backends for sequence/auto id.
newRow.setValue("name", "test date "+System.currentTimeMillis());
newRow.setValue("active", true);
crudDataContext.persist(newRow);
// Delete some data.
DataSet dsDel = crudDataContext.query().from(table).select(table.getColumns()).where("name").equals("foo").or("name").equals("bar").execute();
while (dsDel.next()) {
crudDataContext.delete((UpdateableRow)dsDel.getRow());
}
dsDel.close();
//testFile.deleteOnExit();
}
private int generateNextId(DataContext dataContext,Table table) {
//DataSet dsMax = dataContext.query().from(table).select(FunctionType.MAX, table.getColumn(0)).execute();
//assertTrue(dsMax.next());
//Row rowMax = dsMax.getRow();
//int recordId = new Integer(rowMax.getValue(0).toString()); // MAX stops on first row ?
//dsMax.close();
int recordId = 0;
DataSet ds = dataContext.query().from(table).select(table.getColumns()).execute();
while (ds.next()) {
Row row = ds.getRow();
int id = new Integer(row.getValue(0).toString());
if (id > recordId) {
recordId = id;
}
}
return recordId + 1; // Fetch new ID because CSV has no auto ID
}
}

View file

@ -18,7 +18,7 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.7.2</version>
<version>${mongo-java-driver.version}</version>
</dependency>
</dependencies>
</project>