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 ROOT = 1;
68 public static final int COMPANY=2;
69 public static final int APP = 3;
71 private static final String BEGIN_BATCH = "BEGIN BATCH\n";
72 private static final String APPLY_BATCH = "\nAPPLY BATCH;\n";
73 private static final String SQSCCR = "';\n";
74 private static final String SQCSQ = "','";
76 private HistoryDAO historyDAO;
77 private CacheInfoDAO infoDAO;
80 public NsDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
81 super(trans, NsDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
85 public NsDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO iDAO) throws APIException, IOException {
86 super(trans, NsDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
93 //////////////////////////////////////////
94 // Data Definition, matches Cassandra DM
95 //////////////////////////////////////////
96 private static final int KEYLIMIT = 1;
98 * Data class that matches the Cassandra Table "role"
102 public static class Data extends CacheableData implements Bytification {
105 public String description;
106 public String parent;
107 public Map<String,String> attrib;
109 // ////////////////////////////////////////
111 public Map<String,String> attrib(boolean mutable) {
112 if (attrib == null) {
113 attrib = new HashMap<String,String>();
114 } else if (mutable && !(attrib instanceof HashMap)) {
115 attrib = new HashMap<String,String>(attrib);
121 public int[] invalidate(Cached<?,?> cache) {
127 public NsSplit split(String name) {
128 return new NsSplit(this,name);
132 public ByteBuffer bytify() throws IOException {
133 ByteArrayOutputStream baos = new ByteArrayOutputStream();
134 NSLoader.deflt.marshal(this,new DataOutputStream(baos));
135 return ByteBuffer.wrap(baos.toByteArray());
139 public void reconstitute(ByteBuffer bb) throws IOException {
140 NSLoader.deflt.unmarshal(this,toDIS(bb));
144 public String toString() {
150 private void init(AuthzTrans trans) throws APIException, IOException {
152 if(historyDAO==null) {
153 historyDAO = new HistoryDAO(trans, this);
156 infoDAO = new CacheInfoDAO(trans,this);
159 String[] helpers = setCRUD(trans, TABLE, Data.class, NSLoader.deflt,4/*need to skip attrib */);
161 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
162 " WHERE parent = ?", new NSLoader(1),readConsistency);
166 private static final class NSLoader extends Loader<Data> implements Streamer<Data> {
167 public static final int MAGIC=250935515;
168 public static final int VERSION=1;
169 public static final int BUFF_SIZE=48;
171 public static final NSLoader deflt = new NSLoader(KEYLIMIT);
173 public NSLoader(int keylimit) {
178 public Data load(Data data, Row row) {
179 // Int more efficient
180 data.name = row.getString(0);
181 data.type = row.getInt(1);
182 data.description = row.getString(2);
183 data.parent = row.getString(3);
188 protected void key(Data data, int idx, Object[] obj) {
193 protected void body(Data data, int _idx, Object[] obj) {
197 obj[++idx]=data.description;
198 obj[++idx]=data.parent;
202 public void marshal(Data data, DataOutputStream os) throws IOException {
203 writeHeader(os,MAGIC,VERSION);
204 writeString(os, data.name);
205 os.writeInt(data.type);
206 writeString(os,data.description);
207 writeString(os,data.parent);
208 if(data.attrib==null) {
211 os.writeInt(data.attrib.size());
212 for(Entry<String, String> es : data.attrib(false).entrySet()) {
213 writeString(os,es.getKey());
214 writeString(os,es.getValue());
220 public void unmarshal(Data data, DataInputStream is) throws IOException {
221 /*int version = */readHeader(is,MAGIC,VERSION);
222 // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
224 byte[] buff = new byte[BUFF_SIZE];
225 data.name = readString(is, buff);
226 data.type = is.readInt();
227 data.description = readString(is,buff);
228 data.parent = readString(is,buff);
229 int count = is.readInt();
231 Map<String, String> da = data.attrib(true);
232 for(int i=0;i<count;++i) {
233 da.put(readString(is,buff), readString(is,buff));
241 public Result<Data> create(AuthzTrans trans, Data data) {
242 String ns = data.name;
243 // Ensure Parent is set
244 if(data.parent==null) {
245 return Result.err(Result.ERR_BadData, "Need parent for %s", ns);
249 StringBuilder stmt = new StringBuilder();
250 stmt.append(BEGIN_BATCH);
251 attribInsertStmts(stmt, data);
252 stmt.append(APPLY_BATCH);
254 getSession(trans).execute(stmt.toString());
255 //// TEST CODE for Exception
256 // boolean force = true;
258 // throw new com.datastax.driver.core.exceptions.NoHostAvailableException(new HashMap<InetSocketAddress,Throwable>());
259 //// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"Sample Message");
263 } catch (DriverException | APIException | IOException e) {
264 reportPerhapsReset(trans,e);
265 trans.info().log(stmt);
266 return Result.err(Result.ERR_Backend, "Backend Access");
268 return super.create(trans, data);
272 public Result<Void> update(AuthzTrans trans, Data data) {
273 String ns = data.name;
274 // Ensure Parent is set
275 if(data.parent==null) {
276 return Result.err(Result.ERR_BadData, "Need parent for %s", ns);
279 StringBuilder stmt = new StringBuilder();
280 stmt.append(BEGIN_BATCH);
282 Map<String, String> localAttr = data.attrib;
283 Result<Map<String, String>> rremoteAttr = readAttribByNS(trans,ns);
284 if(rremoteAttr.notOK()) {
285 return Result.err(rremoteAttr);
289 for(Entry<String, String> es : localAttr.entrySet()) {
290 str = rremoteAttr.value.get(es.getKey());
291 if(str==null || !str.equals(es.getValue())) {
292 attribUpdateStmt(stmt, ns, es.getKey(),es.getValue());
296 // No point in deleting... insert overwrites...
297 // for(Entry<String, String> es : remoteAttr.entrySet()) {
298 // str = localAttr.get(es.getKey());
299 // if(str==null || !str.equals(es.getValue())) {
300 // attribDeleteStmt(stmt, ns, es.getKey());
303 if(stmt.length()>BEGIN_BATCH.length()) {
304 stmt.append(APPLY_BATCH);
305 getSession(trans).execute(stmt.toString());
307 } catch (DriverException | APIException | IOException e) {
308 reportPerhapsReset(trans,e);
309 trans.info().log(stmt);
310 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
313 return super.update(trans,data);
317 * @see org.onap.aaf.auth.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object)
320 public Result<List<Data>> read(AuthzTrans trans, Data data) {
321 Result<List<Data>> rld = super.read(trans, data);
323 if(rld.isOKhasData()) {
324 for(Data d : rld.value) {
325 // Note: Map is null at this point, save time/mem by assignment
326 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
328 d.attrib = rabn.value;
330 return Result.err(rabn);
338 * @see org.onap.aaf.auth.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object[])
341 public Result<List<Data>> read(AuthzTrans trans, Object... key) {
342 Result<List<Data>> rld = super.read(trans, key);
344 if(rld.isOKhasData()) {
345 for(Data d : rld.value) {
346 // Note: Map is null at this point, save time/mem by assignment
347 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
349 d.attrib = rabn.value;
351 return Result.err(rabn);
359 public Result<Void> delete(AuthzTrans trans, Data data, boolean reread) {
360 TimeTaken tt = trans.start("Delete NS Attributes " + data.name, Env.REMOTE);
362 StringBuilder stmt = new StringBuilder();
363 attribDeleteAllStmt(stmt, data);
365 getSession(trans).execute(stmt.toString());
366 } catch (DriverException | APIException | IOException e) {
367 reportPerhapsReset(trans,e);
368 trans.info().log(stmt);
369 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
374 return super.delete(trans, data, reread);
378 public Result<Map<String,String>> readAttribByNS(AuthzTrans trans, String ns) {
379 Map<String,String> map = new HashMap<String,String>();
380 TimeTaken tt = trans.start("readAttribByNS " + ns, Env.REMOTE);
382 ResultSet rs = getSession(trans).execute("SELECT key,value FROM "
388 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
390 map.put(r.getString(0), r.getString(1));
392 } catch (DriverException | APIException | IOException e) {
393 reportPerhapsReset(trans,e);
394 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
398 return Result.ok(map);
401 public Result<Set<String>> readNsByAttrib(AuthzTrans trans, String key) {
402 Set<String> set = new HashSet<String>();
403 TimeTaken tt = trans.start("readNsBykey " + key, Env.REMOTE);
405 ResultSet rs = getSession(trans).execute("SELECT ns FROM "
411 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
413 set.add(r.getString(0));
415 } catch (DriverException | APIException | IOException e) {
416 reportPerhapsReset(trans,e);
417 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
421 return Result.ok(set);
424 public Result<Void> attribAdd(AuthzTrans trans, String ns, String key, String value) {
426 getSession(trans).execute(attribInsertStmt(new StringBuilder(),ns,key,value).toString());
428 } catch (DriverException | APIException | IOException e) {
429 reportPerhapsReset(trans,e);
430 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
434 private StringBuilder attribInsertStmt(StringBuilder sb, String ns, String key, String value) {
435 sb.append("INSERT INTO ");
436 sb.append(TABLE_ATTRIB);
437 sb.append(" (ns,key,value) VALUES ('");
447 private StringBuilder attribUpdateStmt(StringBuilder sb, String ns, String key, String value) {
448 sb.append("UPDATE ");
449 sb.append(TABLE_ATTRIB);
450 sb.append(" set value='");
452 sb.append("' where ns='");
454 sb.append("' AND key='");
461 public Result<Void> attribRemove(AuthzTrans trans, String ns, String key) {
463 getSession(trans).execute(attribDeleteStmt(new StringBuilder(),ns,key).toString());
465 } catch (DriverException | APIException | IOException e) {
466 reportPerhapsReset(trans,e);
467 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
471 private StringBuilder attribDeleteStmt(StringBuilder stmt, String ns, String key) {
472 stmt.append("DELETE FROM ");
473 stmt.append(TABLE_ATTRIB);
474 stmt.append(" WHERE ns='");
476 stmt.append("' AND key='");
482 private void attribDeleteAllStmt(StringBuilder stmt, Data data) {
483 stmt.append(" DELETE FROM ");
484 stmt.append(TABLE_ATTRIB);
485 stmt.append(" WHERE ns='");
486 stmt.append(data.name);
490 private void attribInsertStmts(StringBuilder stmt, Data data) {
492 for(Entry<String,String> es : data.attrib(false).entrySet() ) {
494 attribInsertStmt(stmt,data.name,es.getKey(),es.getValue());
499 * Add description to Namespace
505 public Result<Void> addDescription(AuthzTrans trans, String ns, String description) {
507 getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '"
508 + description.replace("'", "''") + "' WHERE name = '" + ns + "';");
509 } catch (DriverException | APIException | IOException e) {
510 reportPerhapsReset(trans,e);
511 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
514 Data data = new Data();
516 wasModified(trans, CRUD.update, data, "Added description " + description + " to namespace " + ns, null );
520 public Result<List<Data>> getChildren(AuthzTrans trans, String parent) {
521 return psNS.read(trans, R_TEXT, new Object[]{parent});
526 * Log Modification statements to History
528 * @param modified which CRUD action was done
529 * @param data entity data that needs a log entry
530 * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
533 protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
534 boolean memo = override.length>0 && override[0]!=null;
535 boolean subject = override.length>1 && override[1]!=null;
537 //TODO Must log history
538 HistoryDAO.Data hd = HistoryDAO.newInitedData();
539 hd.user = trans.user();
540 hd.action = modified.name();
542 hd.subject = subject ? override[1] : data.name;
543 hd.memo = memo ? override[0] : (data.name + " was " + modified.name() + 'd' );
544 if(modified==CRUD.delete) {
546 hd.reconstruct = data.bytify();
547 } catch (IOException e) {
548 trans.error().log(e,"Could not serialize NsDAO.Data");
552 if(historyDAO.create(trans, hd).status!=Status.OK) {
553 trans.error().log("Cannot log to History");
555 if(infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {
556 trans.error().log("Cannot touch CacheInfo");