001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.isis.core.metamodel.services.devutils; 020 021import java.io.ByteArrayOutputStream; 022import java.io.File; 023import java.io.IOException; 024import java.io.OutputStreamWriter; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.List; 028import java.util.zip.ZipEntry; 029import java.util.zip.ZipOutputStream; 030 031import javax.activation.MimeType; 032import javax.activation.MimeTypeParseException; 033 034import com.google.common.base.Predicate; 035import com.google.common.collect.Collections2; 036import com.google.common.collect.Lists; 037 038import org.apache.isis.applib.FatalException; 039import org.apache.isis.applib.RecoverableException; 040import org.apache.isis.applib.annotation.Programmatic; 041import org.apache.isis.applib.services.devutils.DeveloperUtilitiesService; 042import org.apache.isis.applib.value.Blob; 043import org.apache.isis.applib.value.Clob; 044import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 045import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager; 046import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware; 047import org.apache.isis.core.metamodel.layoutmetadata.json.LayoutMetadataReaderFromJson; 048import org.apache.isis.core.metamodel.spec.ObjectSpecification; 049import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpi; 050import org.apache.isis.core.metamodel.spec.SpecificationLoaderSpiAware; 051import org.apache.isis.core.metamodel.spec.feature.Contributed; 052import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 053import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; 054import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; 055import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; 056 057public class DeveloperUtilitiesServiceDefault implements DeveloperUtilitiesService, SpecificationLoaderSpiAware, AdapterManagerAware { 058 059 060 private final MimeType mimeTypeTextCsv; 061 private final MimeType mimeTypeApplicationZip; 062 private final MimeType mimeTypeApplicationJson; 063 064 public DeveloperUtilitiesServiceDefault() { 065 try { 066 mimeTypeTextCsv = new MimeType("text", "csv"); 067 mimeTypeApplicationJson = new MimeType("application", "jzon"); 068 mimeTypeApplicationZip = new MimeType("application", "zip"); 069 } catch (MimeTypeParseException e) { 070 throw new RuntimeException(e); 071 } 072 } 073 074 // ////////////////////////////////////// 075 076 077 @Override 078 public Clob downloadMetaModel() { 079 080 final Collection<ObjectSpecification> specifications = specificationLoader.allSpecifications(); 081 082 final List<MetaModelRow> rows = Lists.newArrayList(); 083 for (ObjectSpecification spec : specifications) { 084 if (exclude(spec)) { 085 continue; 086 } 087 final List<ObjectAssociation> properties = spec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES); 088 for (ObjectAssociation property : properties) { 089 final OneToOneAssociation otoa = (OneToOneAssociation) property; 090 if (exclude(otoa)) { 091 continue; 092 } 093 rows.add(new MetaModelRow(spec, otoa)); 094 } 095 final List<ObjectAssociation> associations = spec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.COLLECTIONS); 096 for (ObjectAssociation collection : associations) { 097 final OneToManyAssociation otma = (OneToManyAssociation) collection; 098 if (exclude(otma)) { 099 continue; 100 } 101 rows.add(new MetaModelRow(spec, otma)); 102 } 103 final List<ObjectAction> actions = spec.getObjectActions(Contributed.INCLUDED); 104 for (ObjectAction action : actions) { 105 if (exclude(action)) { 106 continue; 107 } 108 rows.add(new MetaModelRow(spec, action)); 109 } 110 } 111 112 Collections.sort(rows); 113 114 final StringBuilder buf = new StringBuilder(); 115 buf.append(MetaModelRow.header()).append("\n"); 116 for (MetaModelRow row : rows) { 117 buf.append(row.asTextCsv()).append("\n"); 118 } 119 return new Clob("metamodel.csv", mimeTypeTextCsv, buf.toString().toCharArray()); 120 } 121 122 protected boolean exclude(OneToOneAssociation property) { 123 return false; 124 } 125 126 protected boolean exclude(OneToManyAssociation collection) { 127 return false; 128 } 129 130 protected boolean exclude(ObjectAction action) { 131 return false; 132 } 133 134 protected boolean exclude(ObjectSpecification spec) { 135 return isBuiltIn(spec) || spec.isAbstract(); 136 } 137 138 protected boolean isBuiltIn(ObjectSpecification spec) { 139 final String className = spec.getFullIdentifier(); 140 return className.startsWith("java") || className.startsWith("org.joda"); 141 } 142 143 // ////////////////////////////////////// 144 145 @Override 146 public void refreshServices() { 147 Collection<ObjectSpecification> specifications = Lists.newArrayList(specificationLoader.allSpecifications()); 148 for (ObjectSpecification objectSpec : specifications) { 149 if(objectSpec.isService()){ 150 specificationLoader.invalidateCache(objectSpec.getCorrespondingClass()); 151 } 152 } 153 } 154 155 // ////////////////////////////////////// 156 157 @Override 158 public Object refreshLayout(Object domainObject) { 159 specificationLoader.invalidateCacheFor(domainObject); 160 return domainObject; 161 } 162 163 // ////////////////////////////////////// 164 165 @Override 166 public Clob downloadLayout(Object domainObject) { 167 168 final ObjectAdapter adapterFor = adapterManager.adapterFor(domainObject); 169 final ObjectSpecification objectSpec = adapterFor.getSpecification(); 170 171 final LayoutMetadataReaderFromJson propertiesReader = new LayoutMetadataReaderFromJson(); 172 final String json = propertiesReader.asJson(objectSpec); 173 174 return new Clob(objectSpec.getShortIdentifier() +".layout.json", mimeTypeApplicationJson, json); 175 } 176 177 // ////////////////////////////////////// 178 179 @Override 180 public Blob downloadLayouts() { 181 final LayoutMetadataReaderFromJson propertiesReader = new LayoutMetadataReaderFromJson(); 182 final Collection<ObjectSpecification> allSpecs = specificationLoader.allSpecifications(); 183 final Collection<ObjectSpecification> domainObjectSpecs = Collections2.filter(allSpecs, new Predicate<ObjectSpecification>(){ 184 @Override 185 public boolean apply(ObjectSpecification input) { 186 return !input.isAbstract() && 187 !input.isService() && 188 !input.isValue() && 189 !input.isParentedOrFreeCollection(); 190 }}); 191 try { 192 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 193 ZipOutputStream zos = new ZipOutputStream(baos); 194 OutputStreamWriter writer = new OutputStreamWriter(zos); 195 for (ObjectSpecification objectSpec : domainObjectSpecs) { 196 zos.putNextEntry(new ZipEntry(zipEntryNameFor(objectSpec))); 197 writer.write(propertiesReader.asJson(objectSpec)); 198 writer.flush(); 199 zos.closeEntry(); 200 } 201 writer.close(); 202 return new Blob("layouts.zip", mimeTypeApplicationZip, baos.toByteArray()); 203 } catch (final IOException ex) { 204 throw new FatalException("Unable to create zip of layouts", ex); 205 } 206 } 207 208 private static String zipEntryNameFor(ObjectSpecification objectSpec) { 209 final String fqn = objectSpec.getFullIdentifier(); 210 return fqn.replace(".", File.separator)+".layout.json"; 211 } 212 213 214 // ////////////////////////////////////// 215 216 private SpecificationLoaderSpi specificationLoader; 217 private AdapterManager adapterManager; 218 219 @Programmatic 220 @Override 221 public void setSpecificationLoaderSpi(SpecificationLoaderSpi specificationLoader) { 222 this.specificationLoader = specificationLoader; 223 } 224 225 @Override 226 public void setAdapterManager(AdapterManager adapterManager) { 227 this.adapterManager = adapterManager; 228 } 229 230 231}