1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.dao.aaf.cass;
\r
26 import java.io.ByteArrayOutputStream;
\r
27 import java.io.DataInputStream;
\r
28 import java.io.DataOutputStream;
\r
29 import java.io.IOException;
\r
30 import java.nio.ByteBuffer;
\r
31 import java.util.HashMap;
\r
32 import java.util.HashSet;
\r
33 import java.util.Iterator;
\r
34 import java.util.List;
\r
35 import java.util.Map;
\r
36 import java.util.Map.Entry;
\r
37 import java.util.Set;
\r
39 import com.att.authz.env.AuthzTrans;
\r
40 import com.att.authz.layer.Result;
\r
41 import com.att.dao.Bytification;
\r
42 import com.att.dao.Cached;
\r
43 import com.att.dao.CassAccess;
\r
44 import com.att.dao.CassDAOImpl;
\r
45 import com.att.dao.Loader;
\r
46 import com.att.dao.Streamer;
\r
47 import com.att.inno.env.APIException;
\r
48 import com.att.inno.env.Env;
\r
49 import com.att.inno.env.TimeTaken;
\r
50 import com.datastax.driver.core.Cluster;
\r
51 import com.datastax.driver.core.ResultSet;
\r
52 import com.datastax.driver.core.Row;
\r
53 import com.datastax.driver.core.exceptions.DriverException;
\r
58 * Data Access Object for Namespace Data
\r
61 public class NsDAO extends CassDAOImpl<AuthzTrans,NsDAO.Data> {
\r
62 public static final String TABLE = "ns";
\r
63 public static final String TABLE_ATTRIB = "ns_attrib";
\r
64 public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F
\r
65 public static final int ROOT = 1;
\r
66 public static final int COMPANY=2;
\r
67 public static final int APP = 3;
\r
69 private static final String BEGIN_BATCH = "BEGIN BATCH\n";
\r
70 private static final String APPLY_BATCH = "APPLY BATCH;\n";
\r
71 private static final String SQSCCR = "';\n";
\r
72 private static final String SQCSQ = "','";
\r
74 private HistoryDAO historyDAO;
\r
75 private CacheInfoDAO infoDAO;
\r
76 private PSInfo psNS;
\r
78 public NsDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
\r
79 super(trans, NsDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
\r
83 public NsDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO iDAO) throws APIException, IOException {
\r
84 super(trans, NsDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
\r
91 //////////////////////////////////////////
\r
92 // Data Definition, matches Cassandra DM
\r
93 //////////////////////////////////////////
\r
94 private static final int KEYLIMIT = 1;
\r
96 * Data class that matches the Cassandra Table "role"
\r
99 public static class Data extends CacheableData implements Bytification {
\r
100 public String name;
\r
102 public String description;
\r
103 public String parent;
\r
104 public Map<String,String> attrib;
\r
106 // ////////////////////////////////////////
\r
108 public Map<String,String> attrib(boolean mutable) {
\r
109 if (attrib == null) {
\r
110 attrib = new HashMap<String,String>();
\r
111 } else if (mutable && !(attrib instanceof HashMap)) {
\r
112 attrib = new HashMap<String,String>(attrib);
\r
118 public int[] invalidate(Cached<?,?> cache) {
\r
124 public NsSplit split(String name) {
\r
125 return new NsSplit(this,name);
\r
129 public ByteBuffer bytify() throws IOException {
\r
130 ByteArrayOutputStream baos = new ByteArrayOutputStream();
\r
131 NSLoader.deflt.marshal(this,new DataOutputStream(baos));
\r
132 return ByteBuffer.wrap(baos.toByteArray());
\r
136 public void reconstitute(ByteBuffer bb) throws IOException {
\r
137 NSLoader.deflt.unmarshal(this,toDIS(bb));
\r
141 public String toString() {
\r
147 private void init(AuthzTrans trans) throws APIException, IOException {
\r
149 if(historyDAO==null) {
\r
150 historyDAO = new HistoryDAO(trans, this);
\r
152 if(infoDAO==null) {
\r
153 infoDAO = new CacheInfoDAO(trans,this);
\r
156 String[] helpers = setCRUD(trans, TABLE, Data.class, NSLoader.deflt,4/*need to skip attrib */);
\r
158 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
\r
159 " WHERE parent = ?", new NSLoader(1),readConsistency);
\r
163 private static final class NSLoader extends Loader<Data> implements Streamer<Data> {
\r
164 public static final int MAGIC=250935515;
\r
165 public static final int VERSION=1;
\r
166 public static final int BUFF_SIZE=48;
\r
168 public static final NSLoader deflt = new NSLoader(KEYLIMIT);
\r
170 public NSLoader(int keylimit) {
\r
175 public Data load(Data data, Row row) {
\r
176 // Int more efficient
\r
177 data.name = row.getString(0);
\r
178 data.type = row.getInt(1);
\r
179 data.description = row.getString(2);
\r
180 data.parent = row.getString(3);
\r
185 protected void key(Data data, int idx, Object[] obj) {
\r
186 obj[idx]=data.name;
\r
190 protected void body(Data data, int _idx, Object[] obj) {
\r
193 obj[idx]=data.type;
\r
194 obj[++idx]=data.description;
\r
195 obj[++idx]=data.parent;
\r
199 public void marshal(Data data, DataOutputStream os) throws IOException {
\r
200 writeHeader(os,MAGIC,VERSION);
\r
201 writeString(os, data.name);
\r
202 os.writeInt(data.type);
\r
203 writeString(os,data.description);
\r
204 writeString(os,data.parent);
\r
205 if(data.attrib==null) {
\r
208 os.writeInt(data.attrib.size());
\r
209 for(Entry<String, String> es : data.attrib(false).entrySet()) {
\r
210 writeString(os,es.getKey());
\r
211 writeString(os,es.getValue());
\r
217 public void unmarshal(Data data, DataInputStream is) throws IOException {
\r
218 /*int version = */readHeader(is,MAGIC,VERSION);
\r
219 // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
\r
221 byte[] buff = new byte[BUFF_SIZE];
\r
222 data.name = readString(is, buff);
\r
223 data.type = is.readInt();
\r
224 data.description = readString(is,buff);
\r
225 data.parent = readString(is,buff);
\r
226 int count = is.readInt();
\r
228 Map<String, String> da = data.attrib(true);
\r
229 for(int i=0;i<count;++i) {
\r
230 da.put(readString(is,buff), readString(is,buff));
\r
238 public Result<Data> create(AuthzTrans trans, Data data) {
\r
239 String ns = data.name;
\r
240 // Ensure Parent is set
\r
241 int ldot = ns.lastIndexOf('.');
\r
242 data.parent=ldot<0?".":ns.substring(0,ldot);
\r
244 // insert Attributes
\r
245 StringBuilder stmt = new StringBuilder();
\r
246 stmt.append(BEGIN_BATCH);
\r
247 attribInsertStmts(stmt, data);
\r
248 stmt.append(APPLY_BATCH);
\r
250 getSession(trans).execute(stmt.toString());
\r
251 //// TEST CODE for Exception
\r
252 // boolean force = true;
\r
254 // throw new com.datastax.driver.core.exceptions.NoHostAvailableException(new HashMap<InetSocketAddress,Throwable>());
\r
255 //// throw new com.datastax.driver.core.exceptions.AuthenticationException(new InetSocketAddress(9999),"Sample Message");
\r
259 } catch (DriverException | APIException | IOException e) {
\r
260 reportPerhapsReset(trans,e);
\r
261 trans.info().log(stmt);
\r
262 return Result.err(Result.ERR_Backend, "Backend Access");
\r
264 return super.create(trans, data);
\r
268 public Result<Void> update(AuthzTrans trans, Data data) {
\r
269 String ns = data.name;
\r
270 // Ensure Parent is set
\r
271 int ldot = ns.lastIndexOf('.');
\r
272 data.parent=ldot<0?".":ns.substring(0,ldot);
\r
274 StringBuilder stmt = new StringBuilder();
\r
275 stmt.append(BEGIN_BATCH);
\r
277 Map<String, String> localAttr = data.attrib;
\r
278 Result<Map<String, String>> rremoteAttr = readAttribByNS(trans,ns);
\r
279 if(rremoteAttr.notOK()) {
\r
280 return Result.err(rremoteAttr);
\r
282 // update Attributes
\r
284 for(Entry<String, String> es : localAttr.entrySet()) {
\r
285 str = rremoteAttr.value.get(es.getKey());
\r
286 if(str==null || !str.equals(es.getValue())) {
\r
287 attribInsertStmt(stmt, ns, es.getKey(),es.getValue());
\r
291 // No point in deleting... insert overwrites...
\r
292 // for(Entry<String, String> es : remoteAttr.entrySet()) {
\r
293 // str = localAttr.get(es.getKey());
\r
294 // if(str==null || !str.equals(es.getValue())) {
\r
295 // attribDeleteStmt(stmt, ns, es.getKey());
\r
298 if(stmt.length()>BEGIN_BATCH.length()) {
\r
299 stmt.append(APPLY_BATCH);
\r
300 getSession(trans).execute(stmt.toString());
\r
302 } catch (DriverException | APIException | IOException e) {
\r
303 reportPerhapsReset(trans,e);
\r
304 trans.info().log(stmt);
\r
305 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
308 return super.update(trans,data);
\r
312 * @see com.att.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object)
\r
315 public Result<List<Data>> read(AuthzTrans trans, Data data) {
\r
316 Result<List<Data>> rld = super.read(trans, data);
\r
318 if(rld.isOKhasData()) {
\r
319 for(Data d : rld.value) {
\r
320 // Note: Map is null at this point, save time/mem by assignment
\r
321 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
\r
323 d.attrib = rabn.value;
\r
325 return Result.err(rabn);
\r
333 * @see com.att.dao.CassDAOImpl#read(com.att.inno.env.TransStore, java.lang.Object[])
\r
336 public Result<List<Data>> read(AuthzTrans trans, Object... key) {
\r
337 Result<List<Data>> rld = super.read(trans, key);
\r
339 if(rld.isOKhasData()) {
\r
340 for(Data d : rld.value) {
\r
341 // Note: Map is null at this point, save time/mem by assignment
\r
342 Result<Map<String, String>> rabn = readAttribByNS(trans,d.name);
\r
344 d.attrib = rabn.value;
\r
346 return Result.err(rabn);
\r
354 public Result<Void> delete(AuthzTrans trans, Data data, boolean reread) {
\r
355 TimeTaken tt = trans.start("Delete NS Attributes " + data.name, Env.REMOTE);
\r
357 StringBuilder stmt = new StringBuilder();
\r
358 attribDeleteAllStmt(stmt, data);
\r
360 getSession(trans).execute(stmt.toString());
\r
361 } catch (DriverException | APIException | IOException e) {
\r
362 reportPerhapsReset(trans,e);
\r
363 trans.info().log(stmt);
\r
364 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
369 return super.delete(trans, data, reread);
\r
373 public Result<Map<String,String>> readAttribByNS(AuthzTrans trans, String ns) {
\r
374 Map<String,String> map = new HashMap<String,String>();
\r
375 TimeTaken tt = trans.start("readAttribByNS " + ns, Env.REMOTE);
\r
377 ResultSet rs = getSession(trans).execute("SELECT key,value FROM "
\r
383 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
\r
384 Row r = iter.next();
\r
385 map.put(r.getString(0), r.getString(1));
\r
387 } catch (DriverException | APIException | IOException e) {
\r
388 reportPerhapsReset(trans,e);
\r
389 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
393 return Result.ok(map);
\r
396 public Result<Set<String>> readNsByAttrib(AuthzTrans trans, String key) {
\r
397 Set<String> set = new HashSet<String>();
\r
398 TimeTaken tt = trans.start("readNsBykey " + key, Env.REMOTE);
\r
400 ResultSet rs = getSession(trans).execute("SELECT ns FROM "
\r
406 for(Iterator<Row> iter = rs.iterator();iter.hasNext(); ) {
\r
407 Row r = iter.next();
\r
408 set.add(r.getString(0));
\r
410 } catch (DriverException | APIException | IOException e) {
\r
411 reportPerhapsReset(trans,e);
\r
412 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
416 return Result.ok(set);
\r
419 public Result<Void> attribAdd(AuthzTrans trans, String ns, String key, String value) {
\r
421 getSession(trans).execute(attribInsertStmt(new StringBuilder(),ns,key,value).toString());
\r
422 return Result.ok();
\r
423 } catch (DriverException | APIException | IOException e) {
\r
424 reportPerhapsReset(trans,e);
\r
425 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
429 private StringBuilder attribInsertStmt(StringBuilder sb, String ns, String key, String value) {
\r
430 sb.append("INSERT INTO ");
\r
431 sb.append(TABLE_ATTRIB);
\r
432 sb.append(" (ns,key,value) VALUES ('");
\r
442 public Result<Void> attribRemove(AuthzTrans trans, String ns, String key) {
\r
444 getSession(trans).execute(attribDeleteStmt(new StringBuilder(),ns,key).toString());
\r
445 return Result.ok();
\r
446 } catch (DriverException | APIException | IOException e) {
\r
447 reportPerhapsReset(trans,e);
\r
448 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
452 private StringBuilder attribDeleteStmt(StringBuilder stmt, String ns, String key) {
\r
453 stmt.append("DELETE FROM ");
\r
454 stmt.append(TABLE_ATTRIB);
\r
455 stmt.append(" WHERE ns='");
\r
457 stmt.append("' AND key='");
\r
463 private void attribDeleteAllStmt(StringBuilder stmt, Data data) {
\r
464 stmt.append(" DELETE FROM ");
\r
465 stmt.append(TABLE_ATTRIB);
\r
466 stmt.append(" WHERE ns='");
\r
467 stmt.append(data.name);
\r
468 stmt.append(SQSCCR);
\r
471 private void attribInsertStmts(StringBuilder stmt, Data data) {
\r
472 // INSERT new Attrib
\r
473 for(Entry<String,String> es : data.attrib(false).entrySet() ) {
\r
475 attribInsertStmt(stmt,data.name,es.getKey(),es.getValue());
\r
480 * Add description to Namespace
\r
483 * @param description
\r
486 public Result<Void> addDescription(AuthzTrans trans, String ns, String description) {
\r
488 getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '"
\r
489 + description + "' WHERE name = '" + ns + "';");
\r
490 } catch (DriverException | APIException | IOException e) {
\r
491 reportPerhapsReset(trans,e);
\r
492 return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
\r
495 Data data = new Data();
\r
497 wasModified(trans, CRUD.update, data, "Added description " + description + " to namespace " + ns, null );
\r
498 return Result.ok();
\r
501 public Result<List<Data>> getChildren(AuthzTrans trans, String parent) {
\r
502 return psNS.read(trans, R_TEXT, new Object[]{parent});
\r
507 * Log Modification statements to History
\r
509 * @param modified which CRUD action was done
\r
510 * @param data entity data that needs a log entry
\r
511 * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
\r
514 protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
\r
515 boolean memo = override.length>0 && override[0]!=null;
\r
516 boolean subject = override.length>1 && override[1]!=null;
\r
518 //TODO Must log history
\r
519 HistoryDAO.Data hd = HistoryDAO.newInitedData();
\r
520 hd.user = trans.user();
\r
521 hd.action = modified.name();
\r
523 hd.subject = subject ? override[1] : data.name;
\r
524 hd.memo = memo ? override[0] : (data.name + " was " + modified.name() + 'd' );
\r
525 if(modified==CRUD.delete) {
\r
527 hd.reconstruct = data.bytify();
\r
528 } catch (IOException e) {
\r
529 trans.error().log(e,"Could not serialize NsDAO.Data");
\r
533 if(historyDAO.create(trans, hd).status!=Status.OK) {
\r
534 trans.error().log("Cannot log to History");
\r
536 if(infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {
\r
537 trans.error().log("Cannot touch CacheInfo");
\r