1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * ===========================================================================
\r
7 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
8 * * you may not use this file except in compliance with the License.
\r
9 * * You may obtain a copy of the License at
\r
11 * * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * * Unless required by applicable law or agreed to in writing, software
\r
14 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
15 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
16 * * See the License for the specific language governing permissions and
\r
17 * * limitations under the License.
\r
18 * * ============LICENSE_END====================================================
\r
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
23 package org.onap.aaf.dao.aaf.cass;
\r
25 import java.io.ByteArrayOutputStream;
\r
26 import java.io.DataInputStream;
\r
27 import java.io.DataOutputStream;
\r
28 import java.io.IOException;
\r
29 import java.nio.ByteBuffer;
\r
30 import java.util.HashMap;
\r
31 import java.util.HashSet;
\r
32 import java.util.Iterator;
\r
33 import java.util.List;
\r
34 import java.util.Map;
\r
35 import java.util.Map.Entry;
\r
37 import org.onap.aaf.authz.env.AuthzTrans;
\r
38 import org.onap.aaf.authz.layer.Result;
\r
39 import org.onap.aaf.dao.Bytification;
\r
40 import org.onap.aaf.dao.Cached;
\r
41 import org.onap.aaf.dao.CassAccess;
\r
42 import org.onap.aaf.dao.CassDAOImpl;
\r
43 import org.onap.aaf.dao.Loader;
\r
44 import org.onap.aaf.dao.Streamer;
\r
46 import java.util.Set;
\r
48 import org.onap.aaf.inno.env.APIException;
\r
49 import org.onap.aaf.inno.env.Env;
\r
50 import org.onap.aaf.inno.env.TimeTaken;
\r
51 import com.datastax.driver.core.Cluster;
\r
52 import com.datastax.driver.core.ResultSet;
\r
53 import com.datastax.driver.core.Row;
\r
54 import com.datastax.driver.core.exceptions.DriverException;
\r
59 * Data Access Object for Namespace Data
\r
62 public class NsDAO extends CassDAOImpl<AuthzTrans,NsDAO.Data> {
\r
63 public static final String TABLE = "ns";
\r
64 public static final String TABLE_ATTRIB = "ns_attrib";
\r
65 public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F
\r
66 public static final int ROOT = 1;
\r
67 public static final int COMPANY=2;
\r
68 public static final int APP = 3;
\r
70 private static final String BEGIN_BATCH = "BEGIN BATCH\n";
\r
71 private static final String APPLY_BATCH = "APPLY BATCH;\n";
\r
72 private static final String SQSCCR = "';\n";
\r
73 private static final String SQCSQ = "','";
\r
75 private HistoryDAO historyDAO;
\r
76 private CacheInfoDAO infoDAO;
\r
77 private PSInfo psNS;
\r
79 public NsDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
\r
80 super(trans, NsDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
\r
84 public NsDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO iDAO) throws APIException, IOException {
\r
85 super(trans, NsDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
\r
92 //////////////////////////////////////////
\r
93 // Data Definition, matches Cassandra DM
\r
94 //////////////////////////////////////////
\r
95 private static final int KEYLIMIT = 1;
\r
97 * Data class that matches the Cassandra Table "role"
\r
100 public static class Data extends CacheableData implements Bytification {
\r
101 public String name;
\r
103 public String description;
\r
104 public String parent;
\r
105 public Map<String,String> attrib;
\r
107 // ////////////////////////////////////////
\r
109 public Map<String,String> attrib(boolean mutable) {
\r
110 if (attrib == null) {
\r
111 attrib = new HashMap<String,String>();
\r
112 } else if (mutable && !(attrib instanceof HashMap)) {
\r
113 attrib = new HashMap<String,String>(attrib);
\r
119 public int[] invalidate(Cached<?,?> cache) {
\r
125 public NsSplit split(String name) {
\r
126 return new NsSplit(this,name);
\r
130 public ByteBuffer bytify() throws IOException {
\r
131 ByteArrayOutputStream baos = new ByteArrayOutputStream();
\r
132 NSLoader.deflt.marshal(this,new DataOutputStream(baos));
\r
133 return ByteBuffer.wrap(baos.toByteArray());
\r
137 public void reconstitute(ByteBuffer bb) throws IOException {
\r
138 NSLoader.deflt.unmarshal(this,toDIS(bb));
\r
142 public String toString() {
\r
148 private void init(AuthzTrans trans) throws APIException, IOException {
\r
150 if(historyDAO==null) {
\r
151 historyDAO = new HistoryDAO(trans, this);
\r
153 if(infoDAO==null) {
\r
154 infoDAO = new CacheInfoDAO(trans,this);
\r
157 String[] helpers = setCRUD(trans, TABLE, Data.class, NSLoader.deflt,4/*need to skip attrib */);
\r
159 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
\r
160 " WHERE parent = ?", new NSLoader(1),readConsistency);
\r
164 private static final class NSLoader extends Loader<Data> implements Streamer<Data> {
\r
165 public static final int MAGIC=250935515;
\r
166 public static final int VERSION=1;
\r
167 public static final int BUFF_SIZE=48;
\r
169 public static final NSLoader deflt = new NSLoader(KEYLIMIT);
\r
171 public NSLoader(int keylimit) {
\r
176 public Data load(Data data, Row row) {
\r
177 // Int more efficient
\r
178 data.name = row.getString(0);
\r
179 data.type = row.getInt(1);
\r
180 data.description = row.getString(2);
\r
181 data.parent = row.getString(3);
\r
186 protected void key(Data data, int idx, Object[] obj) {
\r
187 obj[idx]=data.name;
\r
191 protected void body(Data data, int _idx, Object[] obj) {
\r
194 obj[idx]=data.type;
\r
195 obj[++idx]=data.description;
\r
196 obj[++idx]=data.parent;
\r
200 public void marshal(Data data, DataOutputStream os) throws IOException {
\r
201 writeHeader(os,MAGIC,VERSION);
\r
202 writeString(os, data.name);
\r
203 os.writeInt(data.type);
\r
204 writeString(os,data.description);
\r
205 writeString(os,data.parent);
\r
206 if(data.attrib==null) {
\r
209 os.writeInt(data.attrib.size());
\r
210 for(Entry<String, String> es : data.attrib(false).entrySet()) {
\r
211 writeString(os,es.getKey());
\r
212 writeString(os,es.getValue());
\r
218 public void unmarshal(Data data, DataInputStream is) throws IOException {
\r
219 /*int version = */readHeader(is,MAGIC,VERSION);
\r
220 // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
\r
222 byte[] buff = new byte[BUFF_SIZE];
\r
223 data.name = readString(is, buff);
\r
224 data.type = is.readInt();
\r
225 data.description = readString(is,buff);
\r
226 data.parent = readString(is,buff);
\r
227 int count = is.readInt();
\r
229 Map<String, String> da = data.attrib(true);
\r
230 for(int i=0;i<count;++i) {
\r
231 da.put(readString(is,buff), readString(is,buff));
\r
239 public Result<Data> create(AuthzTrans trans, Data data) {
\r
240 String ns = data.name;
\r
241 // Ensure Parent is set
\r
242 int ldot = ns.lastIndexOf('.');
\r
243 data.parent=ldot<0?".":ns.substring(0,ldot);
\r
245 // insert Attributes
\r
246 StringBuilder stmt = new StringBuilder();
\r
247 stmt.append(BEGIN_BATCH);
\r
248 attribInsertStmts(stmt, data);
\r
249 stmt.append(APPLY_BATCH);
\r
251 getSession(trans).execute(stmt.toString());
\r
252 //// TEST CODE for Exception
\r
253 // boolean force = true;
\r
255 // throw new com.datastax.driver.core.exceptions.NoHostAvailableException(new HashMap<InetSocketAddress,Throwable>());
\r
256 //// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"Sample Message");
\r
260 } catch (DriverException | APIException | IOException e) {
\r
261 reportPerhapsReset(trans,e);
\r
262 trans.info().log(stmt);
\r
263 return Result.err(Result.ERR_Backend, "Backend Access");
\r
265 return super.create(trans, data);
\r
269 public Result<Void> update(AuthzTrans trans, Data data) {
\r
270 String ns = data.name;
\r
271 // Ensure Parent is set
\r
272 int ldot = ns.lastIndexOf('.');
\r
273 data.parent=ldot<0?".":ns.substring(0,ldot);
\r
275 StringBuilder stmt = new StringBuilder();
\r
276 stmt.append(BEGIN_BATCH);
\r
278 Map<String, String> localAttr = data.attrib;
\r
279 Result<Map<String, String>> rremoteAttr = readAttribByNS(trans,ns);
\r
280 if(rremoteAttr.notOK()) {
\r
281 return Result.err(rremoteAttr);
\r
283 // update Attributes
\r
285 for(Entry<String, String> es : localAttr.entrySet()) {
\r
286 str = rremoteAttr.value.get(es.getKey());
\r
287 if(str==null || !str.equals(es.getValue())) {
\r
288 attribInsertStmt(stmt, ns, es.getKey(),es.getValue());
\r
292 // No point in deleting... insert overwrites...
\r
293 // for(Entry<String, String> es : remoteAttr.entrySet()) {
\r
294 // str = localAttr.get(es.getKey());
\r
295 // if(str==null || !str.equals(es.getValue())) {
\r
296 // attribDeleteStmt(stmt, ns, es.getKey());
\r
299 if(stmt.length()>BEGIN_BATCH.length()) {
\r
300 stmt.append(APPLY_BATCH);
\r
301 getSession(trans).execute(stmt.toString());
\r
303 } catch (DriverException | APIException | IOException e) {
\r
304 reportPerhapsReset(trans,e);
\r
305 trans.info().log(stmt);
\r
306 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
309 return super.update(trans,data);
\r
313 * @see org.onap.aaf.dao.CassDAOImpl#read(org.onap.aaf.inno.env.TransStore, java.lang.Object)
\r
316 public Result<List<Data>> read(AuthzTrans trans, Data data) {
\r
317 Result<List<Data>> rld = super.read(trans, data);
\r
319 if(rld.isOKhasData()) {
\r
320 for(Data d : rld.value) {
\r
321 // Note: Map is null at this point, save time/mem by assignment
\r
322 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
\r
324 d.attrib = rabn.value;
\r
326 return Result.err(rabn);
\r
334 * @see org.onap.aaf.dao.CassDAOImpl#read(org.onap.aaf.inno.env.TransStore, java.lang.Object[])
\r
337 public Result<List<Data>> read(AuthzTrans trans, Object... key) {
\r
338 Result<List<Data>> rld = super.read(trans, key);
\r
340 if(rld.isOKhasData()) {
\r
341 for(Data d : rld.value) {
\r
342 // Note: Map is null at this point, save time/mem by assignment
\r
343 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
\r
345 d.attrib = rabn.value;
\r
347 return Result.err(rabn);
\r
355 public Result<Void> delete(AuthzTrans trans, Data data, boolean reread) {
\r
356 TimeTaken tt = trans.start("Delete NS Attributes " + data.name, Env.REMOTE);
\r
358 StringBuilder stmt = new StringBuilder();
\r
359 attribDeleteAllStmt(stmt, data);
\r
361 getSession(trans).execute(stmt.toString());
\r
362 } catch (DriverException | APIException | IOException e) {
\r
363 reportPerhapsReset(trans,e);
\r
364 trans.info().log(stmt);
\r
365 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
370 return super.delete(trans, data, reread);
\r
374 public Result<Map<String,String>> readAttribByNS(AuthzTrans trans, String ns) {
\r
375 Map<String,String> map = new HashMap<String,String>();
\r
376 TimeTaken tt = trans.start("readAttribByNS " + ns, Env.REMOTE);
\r
378 ResultSet rs = getSession(trans).execute("SELECT key,value FROM "
\r
384 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
\r
385 Row r = iter.next();
\r
386 map.put(r.getString(0), r.getString(1));
\r
388 } catch (DriverException | APIException | IOException e) {
\r
389 reportPerhapsReset(trans,e);
\r
390 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
394 return Result.ok(map);
\r
397 public Result<Set<String>> readNsByAttrib(AuthzTrans trans, String key) {
\r
398 Set<String> set = new HashSet<String>();
\r
399 TimeTaken tt = trans.start("readNsBykey " + key, Env.REMOTE);
\r
401 ResultSet rs = getSession(trans).execute("SELECT ns FROM "
\r
407 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
\r
408 Row r = iter.next();
\r
409 set.add(r.getString(0));
\r
411 } catch (DriverException | APIException | IOException e) {
\r
412 reportPerhapsReset(trans,e);
\r
413 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
417 return Result.ok(set);
\r
420 public Result<Void> attribAdd(AuthzTrans trans, String ns, String key, String value) {
\r
422 getSession(trans).execute(attribInsertStmt(new StringBuilder(),ns,key,value).toString());
\r
423 return Result.ok();
\r
424 } catch (DriverException | APIException | IOException e) {
\r
425 reportPerhapsReset(trans,e);
\r
426 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
430 private StringBuilder attribInsertStmt(StringBuilder sb, String ns, String key, String value) {
\r
431 sb.append("INSERT INTO ");
\r
432 sb.append(TABLE_ATTRIB);
\r
433 sb.append(" (ns,key,value) VALUES ('");
\r
443 public Result<Void> attribRemove(AuthzTrans trans, String ns, String key) {
\r
445 getSession(trans).execute(attribDeleteStmt(new StringBuilder(),ns,key).toString());
\r
446 return Result.ok();
\r
447 } catch (DriverException | APIException | IOException e) {
\r
448 reportPerhapsReset(trans,e);
\r
449 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
453 private StringBuilder attribDeleteStmt(StringBuilder stmt, String ns, String key) {
\r
454 stmt.append("DELETE FROM ");
\r
455 stmt.append(TABLE_ATTRIB);
\r
456 stmt.append(" WHERE ns='");
\r
458 stmt.append("' AND key='");
\r
464 private void attribDeleteAllStmt(StringBuilder stmt, Data data) {
\r
465 stmt.append(" DELETE FROM ");
\r
466 stmt.append(TABLE_ATTRIB);
\r
467 stmt.append(" WHERE ns='");
\r
468 stmt.append(data.name);
\r
469 stmt.append(SQSCCR);
\r
472 private void attribInsertStmts(StringBuilder stmt, Data data) {
\r
473 // INSERT new Attrib
\r
474 for(Entry<String,String> es : data.attrib(false).entrySet() ) {
\r
476 attribInsertStmt(stmt,data.name,es.getKey(),es.getValue());
\r
481 * Add description to Namespace
\r
484 * @param description
\r
487 public Result<Void> addDescription(AuthzTrans trans, String ns, String description) {
\r
489 getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '"
\r
490 + description + "' WHERE name = '" + ns + "';");
\r
491 } catch (DriverException | APIException | IOException e) {
\r
492 reportPerhapsReset(trans,e);
\r
493 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
496 Data data = new Data();
\r
498 wasModified(trans, CRUD.update, data, "Added description " + description + " to namespace " + ns, null );
\r
499 return Result.ok();
\r
502 public Result<List<Data>> getChildren(AuthzTrans trans, String parent) {
\r
503 return psNS.read(trans, R_TEXT, new Object[]{parent});
\r
508 * Log Modification statements to History
\r
510 * @param modified which CRUD action was done
\r
511 * @param data entity data that needs a log entry
\r
512 * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
\r
515 protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
\r
516 boolean memo = override.length>0 && override[0]!=null;
\r
517 boolean subject = override.length>1 && override[1]!=null;
\r
519 //TODO Must log history
\r
520 HistoryDAO.Data hd = HistoryDAO.newInitedData();
\r
521 hd.user = trans.user();
\r
522 hd.action = modified.name();
\r
524 hd.subject = subject ? override[1] : data.name;
\r
525 hd.memo = memo ? override[0] : (data.name + " was " + modified.name() + 'd' );
\r
526 if(modified==CRUD.delete) {
\r
528 hd.reconstruct = data.bytify();
\r
529 } catch (IOException e) {
\r
530 trans.error().log(e,"Could not serialize NsDAO.Data");
\r
534 if(historyDAO.create(trans, hd).status!=Status.OK) {
\r
535 trans.error().log("Cannot log to History");
\r
537 if(infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {
\r
538 trans.error().log("Cannot touch CacheInfo");
\r