2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6 * ===========================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END====================================================
22 package org.onap.aaf.auth.dao.cass;
24 import java.io.ByteArrayOutputStream;
25 import java.io.DataInputStream;
26 import java.io.DataOutputStream;
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.List;
34 import java.util.Map.Entry;
36 import org.onap.aaf.auth.dao.Bytification;
37 import org.onap.aaf.auth.dao.Cached;
38 import org.onap.aaf.auth.dao.CassAccess;
39 import org.onap.aaf.auth.dao.CassDAOImpl;
40 import org.onap.aaf.auth.dao.Loader;
41 import org.onap.aaf.auth.dao.Streamer;
42 import org.onap.aaf.auth.env.AuthzTrans;
43 import org.onap.aaf.auth.layer.Result;
44 import org.onap.aaf.misc.env.APIException;
45 import org.onap.aaf.misc.env.Env;
46 import org.onap.aaf.misc.env.TimeTaken;
50 import com.datastax.driver.core.Cluster;
51 import com.datastax.driver.core.ResultSet;
52 import com.datastax.driver.core.Row;
53 import com.datastax.driver.core.exceptions.DriverException;
58 * Data Access Object for Namespace Data
63 public class NsDAO extends CassDAOImpl<AuthzTrans,NsDAO.Data> {
64 public static final String TABLE = "ns";
65 public static final String TABLE_ATTRIB = "ns_attrib";
66 public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F
67 public static final int USER = 0;
68 public static final int ROOT = 1;
69 public static final int COMPANY=2;
70 public static final int APP = 3;
72 private static final String BEGIN_BATCH = "BEGIN BATCH\n";
73 private static final String APPLY_BATCH = "\nAPPLY BATCH;\n";
74 private static final String SQSCCR = "';\n";
75 private static final String SQCSQ = "','";
77 private HistoryDAO historyDAO;
78 private CacheInfoDAO infoDAO;
81 public NsDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
82 super(trans, NsDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
86 public NsDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO iDAO) throws APIException, IOException {
87 super(trans, NsDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
94 //////////////////////////////////////////
95 // Data Definition, matches Cassandra DM
96 //////////////////////////////////////////
97 private static final int KEYLIMIT = 1;
99 * Data class that matches the Cassandra Table "role"
103 public static class Data extends CacheableData implements Bytification {
106 public String description;
107 public String parent;
108 public Map<String,String> attrib;
110 // ////////////////////////////////////////
112 public Map<String,String> attrib(boolean mutable) {
113 if (attrib == null) {
114 attrib = new HashMap<>();
115 } else if (mutable && !(attrib instanceof HashMap)) {
116 attrib = new HashMap<>(attrib);
122 public int[] invalidate(Cached<?,?> cache) {
128 public NsSplit split(String name) {
129 return new NsSplit(this,name);
133 public ByteBuffer bytify() throws IOException {
134 ByteArrayOutputStream baos = new ByteArrayOutputStream();
135 NSLoader.deflt.marshal(this,new DataOutputStream(baos));
136 return ByteBuffer.wrap(baos.toByteArray());
140 public void reconstitute(ByteBuffer bb) throws IOException {
141 NSLoader.deflt.unmarshal(this,toDIS(bb));
145 public String toString() {
151 private void init(AuthzTrans trans) throws APIException, IOException {
153 if (historyDAO==null) {
154 historyDAO = new HistoryDAO(trans, this);
157 infoDAO = new CacheInfoDAO(trans,this);
160 String[] helpers = setCRUD(trans, TABLE, Data.class, NSLoader.deflt,4/*need to skip attrib */);
162 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
163 " WHERE parent = ?", new NSLoader(1),readConsistency);
167 private static final class NSLoader extends Loader<Data> implements Streamer<Data> {
168 public static final int MAGIC=250935515;
169 public static final int VERSION=1;
170 public static final int BUFF_SIZE=48;
172 public static final NSLoader deflt = new NSLoader(KEYLIMIT);
174 public NSLoader(int keylimit) {
179 public Data load(Data data, Row row) {
180 // Int more efficient
181 data.name = row.getString(0);
182 data.type = row.getInt(1);
183 data.description = row.getString(2);
184 data.parent = row.getString(3);
189 protected void key(Data data, int idx, Object[] obj) {
194 protected void body(Data data, int _idx, Object[] obj) {
198 obj[++idx]=data.description;
199 obj[++idx]=data.parent;
203 public void marshal(Data data, DataOutputStream os) throws IOException {
204 writeHeader(os,MAGIC,VERSION);
205 writeString(os, data.name);
206 os.writeInt(data.type);
207 writeString(os,data.description);
208 writeString(os,data.parent);
209 if (data.attrib==null) {
212 os.writeInt(data.attrib.size());
213 for (Entry<String, String> es : data.attrib(false).entrySet()) {
214 writeString(os,es.getKey());
215 writeString(os,es.getValue());
221 public void unmarshal(Data data, DataInputStream is) throws IOException {
222 /*int version = */readHeader(is,MAGIC,VERSION);
223 // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
225 byte[] buff = new byte[BUFF_SIZE];
226 data.name = readString(is, buff);
227 data.type = is.readInt();
228 data.description = readString(is,buff);
229 data.parent = readString(is,buff);
230 int count = is.readInt();
232 Map<String, String> da = data.attrib(true);
233 for (int i=0;i<count;++i) {
234 da.put(readString(is,buff), readString(is,buff));
242 public Result<Data> create(AuthzTrans trans, Data data) {
243 String ns = data.name;
244 // Ensure Parent is set
245 if (data.parent==null) {
246 return Result.err(Result.ERR_BadData, "Need parent for %s", ns);
250 StringBuilder stmt = new StringBuilder();
251 stmt.append(BEGIN_BATCH);
252 attribInsertStmts(stmt, data);
253 stmt.append(APPLY_BATCH);
255 getSession(trans).execute(stmt.toString());
256 //// TEST CODE for Exception
257 // boolean force = true;
259 // throw new com.datastax.driver.core.exceptions.NoHostAvailableException(new HashMap<>());
260 //// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"Sample Message");
264 } catch (DriverException | APIException | IOException e) {
265 reportPerhapsReset(trans,e);
266 trans.info().log(stmt);
267 return Result.err(Result.ERR_Backend, "Backend Access");
269 return super.create(trans, data);
273 public Result<Void> update(AuthzTrans trans, Data data) {
274 String ns = data.name;
275 // Ensure Parent is set
276 if (data.parent==null) {
277 return Result.err(Result.ERR_BadData, "Need parent for %s", ns);
280 StringBuilder stmt = new StringBuilder();
281 stmt.append(BEGIN_BATCH);
283 Map<String, String> localAttr = data.attrib;
284 Result<Map<String, String>> rremoteAttr = readAttribByNS(trans,ns);
285 if (rremoteAttr.notOK()) {
286 return Result.err(rremoteAttr);
290 for (Entry<String, String> es : localAttr.entrySet()) {
291 str = rremoteAttr.value.get(es.getKey());
292 if (str==null || !str.equals(es.getValue())) {
293 attribUpdateStmt(stmt, ns, es.getKey(),es.getValue());
297 // No point in deleting... insert overwrites...
298 // for (Entry<String, String> es : remoteAttr.entrySet()) {
299 // str = localAttr.get(es.getKey());
300 // if (str==null || !str.equals(es.getValue())) {
301 // attribDeleteStmt(stmt, ns, es.getKey());
304 if (stmt.length()>BEGIN_BATCH.length()) {
305 stmt.append(APPLY_BATCH);
306 getSession(trans).execute(stmt.toString());
308 } catch (DriverException | APIException | IOException e) {
309 reportPerhapsReset(trans,e);
310 trans.info().log(stmt);
311 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
314 return super.update(trans,data);
318 * @see org.onap.aaf.auth.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object)
321 public Result<List<Data>> read(AuthzTrans trans, Data data) {
322 Result<List<Data>> rld = super.read(trans, data);
324 if (rld.isOKhasData()) {
325 for (Data d : rld.value) {
326 // Note: Map is null at this point, save time/mem by assignment
327 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
329 d.attrib = rabn.value;
331 return Result.err(rabn);
339 * @see org.onap.aaf.auth.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object[])
342 public Result<List<Data>> read(AuthzTrans trans, Object... key) {
343 Result<List<Data>> rld = super.read(trans, key);
345 if (rld.isOKhasData()) {
346 for (Data d : rld.value) {
347 // Note: Map is null at this point, save time/mem by assignment
348 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
350 d.attrib = rabn.value;
352 return Result.err(rabn);
360 public Result<Void> delete(AuthzTrans trans, Data data, boolean reread) {
361 TimeTaken tt = trans.start("Delete NS Attributes " + data.name, Env.REMOTE);
363 StringBuilder stmt = new StringBuilder();
364 attribDeleteAllStmt(stmt, data);
366 getSession(trans).execute(stmt.toString());
367 } catch (DriverException | APIException | IOException e) {
368 reportPerhapsReset(trans,e);
369 trans.info().log(stmt);
370 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
375 return super.delete(trans, data, reread);
379 public Result<Map<String,String>> readAttribByNS(AuthzTrans trans, String ns) {
380 Map<String,String> map = new HashMap<>();
381 TimeTaken tt = trans.start("readAttribByNS " + ns, Env.REMOTE);
383 ResultSet rs = getSession(trans).execute("SELECT key,value FROM "
389 for (Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
391 map.put(r.getString(0), r.getString(1));
393 } catch (DriverException | APIException | IOException e) {
394 reportPerhapsReset(trans,e);
395 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
399 return Result.ok(map);
402 public Result<Set<String>> readNsByAttrib(AuthzTrans trans, String key) {
403 Set<String> set = new HashSet<>();
404 TimeTaken tt = trans.start("readNsBykey " + key, Env.REMOTE);
406 ResultSet rs = getSession(trans).execute("SELECT ns FROM "
412 for (Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
414 set.add(r.getString(0));
416 } catch (DriverException | APIException | IOException e) {
417 reportPerhapsReset(trans,e);
418 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
422 return Result.ok(set);
425 public Result<Void> attribAdd(AuthzTrans trans, String ns, String key, String value) {
427 getSession(trans).execute(attribInsertStmt(new StringBuilder(),ns,key,value).toString());
429 } catch (DriverException | APIException | IOException e) {
430 reportPerhapsReset(trans,e);
431 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
435 private StringBuilder attribInsertStmt(StringBuilder sb, String ns, String key, String value) {
436 sb.append("INSERT INTO ");
437 sb.append(TABLE_ATTRIB);
438 sb.append(" (ns,key,value) VALUES ('");
448 private StringBuilder attribUpdateStmt(StringBuilder sb, String ns, String key, String value) {
449 sb.append("UPDATE ");
450 sb.append(TABLE_ATTRIB);
451 sb.append(" set value='");
453 sb.append("' where ns='");
455 sb.append("' AND key='");
462 public Result<Void> attribRemove(AuthzTrans trans, String ns, String key) {
464 getSession(trans).execute(attribDeleteStmt(new StringBuilder(),ns,key).toString());
466 } catch (DriverException | APIException | IOException e) {
467 reportPerhapsReset(trans,e);
468 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
472 private StringBuilder attribDeleteStmt(StringBuilder stmt, String ns, String key) {
473 stmt.append("DELETE FROM ");
474 stmt.append(TABLE_ATTRIB);
475 stmt.append(" WHERE ns='");
477 stmt.append("' AND key='");
483 private void attribDeleteAllStmt(StringBuilder stmt, Data data) {
484 stmt.append(" DELETE FROM ");
485 stmt.append(TABLE_ATTRIB);
486 stmt.append(" WHERE ns='");
487 stmt.append(data.name);
491 private void attribInsertStmts(StringBuilder stmt, Data data) {
493 for (Entry<String,String> es : data.attrib(false).entrySet() ) {
495 attribInsertStmt(stmt,data.name,es.getKey(),es.getValue());
500 * Add description to Namespace
506 public Result<Void> addDescription(AuthzTrans trans, String ns, String description) {
508 getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '"
509 + description.replace("'", "''") + "' WHERE name = '" + ns + "';");
510 } catch (DriverException | APIException | IOException e) {
511 reportPerhapsReset(trans,e);
512 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
515 Data data = new Data();
517 wasModified(trans, CRUD.update, data, "Added description " + description + " to namespace " + ns, null );
521 public Result<List<Data>> getChildren(AuthzTrans trans, String parent) {
522 return psNS.read(trans, R_TEXT, new Object[]{parent});
527 * Log Modification statements to History
529 * @param modified which CRUD action was done
530 * @param data entity data that needs a log entry
531 * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
534 protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
535 boolean memo = override.length>0 && override[0]!=null;
536 boolean subject = override.length>1 && override[1]!=null;
538 //TODO Must log history
539 HistoryDAO.Data hd = HistoryDAO.newInitedData();
540 hd.user = trans.user();
541 hd.action = modified.name();
543 hd.subject = subject ? override[1] : data.name;
544 hd.memo = memo ? override[0] : (data.name + " was " + modified.name() + 'd' );
545 if (modified==CRUD.delete) {
547 hd.reconstruct = data.bytify();
548 } catch (IOException e) {
549 trans.error().log(e,"Could not serialize NsDAO.Data");
553 if (historyDAO.create(trans, hd).status!=Status.OK) {
554 trans.error().log("Cannot log to History");
556 if (infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {
557 trans.error().log("Cannot touch CacheInfo");