use of com.manydesigns.portofino.model.Annotation in project Portofino by ManyDesigns.
the class TablesAction method getAnnotations.
@Path("{db}/{schema}/{table}/{column}/annotations/{typeName}")
@GET
public String getAnnotations(@PathParam("db") String db, @PathParam("schema") String schema, @PathParam("table") String tableName, @PathParam("column") String columnName, @PathParam("typeName") String typeName) throws ClassNotFoundException {
Column column = DatabaseLogic.findColumnByName(persistence.getModel(), db, schema, tableName, columnName);
if (column == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
Class<?> type = getColumnType(column, typeName);
MutableClassAccessor classAccessor = getApplicableAnnotations(type);
JSONStringer jsonStringer = new JSONStringer();
jsonStringer.object();
jsonStringer.key("classAccessor");
ReflectionUtil.classAccessorToJson(classAccessor, jsonStringer);
jsonStringer.key("annotations");
jsonStringer.object();
column.getAnnotations().forEach(a -> {
String annType = a.getType();
if (DATE_FORMAT.equals(annType)) {
jsonStringer.key("dateFormat");
} else if (DECIMAL_FORMAT.equals(annType)) {
jsonStringer.key("decimalFormat");
} else if (FIELD_SIZE.equals(annType)) {
jsonStringer.key("fieldSize");
} else if (FILE_BLOB.equals(annType)) {
jsonStringer.key("fileBlob");
} else if (HIGHLIGHT_LINKS.equals(annType)) {
jsonStringer.key("highlightLinks");
} else if (MIN_INT_VALUE.equals(annType)) {
jsonStringer.key("minValue");
} else if (MIN_DECIMAL_VALUE.equals(annType)) {
jsonStringer.key("minValue");
} else if (MAX_INT_VALUE.equals(annType)) {
jsonStringer.key("maxValue");
} else if (MAX_DECIMAL_VALUE.equals(annType)) {
jsonStringer.key("maxValue");
} else if (MULTILINE.equals(annType)) {
jsonStringer.key("typeOfContent");
jsonStringer.object();
jsonStringer.key("v").value(annType);
jsonStringer.key("l").value("Multiline");
jsonStringer.key("s").value(true);
jsonStringer.endObject();
return;
} else if (REGEXP.equals(annType)) {
jsonStringer.key("regexp");
jsonStringer.value(a.getProperty("value").getValue());
return;
} else if (RICH_TEXT.equals(annType)) {
jsonStringer.key("typeOfContent");
jsonStringer.object();
jsonStringer.key("v").value(annType);
jsonStringer.key("l").value("Rich text");
jsonStringer.key("s").value(true);
jsonStringer.endObject();
return;
} else if (STRING_FORMAT.containsKey(annType)) {
jsonStringer.key("stringFormat");
jsonStringer.object();
jsonStringer.key("v").value(annType);
jsonStringer.key("l").value(STRING_FORMAT.get(annType));
jsonStringer.key("s").value(true);
jsonStringer.endObject();
return;
} else if (DATABASE_BLOB.equals(annType)) {
jsonStringer.key("databaseBlobContentTypeProperty").value(a.getProperty("contentTypeProperty").getValue());
jsonStringer.key("databaseBlobFileNameProperty").value(a.getProperty("fileNameProperty").getValue());
jsonStringer.key("databaseBlobTimestampProperty").value(a.getProperty("timestampProperty").getValue());
return;
} else {
jsonStringer.key("annotation");
jsonStringer.object();
jsonStringer.key("type").value(annType);
jsonStringer.key("properties");
jsonStringer.array();
a.getProperties().forEach(p -> jsonStringer.key(p.getName()).value(p.getValue()));
jsonStringer.endArray();
jsonStringer.endObject();
return;
}
if (a.getProperties().size() > 1) {
jsonStringer.object();
for (Property p : a.getProperties()) {
jsonStringer.key(p.getName()).value(p.getValue());
}
jsonStringer.endObject();
} else {
jsonStringer.value(a.getProperties().get(0).getValue());
}
});
jsonStringer.endObject();
jsonStringer.endObject();
return jsonStringer.toString();
}
use of com.manydesigns.portofino.model.Annotation in project Portofino by ManyDesigns.
the class TablesAction method saveColumn.
@Path("{db}/{schema}/{table}/{column}")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void saveColumn(@PathParam("db") String db, @PathParam("schema") String schema, @PathParam("table") String tableName, @PathParam("column") String columnName, ColumnAndAnnotations column) throws Exception {
Column existing = DatabaseLogic.findColumnByName(persistence.getModel(), db, schema, tableName, columnName);
if (existing == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
Table table = existing.getTable();
Class<?> type = getColumnType(existing, existing.getJavaType());
Form annotationsForm = new FormBuilder(getApplicableAnnotations(type)).build();
MapKeyValueAccessor annotationsAccessor = new MapKeyValueAccessor(column.getAnnotations());
annotationsForm.readFrom(annotationsAccessor);
if (annotationsForm.validate()) {
BeanUtils.copyProperties(column.getColumn(), existing);
existing.setTable(table);
existing.getAnnotations().removeIf(a -> KNOWN_ANNOTATIONS.contains(a.getType()));
Set<Map.Entry<String, Object>> set = column.getAnnotations().entrySet();
set.forEach(e -> {
if (e.getValue() == null) {
return;
}
Annotation a;
String value = e.getValue().toString();
if (StringUtils.isBlank(value)) {
return;
}
switch(e.getKey()) {
case "dateFormat":
try {
new SimpleDateFormat(value);
} catch (IllegalArgumentException ex) {
logger.error("Invalid date format: " + value, ex);
// TODO I18n
RequestMessages.addErrorMessage("Invalid date format: " + value);
break;
}
a = new Annotation(DATE_FORMAT);
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "decimalFormat":
try {
new java.text.DecimalFormat(value);
} catch (IllegalArgumentException ex) {
logger.error("Invalid decimal format: " + value, ex);
// TODO I18n
RequestMessages.addErrorMessage("Invalid decimal format: " + value);
break;
}
a = new Annotation(DECIMAL_FORMAT);
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "fieldSize":
a = new Annotation(FIELD_SIZE);
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "fileBlob":
if (Boolean.TRUE.equals(e.getValue())) {
a = new Annotation(FILE_BLOB);
existing.getAnnotations().add(a);
}
break;
case "databaseBlobContentTypeProperty":
Annotation databaseBlobAnn1 = existing.ensureAnnotation(DATABASE_BLOB);
databaseBlobAnn1.setProperty("contentTypeProperty", value);
break;
case "databaseBlobFileNameProperty":
Annotation databaseBlobAnn2 = existing.ensureAnnotation(DATABASE_BLOB);
databaseBlobAnn2.setProperty("fileNameProperty", value);
break;
case "databaseBlobTimestampProperty":
Annotation databaseBlobAnn3 = existing.ensureAnnotation(DATABASE_BLOB);
databaseBlobAnn3.setProperty("timestampProperty", value);
break;
case "highlightLinks":
a = new Annotation(HIGHLIGHT_LINKS);
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "minValue":
if (type == Integer.class || type == Long.class || type == BigInteger.class) {
a = new Annotation(MIN_INT_VALUE);
} else {
a = new Annotation(MIN_DECIMAL_VALUE);
}
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "maxValue":
if (type == Integer.class || type == Long.class || type == BigInteger.class) {
a = new Annotation(MAX_INT_VALUE);
} else {
a = new Annotation(MAX_DECIMAL_VALUE);
}
a.getProperties().add(new Property("value", value));
existing.getAnnotations().add(a);
break;
case "regexp":
a = new Annotation(REGEXP);
a.getProperties().add(new Property("value", value));
// Default error message
a.getProperties().add(new Property("errorMessage", "elements.error.field.regexp.format"));
existing.getAnnotations().add(a);
break;
case "stringFormat":
a = new Annotation(((Map) e.getValue()).get("v").toString());
existing.getAnnotations().add(a);
break;
case "typeOfContent":
a = new Annotation(((Map) e.getValue()).get("v").toString());
a.getProperties().add(new Property("value", "true"));
existing.getAnnotations().add(a);
break;
default:
String msg = "Unsupported annotation: " + e.getKey();
logger.error(msg);
// TODO i18n
RequestMessages.addErrorMessage(msg);
}
});
persistence.initModel();
persistence.saveXmlModel();
} else {
throw new WebApplicationException(Response.serverError().entity(annotationsForm).build());
}
}
use of com.manydesigns.portofino.model.Annotation in project Portofino by ManyDesigns.
the class CrudActionTest method testBlobs.
public void testBlobs() throws Exception {
MutableHttpServletRequest req = new MutableHttpServletRequest();
ElementsThreadLocals.setMultipart(req);
req.getServletContext().setInitParameter("portofino.api.root", "http://fake");
req.makeMultipart();
Column column = DatabaseLogic.findColumnByName(persistence.getModel(), "jpetstore", "PUBLIC", "PRODUCT", "DESCN");
Annotation ann = new Annotation(column, FileBlob.class.getName());
column.getAnnotations().add(ann);
persistence.initModel();
CrudAction crudAction = new CrudAction() {
public void commitTransaction() {
super.commitTransaction();
session.beginTransaction();
}
@NotNull
@Override
protected ClassAccessor filterAccordingToPermissions(ClassAccessor classAccessor) {
// Let's ignore Shiro
return classAccessor;
}
@Override
protected String getUrlEncoding() {
return PortofinoProperties.URL_ENCODING_DEFAULT;
}
};
CrudConfiguration configuration = new CrudConfiguration();
configuration.setDatabase("jpetstore");
configuration.setQuery("from product");
String metaFilenamePattern = "blob-{0}.properties";
String dataFilenamePattern = "blob-{0}.data";
crudAction.blobManager = new HierarchicalBlobManager(new File(System.getProperty("java.io.tmpdir")), metaFilenamePattern, dataFilenamePattern);
CrudProperty property = new CrudProperty();
property.setName("productid");
property.setEnabled(true);
property.setInsertable(true);
property.setUpdatable(true);
configuration.getProperties().add(property);
property = new CrudProperty();
property.setName("category");
property.setEnabled(true);
property.setInsertable(true);
property.setUpdatable(true);
configuration.getProperties().add(property);
property = new CrudProperty();
property.setName("descn");
property.setEnabled(true);
property.setInsertable(true);
property.setUpdatable(true);
configuration.getProperties().add(property);
property = new CrudProperty();
property.setName("name");
property.setEnabled(true);
property.setInsertable(true);
property.setUpdatable(true);
ann = new Annotation(column, Required.class.getName());
ann.getProperties().add(new Property("value", "true"));
property.getAnnotations().add(ann);
configuration.getProperties().add(property);
configuration.persistence = persistence;
configuration.init();
ActionInstance actionInstance = new ActionInstance(null, null, new ActionDescriptor(), CrudAction.class);
actionInstance.setConfiguration(configuration);
actionInstance.getParameters().add("1");
ActionContext actionContext = new ActionContext();
actionContext.setRequest(req);
actionContext.setActionPath("");
actionContext.setServletContext(req.getServletContext());
req.setParameter("productid", "1");
Map category = (Map) persistence.getSession("jpetstore").createQuery("from category").list().get(0);
req.setParameter("category", (String) category.get("catid"));
crudAction.persistence = persistence;
crudAction.setContext(actionContext);
crudAction.setActionInstance(actionInstance);
crudAction.init();
crudAction.setupForm(Mode.CREATE);
Field descnField = crudAction.getForm().findFieldByPropertyName("descn");
assertNotNull(descnField);
assertTrue(descnField instanceof FileBlobField);
File tmpFile = File.createTempFile("blob", "blob");
DiskFileItem fileItem = new DiskFileItem("descn", "application/octet-stream", false, tmpFile.getName(), 0, tmpFile.getParentFile()) {
@Override
public void delete() {
// Do nothing as we want to reuse this
}
};
OutputStream os = fileItem.getOutputStream();
IOUtils.write("some test data", os, req.getCharacterEncoding());
req.addFileItem("descn", fileItem);
req.setParameter("descn_operation", AbstractBlobField.UPLOAD_MODIFY);
crudAction.httpPostMultipart();
assertFalse(crudAction.form.validate());
AbstractBlobField blobField = (AbstractBlobField) crudAction.form.findFieldByPropertyName("descn");
assertNotNull(blobField.getValue());
assertEquals(tmpFile.getName(), blobField.getValue().getFilename());
assertEquals(fileItem.getSize(), blobField.getValue().getSize());
try {
crudAction.getBlobManager().loadMetadata(new Blob(blobField.getValue().getCode()));
fail("The blob was saved despite validation failing");
} catch (Exception e) {
}
crudAction.object = null;
req.setParameter(blobField.getCodeInputName(), blobField.getValue().getCode());
req.setParameter("name", "name");
req.setParameter("productid", "1");
req.setParameter("category", "BIRDS");
crudAction.httpPostMultipart();
assertTrue(crudAction.form.validate());
blobField = (FileBlobField) crudAction.form.findFieldByPropertyName("descn");
assertNotNull(blobField.getValue());
// This is necessary because the crud might reload the form
crudAction.blobManager.loadMetadata(blobField.getValue());
assertEquals(tmpFile.getName(), blobField.getValue().getFilename());
assertEquals(fileItem.getSize(), blobField.getValue().getSize());
try {
crudAction.blobManager.loadMetadata(new Blob(blobField.getValue().getCode()));
} catch (IOException e) {
e.printStackTrace();
fail("The blob was not saved");
}
crudAction.httpPutMultipart();
assertTrue(crudAction.form.validate());
blobField = (FileBlobField) crudAction.form.findFieldByPropertyName("descn");
assertNotNull(blobField.getValue());
// This is necessary because the crud might reload the form
crudAction.blobManager.loadMetadata(blobField.getValue());
assertEquals(tmpFile.getName(), blobField.getValue().getFilename());
String oldBlobCode = blobField.getValue().getCode();
assertEquals(fileItem.getSize(), blobField.getValue().getSize());
req.setParameter("descn_operation", FileBlobField.UPLOAD_MODIFY);
req.setFileItem("descn", fileItem);
crudAction.httpPutMultipart();
assertTrue(crudAction.form.validate());
blobField = (FileBlobField) crudAction.form.findFieldByPropertyName("descn");
assertNotNull(blobField.getValue());
// This is necessary because the crud might reload the form
crudAction.blobManager.loadMetadata(blobField.getValue());
assertEquals(tmpFile.getName(), blobField.getValue().getFilename());
String newBlobCode = blobField.getValue().getCode();
assertNotEquals(oldBlobCode, newBlobCode);
crudAction.blobManager.loadMetadata(new Blob(newBlobCode));
try {
crudAction.blobManager.loadMetadata(new Blob(oldBlobCode));
fail("The blob " + oldBlobCode + " should have been deleted");
} catch (IOException e) {
// Ok
}
Session session = persistence.getSession("jpetstore");
session.flush();
Object id = ((Map) crudAction.object).get("productid");
int qres = session.createSQLQuery("update product set descn = 'illegal' where productid = :id").setParameter("id", id).executeUpdate();
assertEquals(1, qres);
session.flush();
session.getTransaction().commit();
session.clear();
session.beginTransaction();
// Force loading the object from the DB
crudAction.getParameters().add(id.toString());
crudAction.parametersAcquired();
crudAction.setupForm(Mode.VIEW);
crudAction.form.readFromObject(crudAction.object);
BlobUtils.loadBlobs(crudAction.form, crudAction.getBlobManager(), false);
blobField = (FileBlobField) crudAction.form.findFieldByPropertyName("descn");
assertNotNull(blobField.getValue());
assertNotNull(blobField.getBlobError());
assertNull(blobField.getValue().getFilename());
qres = session.createSQLQuery("update product set descn = :blobCode where productid = :id").setParameter("id", id).setParameter("blobCode", newBlobCode).executeUpdate();
assertEquals(1, qres);
session.flush();
session.getTransaction().commit();
session.clear();
session.beginTransaction();
// Force reload
crudAction.parametersAcquired();
crudAction.httpDelete(Collections.emptyList());
try {
crudAction.blobManager.loadMetadata(new Blob(newBlobCode));
fail("The blob " + newBlobCode + " should have been deleted");
} catch (IOException e) {
// Ok
}
}
use of com.manydesigns.portofino.model.Annotation in project Portofino by ManyDesigns.
the class CrudProperty method init.
// **************************************************************************
// Configuration implementation
// **************************************************************************
public void init(Model model, Configuration configuration) {
assert name != null;
for (Annotation annotation : annotations) {
annotation.reset();
annotation.init(model, configuration);
annotation.link(model, configuration);
}
}
use of com.manydesigns.portofino.model.Annotation in project Portofino by ManyDesigns.
the class UpstairsAction method setupColumn.
protected int setupColumn(ConnectionProvider connectionProvider, Column column, CrudConfiguration configuration, int columnsInSummary, String linkToParentProperty, boolean isPassword) {
if (column.getActualJavaType() == null) {
logger.debug("Column without a javaType, skipping: {}", column.getQualifiedName());
return columnsInSummary;
}
Table table = column.getTable();
@SuppressWarnings({ "StringEquality" }) boolean enabled = !(linkToParentProperty != NO_LINK_TO_PARENT && column.getActualPropertyName().equals(linkToParentProperty)) && !isUnsupportedProperty(column);
boolean inPk = DatabaseLogic.isInPk(column);
boolean inFk = DatabaseLogic.isInFk(column);
boolean inSummary = enabled && (inPk || columnsInSummary < maxColumnsInSummary) && !isPassword;
boolean updatable = enabled && !column.isAutoincrement() && !inPk;
boolean insertable = enabled && !column.isAutoincrement();
if (!configuration.isLargeResultSet()) {
detectBooleanColumn(connectionProvider, table, column);
}
if (enabled && inPk && !inFk && Number.class.isAssignableFrom(column.getActualJavaType()) && !column.isAutoincrement()) {
for (PrimaryKeyColumn pkc : table.getPrimaryKey().getPrimaryKeyColumns()) {
if (pkc.getActualColumn().equals(column)) {
pkc.setGenerator(new IncrementGenerator(pkc));
insertable = false;
break;
}
}
}
if (isPassword) {
Annotation annotation = DatabaseLogic.findAnnotation(column, Password.class);
if (annotation == null) {
column.getAnnotations().add(new Annotation(column, Password.class.getName()));
}
insertable = false;
updatable = false;
}
if (!isPassword && column.getActualJavaType() == String.class && (column.getLength() == null || column.getLength() > MULTILINE_THRESHOLD)) {
Annotation annotation = DatabaseLogic.findAnnotation(column, Multiline.class);
if (annotation == null) {
annotation = new Annotation(column, Multiline.class.getName());
annotation.setProperty("value", "true");
column.getAnnotations().add(annotation);
}
}
CrudProperty crudProperty = new CrudProperty();
crudProperty.setEnabled(enabled);
crudProperty.setName(column.getActualPropertyName());
crudProperty.setInsertable(insertable);
crudProperty.setUpdatable(updatable);
if (inSummary) {
crudProperty.setInSummary(true);
crudProperty.setSearchable(true);
columnsInSummary++;
}
configuration.getProperties().add(crudProperty);
return columnsInSummary;
}
Aggregations