]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/DataModels.java
Improve JCR repository lifecycle
[lgpl/argeo-commons.git] / org.argeo.cms.jcr / src / org / argeo / cms / jcr / internal / DataModels.java
1 package org.argeo.cms.jcr.internal;
2
3 import static org.argeo.cms.osgi.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
4
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.TreeMap;
10
11 import org.argeo.api.cms.CmsLog;
12 import org.argeo.cms.CmsException;
13 import org.argeo.cms.osgi.DataModelNamespace;
14 import org.osgi.framework.Bundle;
15 import org.osgi.framework.BundleContext;
16 import org.osgi.framework.BundleEvent;
17 import org.osgi.framework.BundleListener;
18 import org.osgi.framework.wiring.BundleCapability;
19 import org.osgi.framework.wiring.BundleWire;
20 import org.osgi.framework.wiring.BundleWiring;
21
22 class DataModels implements BundleListener {
23 private final static CmsLog log = CmsLog.getLog(DataModels.class);
24
25 private Map<String, DataModel> dataModels = new TreeMap<>();
26
27 public DataModels(BundleContext bc) {
28 for (Bundle bundle : bc.getBundles())
29 processBundle(bundle, null);
30 bc.addBundleListener(this);
31 }
32
33 public List<DataModel> getNonAbstractDataModels() {
34 List<DataModel> res = new ArrayList<>();
35 for (String name : dataModels.keySet()) {
36 DataModel dataModel = dataModels.get(name);
37 if (!dataModel.isAbstract())
38 res.add(dataModel);
39 }
40 // TODO reorder?
41 return res;
42 }
43
44 @Override
45 public void bundleChanged(BundleEvent event) {
46 if (event.getType() == Bundle.RESOLVED) {
47 processBundle(event.getBundle(), null);
48 } else if (event.getType() == Bundle.UNINSTALLED) {
49 BundleWiring wiring = event.getBundle().adapt(BundleWiring.class);
50 List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
51 if (providedDataModels.size() == 0)
52 return;
53 for (BundleCapability bundleCapability : providedDataModels) {
54 dataModels.remove(bundleCapability.getAttributes().get(DataModelNamespace.NAME));
55 }
56 }
57
58 }
59
60 protected void processBundle(Bundle bundle, List<Bundle> scannedBundles) {
61 if (scannedBundles != null && scannedBundles.contains(bundle))
62 throw new IllegalStateException("Cycle in CMS data model requirements for " + bundle);
63 BundleWiring wiring = bundle.adapt(BundleWiring.class);
64 if (wiring == null) {
65 int bundleState = bundle.getState();
66 if (bundleState != Bundle.INSTALLED && bundleState != Bundle.UNINSTALLED) {// ignore unresolved bundles
67 log.warn("Bundle " + bundle.getSymbolicName() + " #" + bundle.getBundleId() + " ("
68 + bundle.getLocation() + ") cannot be adapted to a wiring");
69 } else {
70 if (log.isTraceEnabled())
71 log.warn("Bundle " + bundle.getSymbolicName() + " is not resolved.");
72 }
73 return;
74 }
75 List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
76 if (providedDataModels.size() == 0)
77 return;
78 List<BundleWire> requiredDataModels = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
79 // process requirements first
80 for (BundleWire bundleWire : requiredDataModels) {
81 List<Bundle> nextScannedBundles = new ArrayList<>();
82 if (scannedBundles != null)
83 nextScannedBundles.addAll(scannedBundles);
84 nextScannedBundles.add(bundle);
85 Bundle providerBundle = bundleWire.getProvider().getBundle();
86 processBundle(providerBundle, nextScannedBundles);
87 }
88 for (BundleCapability bundleCapability : providedDataModels) {
89 String name = (String) bundleCapability.getAttributes().get(DataModelNamespace.NAME);
90 assert name != null;
91 if (!dataModels.containsKey(name)) {
92 DataModel dataModel = new DataModel(name, bundleCapability, requiredDataModels);
93 dataModels.put(dataModel.getName(), dataModel);
94 }
95 }
96 }
97
98 /** Return a negative depth if dataModel is required by ref, 0 otherwise. */
99 static int required(DataModel ref, DataModel dataModel, int depth) {
100 for (DataModel dm : ref.getRequired()) {
101 if (dm.equals(dataModel))// found here
102 return depth - 1;
103 int d = required(dm, dataModel, depth - 1);
104 if (d != 0)// found deeper
105 return d;
106 }
107 return 0;// not found
108 }
109
110 class DataModel {
111 private final String name;
112 private final boolean abstrct;
113 // private final boolean standalone;
114 private final String cnd;
115 private final List<DataModel> required;
116
117 private DataModel(String name, BundleCapability bundleCapability, List<BundleWire> requiredDataModels) {
118 assert CMS_DATA_MODEL_NAMESPACE.equals(bundleCapability.getNamespace());
119 this.name = name;
120 Map<String, Object> attrs = bundleCapability.getAttributes();
121 abstrct = KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT));
122 // standalone = KernelUtils.asBoolean((String)
123 // attrs.get(DataModelNamespace.CAPABILITY_STANDALONE_ATTRIBUTE));
124 cnd = (String) attrs.get(DataModelNamespace.CND);
125 List<DataModel> req = new ArrayList<>();
126 for (BundleWire wire : requiredDataModels) {
127 String requiredDataModelName = (String) wire.getCapability().getAttributes()
128 .get(DataModelNamespace.NAME);
129 assert requiredDataModelName != null;
130 DataModel requiredDataModel = dataModels.get(requiredDataModelName);
131 if (requiredDataModel == null)
132 throw new CmsException("No required data model " + requiredDataModelName);
133 req.add(requiredDataModel);
134 }
135 required = Collections.unmodifiableList(req);
136 }
137
138 public String getName() {
139 return name;
140 }
141
142 public boolean isAbstract() {
143 return abstrct;
144 }
145
146 // public boolean isStandalone() {
147 // return !isAbstract();
148 // }
149
150 public String getCnd() {
151 return cnd;
152 }
153
154 public List<DataModel> getRequired() {
155 return required;
156 }
157
158 // @Override
159 // public int compareTo(DataModel o) {
160 // if (equals(o))
161 // return 0;
162 // int res = required(this, o, 0);
163 // if (res != 0)
164 // return res;
165 // // the other way round
166 // res = required(o, this, 0);
167 // if (res != 0)
168 // return -res;
169 // return 0;
170 // }
171
172 @Override
173 public int hashCode() {
174 return name.hashCode();
175 }
176
177 @Override
178 public boolean equals(Object obj) {
179 if (obj instanceof DataModel)
180 return ((DataModel) obj).name.equals(name);
181 return false;
182 }
183
184 @Override
185 public String toString() {
186 return "Data model " + name;
187 }
188
189 }
190
191 }